package.xml0000644000175000000120000020345014515245367013772 0ustar pyatsukhnenkowheel redis pecl.php.net PHP extension for interfacing with Redis This extension provides an API for communicating with Redis servers. Michael Grunder mgrunder michael.grunder@gmail.com yes Pavlo Yatsukhnenko yatsukhnenko p.yatsukhnenko@gmail.com yes Nicolas Favre-Felix nff n.favrefelix@gmail.com no 2023-10-22 6.0.2 6.0.0 stable stable PHP --- Sponsors --- Audiomack - https://audiomack.com Open LMS - https://openlms.net BlueHost - https://bluehost.com Object Cache Pro for WordPress - https://objectcache.pro Avtandil Kikabidze - https://github.com/akalongman Zaher Ghaibeh - https://github.com/zaherg BatchLabs - https://batch.com Stackhero - https://github.com/stackhero-io Florian Levis - https://github.com/Gounlaf Luis Zarate - https://github.com/jlzaratec --- phpredis 6.0.2 This release contains fixes for OBJECT, PSUBSCRIBE and SCAN commands. You can find a detailed list of changes in CHANGELOG.md and package.xml or by inspecting the git commit logs. * Fix deprecation error when passing null to match_type parameter.[b835aaa3] (Pavlo Yatsukhnenko) * Fix flaky test and OBJECT in a pipeline. [a7f51f70] (Michael Grunder) * Find our callback by pattern with PSUBSCRIBE [2f276dcd] (Michael Grunder) 7.2.0 1.4.0b1 redis stable stable 6.0.2 6.0.0 2023-10-22 --- Sponsors --- Audiomack - https://audiomack.com Open LMS - https://openlms.net BlueHost - https://bluehost.com Object Cache Pro for WordPress - https://objectcache.pro Avtandil Kikabidze - https://github.com/akalongman Zaher Ghaibeh - https://github.com/zaherg BatchLabs - https://batch.com Stackhero - https://github.com/stackhero-io Florian Levis - https://github.com/Gounlaf Luis Zarate - https://github.com/jlzaratec --- phpredis 6.0.2 This release contains fixes for OBJECT, PSUBSCRIBE and SCAN commands. You can find a detailed list of changes in CHANGELOG.md and package.xml or by inspecting the git commit logs. * Fix deprecation error when passing null to match_type parameter.[b835aaa3] (Pavlo Yatsukhnenko) * Fix flaky test and OBJECT in a pipeline. [a7f51f70] (Michael Grunder) * Find our callback by pattern with PSUBSCRIBE [2f276dcd] (Michael Grunder) stable stable 6.0.1 6.0.0 2023-09-23 --- Sponsors --- Audiomack - https://audiomack.com Open LMS - https://openlms.net/ BlueHost - https://bluehost.com Object Cache Pro for WordPress - https://objectcache.pro/ Avtandil Kikabidze - https://github.com/akalongman Zaher Ghaibeh - https://github.com/zaherg BatchLabs - https://batch.com Stackhero - https://github.com/stackhero-io Florian Levis - https://github.com/Gounlaf Luis Zarate - https://github.com/jlzaratec --- phpredis 6.0.1 This release contains fix for unknown expiration modifier issue as well as memory leak and segfault in exec function and small documentation improvements. You can find a detailed list of changes in CHANGELOG.md and package.xml or by inspecting the git commit logs. * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko), (Markus Podar) * Fix unknown expiration modifier [264c0c7e, 95bd184b] (Pavlo Yatsukhnenko) * Update documentation [3674d663, 849bedb6, 1ad95b63] (Till Kruss), (Joost OrangeJuiced) stable stable 6.0.0 6.0.0 2023-09-09 --- Sponsors --- Audiomack - https://audiomack.com Open LMS - https://openlms.net BlueHost - https://bluehost.com Object Cache Pro for WordPress - https://objectcache.pro Avtandil Kikabidze - https://github.com/akalongman Zaher Ghaibeh - https://github.com/zaherg BatchLabs - https://batch.com Luis Zarate - https://github.com/jlzaratec phpredis 6.0.0 - There were no changes between 6.0.0 and 6.0.0RC2. --- phpredis 6.0.0RC2 * Fix arginfo for arguments that default to null [8d99b7d1] (Nicolas Grekas) * Fix C99 usages [54d9ca45] (Remi Collet) * Raise minimal supported version to 7.2 [e10b9a85] (Remi Collet) --- phpredis 6.0.0RC1 * Fix restoring keys when using compression [82e08723] (Till Kruss) * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) * Fix NULL-pointer dereferences and handle possible UB [36457555] (Pavlo Yatsukhnenko) * Fix security alerts [ee210f86, fb6a297c] (Pavlo Yatsukhnenko), (Michael Grunder) * Fix segfault [55bf0202] (Pavlo Yatsukhnenko) * Fix default host length [c40f9d6c] (Pavlo Yatsukhnenko) * Fix redis session standalone stream ssl context [ed10f365, d1bc6727, 2ff11df5] (patricio.dorantes) * Fix segfault with session+tls [a471c87a] (Pavlo Yatsukhnenko) * Fix non standards conforming prototypes. [b3ce0486] (Michael Grunder) * Avoid registering the same replicas multiple times [f2bfd723] (Marius Adam) * Better unix:// or file:// detection. [d05d301b] (Michael Grunder) * Future proof our igbinary header check [69355faa] (Michael Grunder) * Fix BITOP cross-slot bug [af13f951] (Michael Grunder) * SENTINEL RESET returns a long. [0243dd9d] (Michael Grunder) * Fix redis_sock_read_multibulk_multi_reply_loop logic [d9cb5946, 5a643b62] (Pavlo Yatsukhnenko) * Fix RPOP to unserialize/decompress data. [02c91d59] (Michael Grunder) * Fix testObject for redis 7.2 [fea19b52, dcb95a3f] (Remi Collet) * Fix bug: the pipeline mode socket return an unexpected result after reconnecting [a3327d9d] (thomaston) * Fix stub files [9aa5f387, 74cf49f5, 8b1eafe8, e392dd88, b5ea5fd7, 71758b09, 2a6dee5d] (Nicolas Grekas), (Michael Grunder) * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Kruss) * Allow to pass null as iterator [14d121bb] (Pavlo Yatsukhnenko) * Add NOMKSTREAM option to XADD command. [f9436e25] (Pavlo Yatsukhnenko) * Don't allow reconnect on read response [5a269ab6] (Pavlo Yatsukhnenko) * Reset multi/pipline transaction on pconnect close [0879770a] (Pavlo Yatsukhnenko) * Use read_mbulk_header helper where possible [ca8b4c93] (Pavlo Yatsukhnenko) * Allow to pass null as auth argument [41517753] (Pavlo Yatsukhnenko) * Refactor redis_parse_client_list_response [68136a29, aaa4c91a, 1fb2935b, cf2c052c] (Pavlo Yatsukhnenko) * Refactor subscribe/unsubscribe [3c9e159c] (Pavlo Yatsukhnenko) * Change PHPREDIS_CTX_PTR type [de3635da] (Pavlo Yatsukhnenko) * Refactor redis_parse_info_response [982bd13b] (Pavlo Yatsukhnenko) * Allow IPv6 address within square brackets [c28ad7bb] (Pavlo Yatsukhnenko) * Allow multiple field-value pairs for hmset command. [e858e8e3] (Pavlo Yatsukhnenko) * Refactor MINIT and use @generate-class-entries in stub files [3675f442] (Remi Collet) * Use spl_ce_RuntimeException [3cd5ac1e, a7e5ea64] (Remi Collet) * Regenerate arginfo using 8.2.0 [a38e08da] (Remi Collet) * Refactor client command [a8d10291] (Pavlo Yatsukhnenko) * Pull COUNT/ANY parsing into a helper function [d67b2020] (Michael Grunder) * Return false or NULL on empty lpos response [39a01ac7] (Michael Grunder) * BLPOP with a float timeout [a98605f2, dc9af529] (Michael Grunder) * Make sure we set an error for key based scans [98fda1b8] (Michael Grunder) * Add back a default switch case for setoption handler [87464932] (Michael Grunder) * Update stubs so the tests pass in strict mode [bebd398c] (Michael Grunder) * Move where we generate our salt [d2044c9f] (Michael Grunder) * Refactor XINFO handler [3b0d8b77] (Michael Grunder) * Refactor and fix XPENDING handler [457953f4] (Michael Grunder) * Refactor FLUSHDB and update docs. [54a084e5] (Michael Grunder) * Add missing directed node command to docs and refactor stubs. [5ac92d25] (Michael Grunder) * Refactor BITPOS and implement BIT/BYTE option. [4d8afd38] (Michael Grunder) * INFO with multiple sections [44d03ca0] (Michael Grunder) * Refactor SLOWLOG command [d87f1428] (Michael Grunder) * Refactor SORT and add SORT_RO command [8c7c5a3a] (Michael Grunder) * Use ZEND_STRL in redis_commands.c [78de25a3] (Pavlo Yatsukhnenko) * Refactor PubSub command [2a0d1c1e] (Pavlo Yatsukhnenko) * Refactor SLAVEOF handler [f2cef8be] (Michael Grunder) * Refactor ACL command [504810a5] (Pavlo Yatsukhnenko) * Use fast_zpp API [376d4d27] (Pavlo Yatsukhnenko) * Fix XAUTOCLAIM response handler [0b7bd83f] (Michael Grunder) * Refactor command command [ff863f3f] (Pavlo Yatsukhnenko) * Refactor rawCommand and WAIT [79c9d224] (Michael Grunder) * Refactor SELECT command [86f15cca] (Michael Grunder) * Refactor SRANDMEMBER command. [f62363c2] (Michael Grunder) * Refactor OBJECT command. [acb5db76] (Michael Grunder) * Refactor gen_varkey_cmd [3efa59cb] (Michael Grunder) * Refactor MGET command. [8cb6dd17] (Michael Grunder) * Refactor INFO and SCRIPT commands. [3574ef08] (Michael Grunder) * Refactor MSET and MSETNX commands. [6d104481] (Michael Grunder) * Refactor HMSET command. [90eb0470] (Michael Grunder) * Refactor PFCOUNT command. [19fd7e0c] (Michael Grunder) * Refactor SMOVE command. [204a02c5] (Michael Grunder) * Rework ZRANGE argument handling. [aa0938a4] (Michael Grunder) * Refactor a couple more command methods. [5b560ccf, c8224b93, 40e1b1bf, ccd419a4] (Michael Grunder) * Refactor HMGET command [bb66a547] (Michael Grunder) * Refactor CLIENT command [77c4f7a3] (Pavlo Yatsukhnenko) * Refactor redis_long_response [f14a80db] (Pavlo Yatsukhnenko) * Synchronize Redis and RedisSentinel constructors [ebb2386e] (Pavlo Yatsukhnenko) * Use redis_sock_connect on connect [f6c8b9c6] (Pavlo Yatsukhnenko) * Auto-select db in redis_sock_server_open [6930a81c] (Pavlo Yatsukhnenko) * Use on-stack allocated valiables [7a055cad] (Pavlo Yatsukhnenko) * Add XAUTOCLAIM command [01f3342c] (Pavlo Yatsukhnenko) * Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH [750b6cf3] (Pavlo Yatsukhnenko) * Add reset command [947a2d38] (Pavlo Yatsukhnenko) * Add hRandField command [fe397371] (Pavlo Yatsukhnenko) * Add PXAT/EXAT arguments to SET command. [0a160685] (Pavlo Yatsukhnenko) * Add GETEX, GETDEL commands. [11861d95] (Pavlo Yatsukhnenko) * Add FAILOVER command. [4b767be7] (Pavlo Yatsukhnenko) * Backoff settings in constructor [e6b3fe54] (Pavlo Yatsukhnenko) * Add the COUNT argument to LPOP and RPOP [df97cc35] (Pavlo Yatsukhnenko) * Unsubscribe from all channels [0f1ca0cc] (Pavlo Yatsukhnenko) * Add lPos command. [687a5c78] (Pavlo Yatsukhnenko) * Add the ANY argument to GEOSEARCH and GEORADIUS [bf6f31e3] (Pavlo Yatsukhnenko) * Add 'BIT'/'BYTE' modifier to BITCOUNT + tests [a3d2f131] (Michael Grunder) * Add missing configureoption entries in package.xml [59053f10] (Michele Locati) * Implement CONFIG RESETSTAT [239678a0] (Michael Grunder) * SINTERCARD and ZINTERCARD commands [64300508] (Michael Grunder) * LCS command [c0e839f6] (Michael Grunder) * EXPIRETIME and PEXPIRETIME [f5b2a09b] (Michael Grunder) * [B]LMPOP and [B]ZMPOP commands [6ea978eb] (Michael Grunder) * Implement new RESTORE options [9a3fe401] (Michael Grunder) * Add new Redis 6.2.0 XTRIM options [6b34d17f] (Michael Grunder) * Implement AUTH/AUTH2 arguments for MIGRATE [114d79d1] (Michael Grunder) * Implement CONFIG REWRITE [525958ea] (Michael Grunder) * Implement Redis 7.0.0 [P]EXPIRE[AT] [options 872ae107] (Michael Grunder) * Variadic CONFIG GET/SET [36ef4bd8, a176f586] (Michael Grunder) * EVAL_RO and EVALSHA_RO [f3a40830] (Michael Grunder) * Implement ZRANGESTORE and add ZRANGE options [71bcbcb9] (Michael Grunder) * XGROUP DELCONSUMER and ENTRIESREAD [1343f500] (Michael Grunder) * Expose the transferred number of bytes [e0a88b7b, 90828019, 7a4cee2d] (Pavlo Yatsukhnenko), (Michael Grunder) * TOUCH command [dc1f2398] (Michael Grunder) * Redis Sentinel TLS support [f2bb2cdb] (Pavlo Yatsukhnenko) * Add the CH, NX, XX arguments to GEOADD [2bb64038, e8f5b517] (Pavlo Yatsukhnenko) * Implement SMISMEMBER for RedisCluster [abfac47b] (Michael Grunder) * Implement ssubscribe/sunsubscribe [7644736e] (Pavlo Yatsukhnenko) * Implement BLMOVE and add LMOVE/BLMOVE to cluster. [121e9d9c] (Michael Grunder) * Implement LPOS for RedisCluster [7121aaae] (Michael Grunder) * Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. [fa5d1af9] (Michael Grunder) * Implement HRANDFIELD for RedisCluster [e222b85e] (Michael Grunder) * Implement COPY for RedisCluster [40a2c254] (Michael Grunder) * Implement new ZSET commands for cluster [27900f39] (Michael Grunder) * Add cluster support for strict sessions and lazy write [b6cf6361] (Michael Grunder) * Add function command [90a0e9cc] (Pavlo Yatsukhnenko) * Add FCALL/FCALL_RO commands [7c46ad2c] (Pavlo Yatsukhnenko) * Remove unused macroses [831d6118] (Pavlo Yatsukhnenko) stable stable 5.3.7 5.3.7 2022-02-15 --- Sponsors --- Audiomack - https://audiomack.com Open LMS - https://openlms.net BlueHost - https://bluehost.com Object Cache Pro for WordPress - https://objectcache.pro Avtandil Kikabidze - https://github.com/akalongman Zaher Ghaibeh - https://github.com/zaherg BatchLabs - https://batch.com Luis Zarate - https://github.com/jlzaratec phpredis 5.3.7 - There were no changes between 5.3.7 and 5.3.7RC2. --- phpredis 5.3.7RC2 - There were no changes between 5.3.7RC2 and 5.3.7RC1. --- phpredis 5.3.7RC1 - Fix RedisArray::[hsz]scan and tests [08a9d5db, 0264de18] (Pavlo Yatsukhnenko, Michael Grunder) - Fix RedisArray::scan [8689ab1c] (Pavlo Yatsukhnenko) - Fix LZF decompression logic [0719c1ec] (Michael Grunder) stable stable 5.3.6 5.3.6 2022-01-17 --- Sponsors --- Audiomack - https://audiomack.com Open LMS - https://openlms.net BlueHost - https://bluehost.com Object Cache Pro for WordPress - https://objectcache.pro Avtandil Kikabidze - https://github.com/akalongman Zaher Ghaibeh - https://github.com/zaherg BatchLabs - https://batch.com Luis Zarate - https://github.com/jlzaratec --- phpredis 5.3.6 - Fix a segfault in RedisArray::del [d2f2a7d9] (Pavlo Yatsukhnenko) stable stable 5.3.5 5.3.5 2021-12-18 phpredis 5.3.5 This release adds support for exponential backoff w/jitter, experimental support for detecting a dirty connection, as well as many other fixes and improvements. You can find a detailed list of changes in Changelog.md and package.xml or by inspecting the git commit logs. --- Sponsors --- Audiomack - https://audiomack.com Open LMS - https://openlms.net BlueHost - https://bluehost.com Object Cache Pro for WordPress - https://objectcache.pro Avtandil Kikabidze - https://github.com/akalongman Zaher Ghaibeh - https://github.com/zaherg BatchLabs - https://batch.com Luis Zarate - https://github.com/jlzaratec --- phpredis 5.3.5 * Fix typo in cluster_scan_resp [44affad2] (Michael Grunder) --- phpredis 5.3.5RC1 * Fixed segfault in redis_setoption_handler [692e4e84] (Pavlo Yatsukhnenko) * Fix masters array in the event of a cluster failover [bce692962] (Bar Shaul) * Fix 32 bit type error [672dec87f] (Remi Collet) * Fix radix character in certain locales [89a871e24] (Pavlo Yatsukhnenko) * ZSTD Validation fix [6a77ef5cd] (Michael Grunder) * Remove superfluous typecast [b2871471f] (Remi Collet) * Updated documentation [f84168657, d017788e7, 20ac84710, 0adf05260, aee29bf73, 09a095e72, 12ffbf33a, ff331af98, a6bdb8731, 305c15840, 1aa10e93a, d78b0c79d, c6d37c27c, a6303f5b9, d144bd2c7, a6fb815ef, 9ef862bc6] (neodisco, Clement Tessier, T. Todua, dengliming, Maxime Cornet, Emanuele Filannino Michael Grunder) * Travis CI Fixes [a43f4586e, 4fde8178f, 7bd5415ac, fdb8c4bb7, d4f407470] (Pavlo Yatsukhnenko) * Minor fixes/cleanup [2e190adc1, 99975b592, 9d0879fa5, 22b06457b] (Pavlo Yatsukhnenko) * Fix RedisArray constructor bug [85dc883ba](https://github.com/phpredis/phpredis/commit/85dc883ba) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) * Moved to GitHub Actions [4d2afa786, 502d09fd5] (Pavlo Yatsukhnenko) * Use more appropriate array iteration macro [6008900c2] (Pavlo Yatsukhnenko) * Clean up session tests [ab25ae7f3] (Michael Grunder) * RedisArray refactors [1250f0001, 017b2ea7f, 37ed3f079] (Pavlo Yatsukhnenko) * Use zend_parse_parameters_none helper [a26b14dbe] (Remi Collet) * Support for various exponential backoff strategies [#1986, #1993, 732eb8dcb, 05129c3a3, 5bba6a7fc], (Nathaniel Braun) * Added experimental support for detecting a dirty connection [d68579562] (Michael Grunder) * Created distinct compression utility methods (pack/unpack) [#1939, da2790aec] (Michael Grunder) * SMISMEMBER Command [#1894, ae2382472, ed283e1ab] (Pavlo Yatsukhnenko) stable stable 5.3.4 5.3.4 2021-03-24 phpredis 5.3.4 This release fixes a multi/pipeline segfault on apple silicon as well as two small compression related bugs. You can find a detailed list of changes in CHANGELOG.md and package.xml * Fix multi/pipeline segfault on Apple silicon [e0796d48] (Michael Grunder) * Pass compression flag on HMGET in RedisCluster [edc724e6] (Adam Olley) * Abide by ZSTD error return constants [8400ed1c] (Michael Grunder) * Fix timing related CI session tests [9b986bf8] (Michael Grunder) * Sponsors ~ Audiomack - https://audiomack.com ~ Open LMS - https://openlms.net ~ BlueHost - https://bluehost.com ~ Object Cache Pro for WordPress - https://objectcache.pro ~ Avtandil Kikabidze - https://github.com/akalongman ~ Zaher Ghaibeh - https://github.com/zaherg ~ BatchLabs - https://batch.com stable stable 5.3.3 5.3.3 2021-02-01 phpredis 5.3.3 This release mostly includes just small PHP 8 Windows compatibility fixes such that pecl.php.net can automatically build Windows DLLs. You can find a detailed list of changes in CHANGELOG.md and package.xml * Fix PHP8 Windows includes [270b4db8] (Jan-E) * Fix hash ops for php 8.0.1 [87297cbb] (defender-11) * Disable cloning Redis and RedisCluster objects [cd05a344] (Michael Grunder) * Sponsors ~ Audiomack - https://audiomack.com ~ BlueHost - https://bluehost.com ~ Redis Cache Pro for WordPress - https://wprediscache.com ~ Avtandil Kikabidze - https://github.com/akalongman ~ Zaher Ghaibeh - https://github.com/zaherg ~ BatchLabs - https://batch.com stable stable 5.3.2 5.3.2 2020-10-22 This release containse some bugfixes and small improvements. You can find a detailed list of changes in CHANGELOG.md and package.xml * Sponsors ~ Audiomack - https://audiomack.com ~ BlueHost - https://bluehost.com ~ Redis Cache Pro for WordPress - https://wprediscache.com ~ Avtandil Kikabidze - https://github.com/akalongman ~ Oleg Babushkin - https://github.com/olbabushkin phpredis 5.3.2 * Use "%.17g" sprintf format for doubles as done in Redis server. [32be3006] (Pavlo Yatsukhnenko) * Allow to pass NULL as RedisCluster stream context options. [72024afe] (Pavlo Yatsukhnenko) --- phpredis 5.3.2RC2 --- * Verify SET options are strings before testing them as strings [514bc371] (Michael Grunder) --- phpredis 5.3.2RC1 --- * Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster [950e8de8] (Michael Grunder, Alex Offshore) * Fix xReadGroup() must return message id [500916a4] (Pavlo Yatsukhnenko) * Fix memory leak in rediscluster session handler [b2cffffc] (Pavlo Yatsukhnenko) * Fix XInfo() returns false if the stream is empty [5719c9f7, 566fdeeb] (Pavlo Yatsukhnenko, Michael Grunder) * Relax requirements on set's expire argument [36458071] (Michael Grunder) * Refactor redis_sock_check_liveness [c5950644] (Pavlo Yatsukhnenko) * PHP8 compatibility [a7662da7, f4a30cb2, 17848791] (Pavlo Yatsukhnenko, Remi Collet) * Update documentation [c9ed151d, 398c99d9] (Ali Alwash, Gregoire Pineau) * Add Redis::OPT_NULL_MULTIBULK_AS_NULL setting to treat NULL multi bulk replies as NULL instead of []. [950e8de8] (Michael Grunder, Alex Offshore) * Allow to specify stream context for rediscluster session handler [a8daaff8, 4fbe7df7] (Pavlo Yatsukhnenko) * Add new parameter to RedisCluster to specify stream ssl/tls context. [f771ea16] (Pavlo Yatsukhnenko) * Add new parameter to RedisSentinel to specify auth information [81c502ae] (Pavlo Yatsukhnenko) stable stable 5.3.1 5.3.1 2020-07-07 phpredis 5.3.1 This is a small bugfix release that fixes a couple of issues in 5.3.0. You should upgrade if you're using persistent_id in session.save_path or of if you're having trouble building 5.3.0 because the php_hash_bin2hex symbol is missing. You can find a detailed list of changes in CHANGELOG.md and package.xml * Sponsors ~ Audiomack - https://audiomack.com ~ BlueHost - https://bluehost.com ~ Redis Cache Pro for WordPress - https://wprediscache.com ~ Avtandil Kikabidze - https://github.com/akalongman --- * Properly clean up on session start failure [066cff6a] (Michael Grunder) * Treat NULL as a failure for redis_extract_auth_info [49428a2f, 14ac969d] (Michael Grunder) * Don't dereference a NULL zend_string or efree one [ff2e160f, 7fed06f2] (Michael Grunder) * Fix config.m4 messages and test for and include php_hash.h [83a1b7c5, 3c56289c, 08f202e7] (Remi Collet) * Add openSUSE installation instructions [13a168f4] (Pavlo Yatsukhnenko) * Remove EOL Fedora installation instructions [b4779e6a] (Remi Collet) stable stable 5.3.0 5.3.0 2020-06-30 phpredis 5.3.0 This release contains initial support for Redis 6 ACLs, LZ4 compression, and many more fixes and improvements. You can find a detailed list of changes in CHANGELOG.md and package.xml A special thanks to BlueHost for sponsoring ACL support \o/ * Sponsors ~ Audiomack - https://audiomack.com ~ BlueHost - https://bluehost.com ~ Redis Cache Pro for WordPress - https://wprediscache.com ~ Avtandil Kikabidze - https://github.com/akalongman phpredis 5.3.0 - There were no changes between 5.3.0RC2 and 5.3.0. --- phpredis 5.3.0RC2 --- * Fix LZ4 configuration and use pkg-config if we have it [df398cb0] (Remi Collet) * Make sure persistent pool ID is NULL terminated [0838b5bd, 57bb95bf] (Michael Grunder) * Run LZ4 tests in Travis [3ba3f06d] (Michael Grunder) phpredis 5.3.0RC1 --- * Support for Redis 6 ACLs [a311cc4e] (Michael Grunder) * LZ4 Compression [04def9fb] (Ilia Alshanetsky) * Support for new Redis 6 arguments (XINFO FULL, SET KEEPTTL) [a0c53e0b, f9c7bb57] (Michael Grunder, Victor Kislov) * Support for TLS connections [890ee0e6, b0671296] (Pavlo Yatsukhnenko) * New option Redis::SCAN_PREFIX, Redis::SCAN_NOPREFIX [e80600e2] (Pavlo Yatsukhnenko) * Configurable unit test authentication arguments [e37f38a3, 201a9759] (Pavlo Yatsukhnenko, Michael Grunder) * Improved cluster slot caching mechanism to fix a couple of bugs and make it more efficient. [5ca4141c] (Michael Grunder) * Stop calling Redis constructor when creating a RedisArray [e41e19a8] (Pavlo Yatsukhnenko) * Use ZEND_LONG_FMT instead of system `long` [5bf88124] (Michael Grunder) * Use long for SCAN iteration to fix potential overflow [f13f9b7c] (Victor Kislov) * Fix config.m4 to test for the variable $PHP_REDIS_JSON and not the literal PHP_REDIS_JSON [20a3dc72] (Mizuki Nakano) * Fix compiler warnings [b9b383f4, 215828e] (Remi Collet), Pavlo Yatsukhnenko) * Avoid use-after-free of RediSock [8c45816d] (Pavlo Yatsukhnenko) * Fixed ZADD arginfo [a8e2b021] (Pavlo Yatsukhnenko) * Store AUTH information in flags RedisSock rather than duplicating information. [58dab564] (Pavlo Yatsukhnenko) * Refactor redis_sock_get_connection_pool logic. [73212e1] (Pavlo Yatsukhnenko) * Updated documentation to show LPUSH and RPUSH are variadic and fixed DEL documentation. [92f8dde1] (Michael Grunder) * Authenticate in redis_server_sock_open [4ef465b5] (Pavlo Yatsukhnenko) * Dynamically include json.so in unit tests based on configuration [0ce7ca2f] (Michael Grunder) * Update save_path logic in Redis Cluster session unit tests [dd66fce] (Pavlo Yatsukhnenko) * Refactoring various bits of logic [bbcf32a3, a42cf189, 460c8f29, b7f9df75] (Pavlo Yatsukhnenko) * Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers [b9b383f4](Remi Collet) * PHP 8 compatibility [9ee94ca4, 7e4c7b3e] (Pavlo Yatsukhnenko) * Refactor PHPREDIS_GET_OBJECT macro [d5dadaf6, 190c0d34] (Pavlo Yatsukhnenko) * Fix documentation showing lPush and rPush are variadic [6808cd6a] (Michael Grunder) stable stable 5.2.2 5.2.2 2020-05-05 phpredis 5.2.2 This is a bugfix release that contains a fix for authentication when using persistent connections, and an option to make the ECHO challenge response logic optional. * Inexpensive liveness check, and making ECHO optional [56898f81] (Pavlo Yatsukhnenko) * Move `AUTH` to `redis_sock_server_open` [80f2529b](Pavlo Yatsukhnenko) * Sponsors ~ Audiomack.com - https://audiomack.com ~ Till Kruss - https://github.com/tillkruss stable stable 5.2.1 5.2.1 2020-03-19 phpredis 5.2.1 This is a bugfix release that fixes `redis->zAdd` arginfo as well as a segfault when closing persistent connections. * Fix arginfo for Redis::zadd [a8e2b021] (Pavlo Yatsukhnenko) * Fix segfault on closing persistent stream [b7f9df75] (Pavlo Yatsukhnenko) * Sponsors ~ Audiomack.com - https://audiomack.com ~ Till Kruss - https://github.com/tillkruss stable stable 5.2.0 5.2.0 2020-03-02 phpredis 5.2.0 - There were no changes between 5.2.0RC2 and 5.2.0. phpredis 5.2.0RC2 * Include RedisSentinelTest.php in package.xml! [eddbfc8f] (Michael Grunder) * Fix -Wmaybe-uninitialized warning [740b8c87] (Remi Collet) * Fix improper destructor when zipping values and scores [371ae7ae] (Michael Grunder) * Use php_rand instead of php_mt_rand for liveness challenge string [9ef2ed89] (Michael Grunder) phpredis 5.2.0RC1 This release contains initial support for Redis Sentinel as well as many smaller bug fixes and improvements. It is especially of interest if you use persistent connections, as we've added logic to make sure they are in a good state when retreving them from the pool. IMPORTANT: Sentinel support is considered experimental and the API will likely change based on user feedback. * Sponsors ~ Audiomack.com - https://audiomack.com ~ Till Kruss - https://github.com/tillkruss --- * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4, 383779ed] (Pavlo Yatsukhnenko) * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, 0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre, Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre) * Fix for ASK redirections [ba73fbee] (Michael Grunder) * Create specific 'test skipped' exception [c3d83d44] (Michael Grunder) * Fixed memory leaks in RedisCluster [a107c9fc] (Michael Grunder) * Fixes for session lifetime values that underflow or overflow [7a79ad9c, 3c48a332] (Michael Grunder) * Enables slot caching for Redis Cluster [23b1a9d8] (Michael Booth) * Support TYPE argument for SCAN [8eb39a26, b1724b84, 53fb36c9, 544e641b] (Pavlo Yatsukhnenko) * Added challenge/response mechanism for persistent connections [a5f95925, 25cdaee6, 7b6072e0, 99ebd0cc, 3243f426] (Pavlo Yatsukhnenko, Michael Grunder) phpredis 5.2.0RC2 * Include RedisSentinelTest.php in package.xml! [eddbfc8f] (Michael Grunder) * Fix -Wmaybe-uninitialized warning [740b8c87] (Remi Collet) * Fix improper destructor when zipping values and scores [371ae7ae] (Michael Grunder) * Use php_rand instead of php_mt_rand for liveness challenge string [9ef2ed89] (Michael Grunder) phpredis 5.2.0RC1 This release contains initial support for Redis Sentinel as well as many smaller bug fixes and improvements. It is especially of interest if you use persistent connections, as we've added logic to make sure they are in a good state when retreving them from the pool. IMPORTANT: Sentinel support is considered experimental and the API will likely change based on user feedback. * Sponsors ~ Audiomack.com - https://audiomack.com ~ Till Kruss - https://github.com/tillkruss --- * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4, 383779ed] (Pavlo Yatsukhnenko) * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, 0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre, Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre) * Fix for ASK redirections [ba73fbee] (Michael Grunder) * Create specific 'test skipped' exception [c3d83d44] (Michael Grunder) * Fixed memory leaks in RedisCluster [a107c9fc] (Michael Grunder) * Fixes for session lifetime values that underflow or overflow [7a79ad9c, 3c48a332] (Michael Grunder) * Enables slot caching for Redis Cluster [23b1a9d8] (Michael Booth) * Support TYPE argument for SCAN [8eb39a26, b1724b84, 53fb36c9, 544e641b] (Pavlo Yatsukhnenko) * Added challenge/response mechanism for persistent connections [a5f95925, 25cdaee6, 7b6072e0, 99ebd0cc, 3243f426] (Pavlo Yatsukhnenko, Michael Grunder) stable stable 5.1.1 5.1.0 2019-11-11 phpredis 5.1.1 This release contains only bugfix for unix-socket connection. * Fix fail to connect to redis through unix socket [2bae8010, 9f4ededa] (Pavlo Yatsukhnenko, Michael Grunder) * Documentation improvements (@fitztrev) stable stable 5.1.0 5.1.0 2019-10-31 This release contains important bugfixes and improvements. phpredis 5.1.0 * Allow to specify scheme for session handler [53a8bcc7] (Pavlo Yatsukhnenko) * Add documentation for hyperloglog [75a6f3fa, 96a0f0c3, 9686757a] (@rlunar) phpredis 5.1.0RC2 * Fix missing null byte in PHP_MINFO_FUNCTION [8bc2240c] (Remi Collet) * Remove dead code generic_unsubscribe_cmd [8ee4abbc] (Pavlo Yatsukhnenko) * Add documentation for zpopmin and zpopmax [99ec24b3, 4ab1f940] (@alexander-schranz) phpredis 5.1.0RC1 * Fix regression for multihost_distribute_call added in 112c77e3 [fbe0f804] (Pavlo Yatsukhnenko) * Fix regression for conntecting to unix sockets with relative path added in 1f41da64 [17b139d8, 7ef17ce1] (Pavlo Yatsukhnenko) * Fix unix-socket detection logic broken in 418428fa [a080b73f] (Pavlo Yatsukhnenko) * Fix memory leak and bug with getLastError for redis_mbulk_reply_assoc and redis_mbulk_reply_zipped. [7f42d628, 3a622a07] (Pavlo Yatsukhnenko), (Michael Grunder) * Fix bug with password contain "#" for redis_session [2bb08680] (Pavlo Yatsukhnenko) * Add optional support for Zstd compression, using --enable-redis-ztsd. This requires libzstd version >= 1.3.0 [2abc61da] (Remi Collet) * Fix overallocation in RedisCluster directed node commands [cf93649] (Michael Grunder) * Also attach slaves when caching cluster slots [0d6d3fdd, b114fc26] (Michael Grunder) * Use zend_register_persistent_resource_ex for connection pooling [fdada7ae, 7c6c43a6] (Pavlo Yatsukhnenko) * Refactor redis_session [91a8e734, 978c3074] (Pavlo Yatsukhnenko) * Documentation improvements (@Steveb-p, @tangix, @ljack-adista, @jdreesen, Michael Grunder) stable stable 5.0.0 5.0.0 2019-07-02 This release contains important improvements and breaking changes. The most interesting are: drop PHP5 support, RedisCluster slots caching, JSON and msgpack serializers, soft deprecation of non-Redis commands. phpredis 5.0.0 * Remove HAVE_SPL [55c5586c] (@petk) * Update Fedora installation instructions [90aa067c] (@remicollet) phpredis 5.0.0RC2 * Allow compilation without JSON serialization enabled and fixes for deprecated helper methods. [235a27] (Pavlo Yatsukhnenko) * Fix php msgpack >= 2.0.3 version requirement. [6973478..a537df8] (Michael Grunder) phpredis 5.0.0RC1 * Enable connection pooling by default [8206b147] (Pavlo Yatsukhnenko) * Soft deprecate methods that aren't actually Redis commands [a81b4f2d, 95c8aab9] (Pavlo Yatsukhnenko, Michael Grunder) * Enable pooling for cluster slave nodes [17600dd1] (Michael Grunder) * xInfo response format [4852a510, ac9dca0a] (Pavlo Yatsukhnenko) * Make the XREADGROUP optional COUNT and BLOCK arguments nullable [0c17bd27] (Michael Grunder) * Allow PING to take an optional argument [6e494170] (Michael Grunder) * Allow ZRANGE to be called either with `true` or `['withscores' => true]` [19f3efcf] (Michael Grunder) * Allow to specify server address as schema://host [418428fa] (Pavlo Yatsukhnenko) * Allow persistent_id to be passed as NULL with strict_types enabled [60223762] (Michael Grunder) * Add server address to exception message [e8fb49be, 34d6403d] (Pavlo Yatsukhnenko) * Adds OPT_REPLY_LITERAL for rawCommand and EVAL [5cb30fb2] (Michael Grunder) * JSON serializer [98bd2886, 96c57139] (Pavlo Yatsukhnenko, Michael Grunder) * Add support for STREAM to the type command [d7450b2f, 068ce978, 8a45d18c] (Michael Grunder, Pavlo Yatsukhnenko) * Fix TypeError when using built-in constants in `setOption` [4c7643ee] (@JoyceBabu) * Handle references in MGET [60d8b679] (Michael Grunder) * msgpack serializer [d5b8f833, 545250f3, 52bae8ab] (@bgort, Pavlo Yatsukhnenko, Michael Grunder) * Add RedisCluster slots caching [9f0d7bc0, ea081e05] (Michael Grunder) * Drop PHP5 support [f9928642, 46a50c12, 4601887d, 6ebb36ce, fdbe9d29] (Michael Grunder) * Documentation improvements (@alexander-schranz, @cookieguru, Pavlo Yatsukhnenko, Michael Grunder) stable stable 4.3.0 4.3.0 2019-03-13 phpredis 4.3.0 This is probably the last release with PHP 5 suport!!! * Proper persistent connections pooling implementation [a3703820, c76e00fb, 0433dc03, c75b3b93] (Pavlo Yatsukhnenko) * RedisArray auth [b5549cff, 339cfa2b, 6b411aa8] (Pavlo Yatsukhnenko) * Use zend_string for storing key hashing algorithm [8cd165df, 64e6a57f] (Pavlo Yatsukhnenko) * Add ZPOPMAX and ZPOPMIN support [46f03561, f89e941a, 2ec7d91a] (@mbezhanov, Michael Grunder) * Implement GEORADIUS_RO and GEORADIUSBYMEMBER_RO [22d81a94] (Michael Grunder) * Add callback parameter to subscribe/psubscribe arginfo [0653ff31] (Pavlo Yatsukhnenko) * Don't check the number affected keys in PS_UPDATE_TIMESTAMP_FUNC [b00060ce] (Pavlo Yatsukhnenko) * Xgroup updates [15995c06] (Michael Grunder) * RedisCluster auth [c5994f2a] (Pavlo Yatsukhnenko) * Cancel pipeline mode without executing commands [789256d7] (Pavlo Yatsukhnenko) * Use zend_string for pipeline_cmd [e98f5116] (Pavlo Yatsukhnenko) * Different key hashing algorithms from hash extension [850027ff] (Pavlo Yatsukhnenko) * Breaking the lock acquire loop in case of network problems [61889cd7] (@SkydiveMarius) * Implement consistent hashing algorithm for RedisArray [bb32e6f3, 71922bf1] (Pavlo Yatsukhnenko) * Use zend_string for storing RedisArray hosts [602740d3, 3e7e1c83] (Pavlo Yatsukhnenko) * Update lzf_compress to be compatible with PECL lzf extension [b27fd430] (@jrchamp) * Fix RedisCluster keys memory leak [3b56b7db] (Michael Grunder) * Directly use return_value in RedisCluster::keys method [ad10a49e] (Pavlo Yatsukhnenko) * Fix segfault in Redis Cluster with inconsistent configuration [72749916, 6e455e2e] (Pavlo Yatsukhnenko) * Masters info leakfix [91bd7426] (Michael Grunder) * Refactor redis_sock_read_bulk_reply [bc4dbc4b] (Pavlo Yatsukhnenko) * Remove unused parameter lazy_connect from redis_sock_create [c0793e8b] (Pavlo Yatsukhnenko) * Remove useless ZEND_ACC_[C|D]TOR. [bc9b5597] (@twosee) * Documentation improvements (@fanjiapeng, @alexander-schranz, @hmc, Pavlo Yatsukhnenko, Michael Grunder) beta beta 4.2.0RC3 4.2.0RC3 2018-11-08 phpredis 4.2.0RC3 The main feature of this release is new Streams API implemented by Michael Grunder. 4.2.0RC3: * Optimize close method [2a1ef961] (fanjiapeng) * Prevent potential infinite loop for sessions [4e2de158] (Pavlo Yatsukhnenko) * Fix coverty warnings [6f7ddd27] (Pavlo Yatsukhnenko) * Fix session memory leaks [071a1d54, 92f14b14] (Pavlo Yatsukhnenko, Michael Grunder) * Fix XCLAIM on 32-bit installs [18dc2aac] (Michael Grunder) * Build warning fixes [b5093910, 51027044, 8b0f28cd] (Pavlo Yatsukhnenko, Remi Collet, twosee) 4.2.0RC2: * Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce] (Pavlo Yatsukhnenko) * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) * Treat a -1 response from cluster_check_response as a timeout. [27df9220, 07ef7f4e, d1172426] (Michael Grunder) * Use a ZSET insted of SET for EVAL tests [2e412373] (Michael Grunder) * Missing space between command and args [0af2a7fe] (@remicollet) 4.2.0RC1: * Streams API [2c9e0572] (Michael Grunder) * Reset the socket after a timeout to make sure no wrong data is received [cd6ebc6d] (@marcdejonge) * Modify session testing logic [bfd27471] (Michael Grunder) * Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex [d4a08697] (Michael Grunder) * Fix printf format warnings [dcde9331] (Pavlo Yatsukhnenko) * Session module is required [58bd8cc8] (@remicollet) * Set default values for ini entries [e206ce9c] (Pavlo Yatsukhnenko) * Display ini entries in output of phpinfo [908ac4b3] (Pavlo Yatsukhnenko) * Persistent connections can be closed via close method + change reconnection logic [1d997873] (Pavlo Yatsukhnenko) * Documentation improvements (@mg, @elcheco, @lucascourot, @nolimitdev, Michael Grunder) stable stable 4.1.1 4.1.1 2018-08-01 phpredis 4.1.1 This release contains only bugfixes and documentation improvements * Fix arginfo for Redis::set method [0c5e7f4d] (Pavlo Yatsukhnenko) * Fix compression in RedisCluster [a53e1a34] (Pavlo Yatsukhnenko) * Fix TravisCI builds [9bf32d30] (@jrchamp) * Highlight php codes in documentation [c3b023b0] (@ackintosh) stable stable 4.1.0 4.1.0 2018-01-10 phpredis 4.1.0 The primary new feature of this release is session locking functionality. Thanks to @SkydiveMarius! * Add callbacks validate_sid and update_timestamp to session handler [aaaf0f23] (@hongboliu) * Call cluster_disconnect before destroying cluster object. [28ec4322] (Pavlo Yatsukhnenko) * Bulk strings can be zero length. (Michael Grunder) * Handle async parameter for flushDb and flushAll [beb6e8f3,acd10409,742cdd05] (Pavlo Yatsukhnenko) * Split INSTALL and add more instructions [43613d9e,80d2a917] (@remicollet, Pavlo Yatsukhnenko) * Only the first arg of connect and pconnect is required [063b5c1a] (@mathroc) * Add session locking functionality [300c7251] (@SkydiveMarius, Michael Grunder, Pavlo Yatsukhnenko) * Fix compression in RedisCluster [1aed74b4] (Pavlo Yatsukhnenko) * Refactor geo* commands + documentation improvements (Michael Grunder) stable stable 4.0.2 4.0.2 2018-04-25 phpredis 4.0.2 This release contains only fix of exists method to take multiple keys and return integer value (was broken in 4.0.1) Thanks @RanjanRohit! stable stable 4.0.1 4.0.1 2018-04-18 phpredis 4.0.1 * Fix arginfo for connect/pconnect issue #1337 [c3b228] (@mathroc) * Don't leak a ZVAL [278232] (Michael Grunder) * Fix config.m4 for lzf issue #1325 [20e173] (Pavlo Yatsukhnenko) * Updates EXISTS documentation and notes change in 4.0.0 [bed186] (Michael Grunder) * Fix typo in notes [0bed36] (@szepeviktor) stable stable 4.0.0 4.0.0 2018-03-17 phpredis 4.0.0 *** WARNING! THIS RELEASE CONTAINS BREAKING API CHANGES! *** * Add proper ARGINFO for all methods. (Pavlo Yatsukhnenko, Michael Grunder) * Let EXISTS take multiple keys [cccc39] (Michael Grunder) * Use zend_string as returning value for ra_extract_key and ra_call_extractor [9cd05911] (Pavlo Yatsukhnenko) * Implement SWAPDB and UNLINK commands [84f1f28b, 9e65c429] (Michael Grunder) * Return real connection error as exception [5b9c0c60] (Pavlo Yatsukhnenko, Michael Grunder) * Disallow using empty string as session name. [485db46f] (Pavlo Yatsukhnenko) * Use zend_string for storing auth and prefix members [4b8336f7] (Pavlo Yatsukhnenko) * The element of z_seeds may be a reference on php7 [367bc6aa, 1e63717a] (@janic716) * Avoid connection in helper methods [91e9cfe1] (Pavlo Yatsukhnenko) * Add tcp_keepalive option to redis sock [68c58513, 5101172a, 010336d5, 51e48729] (@git-hulk, Michael Grunder) * More robust GEORADIUS COUNT validation [f7edee5d] (Michael Grunder) * Add LZF compression (experimental) [e2c51251, 8cb2d5bd, 8657557] (Pavlo Yatsukhnenko) * Allow to use empty string as persistent_id [ec4fd1bd] (Pavlo Yatsukhnenko) * Don't use convert_to_string in redis_hmget_cmd [99335d6] (Pavlo Yatsukhnenko) * Allow mixing MULTI and PIPELINE modes (experimental) [5874b0] (Pavlo Yatsukhnenko) * PHP >=7.3.0 uses zend_string to store `php_url` elements [b566fb44] (@fmk) * Documentation improvements (Michael Grunder, @TomA-R) stable stable 3.1.6 3.1.6 2018-01-03 phpredis 3.1.6 This release conains only fix of RedisArray distributor hashing function which was broken in 3.1.4. Huge thanks to @rexchen123 stable stable 3.1.5 3.1.5 2017-12-20 phpredis 3.1.5 This is interim release which contains only bug fixes. * Fix segfault when extending Redis class in PHP 5 [d23eff] (Pavlo Yatsukhnenko) * Fix RedisCluster constructor with PHP 7 strict scalar type [5c21d7] (Pavlo Yatsukhnenko) * Allow to use empty string as persistent_id [344de5] (Pavlo Yatsukhnenko) * Fix cluster_init_seeds. [db1347] (@adlagares) * Fix z_seeds may be a reference [42581a] (@janic716) * PHP >=7.3 uses zend_string for php_url elements [b566fb] (@fmk) stable stable 3.1.4 3.1.4 2017-09-27 phpredis 3.1.4 The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are also many bugfixes and minor improvements to the api, listed below: * Allow mixing MULTI and PIPELINE modes (experimental)! [5874b0] (Pavlo Yatsukhnenko) * Added integration for coverty static analysis and fixed several warnings [faac8b0, eff7398, 4766c25, 0438ab4, 1e0b065, 733732a, 26eeda5, 735025, 42f1c9, af71d4] (Pavlo Yatsukhnenko) * Added arginfo inrospection structures [81a0303, d5609fc, e5660be, 3c60e1f, 50dcb15, 6c2c6fa, 212e323, e23be2c, 682593d, f8de702, 4ef3acd, f116be9, 5c111dd, 9caa029, 0d69650, 6859828, 024e593, 3643ab6, f576fab, 122d41f, a09d0e6] (Tyson Andre, Pavlo Yatsukhnenko) * Fixed link to redis cluster documentation [3b0b06] (Pavlo Yatsukhnenko) * Remove unused PHP_RINIT and PHP_RSHUTDOWN functions [c760bf] (Pavlo Yatsukhnenko) * Removed duplicate HGET in redis array hash table, formatting [d0b9c5] (Pavlo Yatsukhnenko) * Treat NULL bulk as success for session read [659450] (Pavlo Yatsukhnenko) * Refactor redis_send_discard [ea15ce] (Pavlo Yatsukhnenko) * Updated runtime exception handling [8dcaa4, 7c1407] (Pavlo Yatsukhnenko) * Added a github issue template [61aba9] (Pavlo Yatsukhnenko) * Initialize gc member of zend_string [37f569) (Pavlo Yatsukhnenko) * Fix valgrind warnings [471ce07, 1ab89e1, b624a8b] (Pavlo Yatsukhnenko) * Fix php5/php7 compatibility layer [1ab89e, 4e3225] (Pavlo Yatsukhnenko) * Fix typo in README.markdown [e47e44] (Mark Shehata) * Improve redis array rehash [577a91] (Pavlo Yatsukhnenko) * Change redis array pure_cmds from zval to hashtable [a56ed7] (Pavlo Yatsukhnenko) * Don't try to set TCP_NODELAY on a unix socket and don't warn on multiple calls to pipeline [d11798, 77aeba] (Michael Grunder) * Use zend_string rather than char* for various context fields (err, prefix, etc) [2bf7b2] (Pavlo Yatsukhnenko) * Various other library fixes [142b51, 4452f6, e672f4, 658ee3, c9df77, 4a0a46] (Pavlo Yatsukhnenko) stable stable 3.1.3 3.1.3 2017-07-15 phpredis 3.1.3 This release contains two big improvements: 1. Adding a new printf like command construction function with additionaly format specifiers specific to phpredis. 2. Implementation of custom objects for Redis and RedisArray wich eliminates double hash lookup. Also many small improvements and bug fixes were made. * A printf like method to construct a Redis RESP command [a4a0ed, d75081, bdd287, 0eaeae, b3d00d] (Michael Grunder) * Use custom objects instead of zend_list for storing Redis/RedisArray [a765f8, 8fa85a] (Pavlo Yatsukhnenko) * Make sure redisCluster members are all initialized on (re)creation [162d88] (Michael Grunder) * Fix Null Bulk String response parsing in cluster library [058753] (Alberto Fernandez) * Add hStrLen command [c52077, fb88e1] (Pavlo Yatsukhnenko) * Add optional COUNT argument to sPop [d2e203] (Michael Grunder) * Allow sInterStore to take one arg [26aec4, 4cd06b] (Michael Grunder) * Allow MIGRATE to accept multiple keys [9aa3db] (Michael Grunder) * Allow using numeric string in zInter command [ba0070] (Pavlo Yatsukhnenko) * Use crc32 table from PHP distro [f81694] (Pavlo Yatsukhnenko) * Use ZVAL_DEREF macros for dereference input variables [ad4596] (Pavlo Yatsukhnenko) * Add configureoption tag to package.xml [750963] (Pavlo Yatsukhnenko) * Fix read_timeout [18149e, b56dc4] (Pavlo Yatsukhnenko) * Fix zval_get_string impl for PHP5 [4e56ba] (Pavlo Yatsukhnenko) * Fix Redis/RedisArray segfaults [be5c1f, 635c3a, 1f8dde, 43e1e0] (Pavlo Yatsukhnenko) * Fix memory leak and potential segfault [aa6ff7, 88efaa] (Michael Grunder) * Throw exception for all non recoverable errors [e37239] (Pavlo Yatsukhnenko) * Assume "NULL bulk" reply as success (empty session data) [4a81e1] (Pavlo Yatsukhnenko) * Increase read buffers size [520e06] (Pavlo Yatsukhnenko) * Better documentation [f0c25a, c5991f, 9ec9ae] (Michael Grunder) * Better TravisCI integration [e37c08] (Pavlo Yatsukhnenko) * Refactoring (Pavlo Yatsukhnenko, Michael Grunder) stable stable 3.1.2 3.1.2 2017-03-16 phpredis 3.1.2 * RedisArray segfault fix [564ce3] (Pavlo Yatsukhnenko) * Small memory leak fix [645888b] (Mike Grunder) * Segfault fix when recreating RedisCluster objects [abf7d4] (Michael Grunder) * Fix for RedisCluster bulk response parsing [4121c4] (Alberto Fernandez) * Re allow single array for sInterStore [6ef0c2, d01966] (Michael Grunder) * Better TravisCI integration [4fd2f6] (Pavlo Yatsukhnenko) beta beta 3.1.1RC2 3.1.1RC2 2017-01-16 phpredis 3.1.1RC2 * Additional test updates for 32 bit systems (@remicollet) * ARM rounding issue in tests (@remicollet) * Use new zend_list_close instead of zend_list_delete when reconnecting. * Refactoring of redis_boolean_response_impl and redis_sock_write (@yatsukhnenko) phpredis 3.1.1.RC1 This release contains mostly fixes for issues introduced when merging the php 5 and 7 codebase into a single branch. * Fixed a segfault in igbinary serialization (@yatsukhnenko) * Restore 2.2.8/3.0.0 functionality to distinguish between an error and simply empty session data. (@remicollet) * Fix double to string conversion function (@yatsukhnenko) * Use PHP_FE_END definition when available (@remicollet) * Fixed various 'static function declared but not used' warnings * Fixes to various calls which were typecasting pointers to the wrong size. (@remicollet) * Added php session unit test (@yatsukhnenko) * Added explicit module dependancy for igbinary (@remicollet) * Added phpinfo serialization information (@remicollet) stable stable 3.1.0 3.1.0 2016-12-14 phpredis 3.1.0 In this version of phpredis codebase was unified to work with all versions of php \o/ Also many bug fixes and some improvements has been made. --- Improvements --- * Support the client to Redis Cluster just having one master (andyli) [892e5646] * Allow both long and strings that are longs for zrangebyscore offset/limit (Michael Grunder) [bdcdd2aa] * Process NX|XX, CH and INCR options in zAdd command (Pavlo Yatsukhnenko) [71c9f7c8] --- Fixes --- * Fix incrby/decrby for large integers (Michael Grunder) [3a12758a] * Use static declarations for spl_ce_RuntimeException decl (Jeremy Mikola) [a9857d69] * Fixed method call problem causes session handler to display two times (ZiHang Gao) [24f86c49] * psetex method returns '+OK' on success, not true (sitri@ndxbn) [afcd8445] * Fix integer overflow for long (>32bit) increments in hIncrBy (iyesin) [58e1d799] * Move zend_object handler to the end (Michael Grunder) [34107966] * Using setOption on redis array causes immediate connection (Pavlo Yatsukhnenko) [f1a85b38] stable stable 2.2.8 2.2.8 2016-06-02 phpredis 2.2.8 The main improvement in this version of phpredis is support for Redis Cluster. This version of phpredis is intended for versions of php older than 7. In addition there have been many bug fixes and improvements to non cluster related commands, which are listed below. I've attempted to include everyone who contribued to the project in each fix description and have included names or github user ids. Thanks to everyone for submitting bug reports and pull requests. A special thanks to Remi Collet for helping with any and all packaging related issues \o/ --- Improvements --- * Added randomization to our seed nodes to balance which instance is used to map the keyspace (Vitaliy Stepanyuk) [32eb1c5f] * Added support for IPv6 addresses --- Fixes --- * PHP liveness checking workaround (Shafreeck Sea) [c18d58b9] * Various documentation and code formatting and style fixes (ares333, sanpili, Bryan Nelson, linfangrong, Romero Malaquias, Viktor Szepe) * Fix scan reply processing to use long instead of int to avoid overflow (mixiaojiong). * Fix potential segfault in Redis Cluster session storage (Sergei Lomakov) [cc15aae] * Fixed memory leak in discard function [17b1f427] * Sanity check for igbinary unserialization (Maurus Cuelenaere) [3266b222, 5528297a] * Fix segfault occuring from unclosed socket connection for Redis Cluster (CatKang) [04196aee] * Case insensitive zRangeByScore options * Fixed dreaded size_t vs long long compiler warning stable stable 2.2.7 2.2.7 2015-03-03 phpredis 2.2.7 -- Improvements --- * Implemented PFADD, PFMERGE, and PFCOUNT command handling * Implemented ZRANGEBYLEX command (holding off on ZREVRANGEBYLEX as that won't be out until 3.0) * Implemented getMode() so clients can detect whether we're in ATOMIC/MULTI/PIPELINE mode. * Implemented rawCommand() so clients can send arbitrary things to the redis server * Implemented DEBUG OBJECT (@michael-grunder, @isage) * Added/abide by connect timeout for RedisArray * Select to the last selected DB when phpredis reconnects -- Fixes --- * Fix a possible invalid free in _serialize * Added SAVE and BGSAVE to "distributable" commands for RedisArray * @welting -- Fixed invalid "argc" calculation re HLL commands * Allow clients to break out of the subscribe loop and return context. * Fixes a memory leak in SCAN when OPT_SCAN_RETRY id. * @remicollet -- Fix possible segfault when igbinary is enabled. * Add a couple of cases where we throw on an error (LOADING/NOAUTH/MASTERDOWN) * Fix several issues with serialization NARY * @itcom -- Fix missing TSRMLS_CC and a TSRMLS_DC/TSRMLS_CC typo stable stable 2.2.5 2.2.5 2014-03-15 phpredis 2.2.5 This is a minor release with several bug fixes as well as additions to support new commands that have been introduced to Redis since our last release. A special thanks to everyone who helps the project by commenting on issues and submitting pull requests! :) [NEW] Support for the BITPOS command [NEW] Connection timeout option for RedisArray (@MikeToString) [NEW] A _serialize method, to complement our existing _unserialize method [NEW] Support for the PUBSUB command [NEW] Support for SCAN, SSCAN, HSCAN, and ZSCAN [NEW] Support for the WAIT command [FIX] Handle the COPY and REPLACE arguments for the MIGRATE command [DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh) [DOC] Homebrew documentation instructions (@mathias) stable stable 2.2.4 2.2.4 2013-09-01 ** ** Features / Improvements ** * Randomized reconnect delay for RedisArray @mobli This feature adds an optional parameter when constructing a RedisArray object such that a random delay will be introduced if reconnections are made, mitigating any 'thundering herd' type problems. * Lazy connections to RedisArray servers @mobli By default, RedisArray will attempt to connect to each server you pass in the ring on construction. This feature lets you specify that you would rather have RedisArray only attempt a connection when it needs to get data from a particular node (throughput/performance improvement). * Allow LONG and STRING keys in MGET/MSET * Extended SET options for Redis >= 2.6.12 * Persistent connections and UNIX SOCKET support for RedisArray * Allow aggregates for ZUNION/ZINTER without weights @mheijkoop * Support for SLOWLOG command * Reworked MGET algorithm to run in linear time regardless of key count. * Reworked ZINTERSTORE/ZUNIONSTORE algorithm to run in linear time ** ** Bug fixes ** * C99 Compliance (or rather lack thereof) fix @mobli * Added ZEND_ACC_CTOR and ZEND_ACC_DTOR @euskadi31 * Stop throwing and clearing an exception on connect failure @matmoi * Fix a false positive unit test failure having to do with TTL returns stable stable 2.2.3 2.2.3 2013-04-29 First release to PECL redis-6.0.2/liblzf/LICENSE0000644000175000000120000000263514515245367015715 0ustar pyatsukhnenkowheelCopyright (c) 2000-2009 Marc Alexander Lehmann Redistribution and use in source and binary forms, with or without modifica- tion, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Alternatively, the following files carry an additional notice that explicitly allows relicensing under the GPLv2: lzf.c lzf.h lzfP.h lzf_c.c lzf_d.c redis-6.0.2/liblzf/README0000644000175000000120000000233714515245367015567 0ustar pyatsukhnenkowheelDESCRIPTION LZF is an extremely fast (not that much slower than a pure memcpy) compression algorithm. It is ideal for applications where you want to save *some* space but not at the cost of speed. It is ideal for repetitive data as well. The module is self-contained and very small. It's written in ISO-C with no external dependencies other than what C provides and can easily be #include'd into your code, no makefile changes or library builds requires. A C♯ implementation without external dependencies is available, too. I do not know for certain whether any patents in any countries apply to this algorithm, but at the moment it is believed that it is free from any patents. More importantly, it is also free to use in every software package (see LICENSE). See the lzf.h file for details on how the functions in this mini-library are to be used. NOTE: This package contains a very bare-bones command-line utility which is neither optimized for speed nor for compression. This library is really intended to be used inside larger programs. AUTHOR This library was written by Marc Lehmann (See also http://software.schmorp.de/pkg/liblzf). redis-6.0.2/liblzf/lzf.h0000644000175000000120000001170614515245367015653 0ustar pyatsukhnenkowheel/* * Copyright (c) 2000-2008 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #ifndef LZF_H #define LZF_H /*********************************************************************** ** ** lzf -- an extremely fast/free compression/decompression-method ** http://liblzf.plan9.de/ ** ** This algorithm is believed to be patent-free. ** ***********************************************************************/ /* API version (major * 256 + minor) * major API version gets bumped on incompatible changes. * minor API version gets bumped on compatible changes. * 1.5 => 1.6: add LZF_MAX_COMPRESSED_SIZE */ #define LZF_VERSION 0x0106 /* * Compress in_len bytes stored at the memory block starting at * in_data and write the result to out_data, up to a maximum length * of out_len bytes. * * If the output buffer is not large enough or any error occurs return 0, * otherwise return the number of bytes used, which might be considerably * more than in_len (but less than 1 + 104% of the original size), so it * makes sense to always use out_len == in_len - 1), to ensure _some_ * compression, and store the data uncompressed otherwise (with a flag, of * course. * * lzf_compress might use different algorithms on different systems and * even different runs, thus might result in different compressed strings * depending on the phase of the moon or similar factors. However, all * these strings are architecture-independent and will result in the * original data when decompressed using lzf_decompress. * * The buffers must not be overlapping. * * If the option LZF_STATE_ARG is enabled, an extra argument must be * supplied which is not reflected in this header file. Refer to lzfP.h * and lzf_c.c. * */ unsigned int lzf_compress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len); /* * The maximum out_len that needs to be allocated to make sure * any input data can be compressed without overflowing the output * buffer, i.e. maximum out_len = LZF_MAX_COMPRESSED_SIZE (in_len). * This is useful if you don't want to bother with the case of * incompressible data and just want to provide a buffer that is * guaranteeed to be big enough. * This macro can be used at preprocessing time. */ #define LZF_MAX_COMPRESSED_SIZE(n) ((((n) * 33) >> 5 ) + 1) /* * Decompress data compressed with some version of the lzf_compress * function and stored at location in_data and length in_len. The result * will be stored at out_data up to a maximum of out_len characters. * * If the output buffer is not large enough to hold the decompressed * data, a 0 is returned and errno is set to E2BIG. Otherwise the number * of decompressed bytes (i.e. the original length of the data) is * returned. * * If an error in the compressed data is detected, a zero is returned and * errno is set to EINVAL. * * This function is very fast, about as fast as a copying loop. */ unsigned int lzf_decompress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len); #endif redis-6.0.2/liblzf/lzfP.h0000644000175000000120000001557514515245367016003 0ustar pyatsukhnenkowheel/* * Copyright (c) 2000-2007 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #ifndef LZFP_h #define LZFP_h #define STANDALONE 1 /* at the moment, this is ok. */ #ifndef STANDALONE # include "lzf.h" #endif /* * Size of hashtable is (1 << HLOG) * sizeof (char *) * decompression is independent of the hash table size * the difference between 15 and 14 is very small * for small blocks (and 14 is usually a bit faster). * For a low-memory/faster configuration, use HLOG == 13; * For best compression, use 15 or 16 (or more, up to 22). */ #ifndef HLOG # define HLOG 16 #endif /* * Sacrifice very little compression quality in favour of compression speed. * This gives almost the same compression as the default code, and is * (very roughly) 15% faster. This is the preferred mode of operation. */ #ifndef VERY_FAST # define VERY_FAST 1 #endif /* * Sacrifice some more compression quality in favour of compression speed. * (roughly 1-2% worse compression for large blocks and * 9-10% for small, redundant, blocks and >>20% better speed in both cases) * In short: when in need for speed, enable this for binary data, * possibly disable this for text data. */ #ifndef ULTRA_FAST # define ULTRA_FAST 0 #endif /* * Unconditionally aligning does not cost very much, so do it if unsure */ #ifndef STRICT_ALIGN # define STRICT_ALIGN !(defined(__i386) || defined (__amd64)) #endif /* * You may choose to pre-set the hash table (might be faster on some * modern cpus and large (>>64k) blocks, and also makes compression * deterministic/repeatable when the configuration otherwise is the same). */ #ifndef INIT_HTAB # define INIT_HTAB 1 #endif /* * Avoid assigning values to errno variable? for some embedding purposes * (linux kernel for example), this is necessary. NOTE: this breaks * the documentation in lzf.h. Avoiding errno has no speed impact. */ #ifndef AVOID_ERRNO # define AVOID_ERRNO 0 #endif /* * Whether to pass the LZF_STATE variable as argument, or allocate it * on the stack. For small-stack environments, define this to 1. * NOTE: this breaks the prototype in lzf.h. */ #ifndef LZF_STATE_ARG # define LZF_STATE_ARG 0 #endif /* * Whether to add extra checks for input validity in lzf_decompress * and return EINVAL if the input stream has been corrupted. This * only shields against overflowing the input buffer and will not * detect most corrupted streams. * This check is not normally noticeable on modern hardware * (<1% slowdown), but might slow down older cpus considerably. */ #ifndef CHECK_INPUT # define CHECK_INPUT 1 #endif /* * Whether the target CPU has a slow multiplication. This affects * the default hash function for the compressor, and enables a slightly * worse hash function that needs only shifts. */ #ifndef MULTIPLICATION_IS_SLOW # define MULTIPLICATION_IS_SLOW 0 #endif /* * If defined, then this data type will be used for storing offsets. * This can be useful if you want to use a huge hashtable, want to * conserve memory, or both, and your data fits into e.g. 64kb. * If instead you want to compress data > 4GB, then it's better to * to "#define LZF_USE_OFFSETS 0" instead. */ /*#define LZF_HSLOT unsigned short*/ /* * Whether to store pointers or offsets inside the hash table. On * 64 bit architetcures, pointers take up twice as much space, * and might also be slower. Default is to autodetect. */ /*#define LZF_USE_OFFSETS autodetect */ /* * Whether to optimise code for size, at the expense of speed. Use * this when you are extremely tight on memory, perhaps in combination * with AVOID_ERRNO 1 and CHECK_INPUT 0. */ #ifndef OPTIMISE_SIZE # ifdef __OPTIMIZE_SIZE__ # define OPTIMISE_SIZE 1 # else # define OPTIMISE_SIZE 0 # endif #endif /*****************************************************************************/ /* nothing should be changed below */ #ifdef __cplusplus # include # include using namespace std; #else # include # include #endif #if ULTRA_FAST # undef VERY_FAST #endif #ifndef LZF_USE_OFFSETS # ifdef _WIN32 # define LZF_USE_OFFSETS defined(_M_X64) # else # if __cplusplus > 199711L # include # else # include # endif # define LZF_USE_OFFSETS (UINTPTR_MAX > 0xffffffffU) # endif #endif typedef unsigned char u8; #ifdef LZF_HSLOT # define LZF_HSLOT_BIAS ((const u8 *)in_data) #else # if LZF_USE_OFFSETS # define LZF_HSLOT_BIAS ((const u8 *)in_data) typedef unsigned int LZF_HSLOT; # else # define LZF_HSLOT_BIAS 0 typedef const u8 *LZF_HSLOT; # endif #endif #if USHRT_MAX == 65535 typedef unsigned short u16; #elif UINT_MAX == 65535 typedef unsigned int u16; #else # undef STRICT_ALIGN # define STRICT_ALIGN 1 #endif #define LZF_MAX_LIT (1 << 5) #define LZF_MAX_OFF (1 << 13) #define LZF_MAX_REF ((1 << 8) + (1 << 3)) typedef LZF_HSLOT LZF_STATE[1 << (HLOG)]; typedef struct { const u8 *first [1 << (6+8)]; /* most recent occurance of a match */ u16 prev [LZF_MAX_OFF]; /* how many bytes to go backwards for the next match */ } LZF_STATE_BEST[1]; #endif redis-6.0.2/liblzf/lzf_c.c0000644000175000000120000002137114515245367016147 0ustar pyatsukhnenkowheel/* * Copyright (c) 2000-2010,2012 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #include "lzfP.h" #define HSIZE (1 << (HLOG)) /* * don't play with this unless you benchmark! * the data format is not dependent on the hash function. * the hash function might seem strange, just believe me, * it works ;) */ #ifndef FRST # define FRST(p) (((p[0]) << 8) | p[1]) # define NEXT(v,p) (((v) << 8) | p[2]) # if MULTIPLICATION_IS_SLOW # if ULTRA_FAST # define IDX(h) ((( h >> (3*8 - HLOG)) - h ) & (HSIZE - 1)) # elif VERY_FAST # define IDX(h) ((( h >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) # else # define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) # endif # else /* this one was developed with sesse, * and is very similar to the one in snappy. * it does need a modern enough cpu with a fast multiplication. */ # define IDX(h) (((h * 0x1e35a7bdU) >> (32 - HLOG - 8)) & (HSIZE - 1)) # endif #endif #if 0 /* original lzv-like hash function, much worse and thus slower */ # define FRST(p) (p[0] << 5) ^ p[1] # define NEXT(v,p) ((v) << 5) ^ p[2] # define IDX(h) ((h) & (HSIZE - 1)) #endif #if __GNUC__ >= 3 # define expect(expr,value) __builtin_expect ((expr),(value)) # define inline inline #else # define expect(expr,value) (expr) # define inline static #endif #define expect_false(expr) expect ((expr) != 0, 0) #define expect_true(expr) expect ((expr) != 0, 1) /* * compressed format * * 000LLLLL ; literal, L+1=1..33 octets * LLLooooo oooooooo ; backref L+1=1..7 octets, o+1=1..4096 offset * 111ooooo LLLLLLLL oooooooo ; backref L+8 octets, o+1=1..4096 offset * */ unsigned int lzf_compress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len #if LZF_STATE_ARG , LZF_STATE htab #endif ) { #if !LZF_STATE_ARG LZF_STATE htab; #endif const u8 *ip = (const u8 *)in_data; u8 *op = (u8 *)out_data; const u8 *in_end = ip + in_len; u8 *out_end = op + out_len; const u8 *ref; /* off requires a type wide enough to hold a general pointer difference. * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only * works for differences within a single object). We also assume that * no bit pattern traps. Since the only platform that is both non-POSIX * and fails to support both assumptions is windows 64 bit, we make a * special workaround for it. */ #if defined (_WIN32) && defined (_M_X64) /* workaround for missing POSIX compliance */ #if __GNUC__ unsigned long long off; #else unsigned __int64 off; #endif #else unsigned long off; #endif unsigned int hval; int lit; if (!in_len || !out_len) return 0; #if INIT_HTAB memset (htab, 0, sizeof (htab)); #endif lit = 0; op++; /* start run */ hval = FRST (ip); while (ip < in_end - 2) { LZF_HSLOT *hslot; hval = NEXT (hval, ip); hslot = htab + IDX (hval); ref = *hslot + LZF_HSLOT_BIAS; *hslot = ip - LZF_HSLOT_BIAS; if (1 #if INIT_HTAB && ref < ip /* the next test will actually take care of this, but this is faster */ #endif && (off = ip - ref - 1) < LZF_MAX_OFF && ref > (u8 *)in_data && ref[2] == ip[2] #if STRICT_ALIGN && ((ref[1] << 8) | ref[0]) == ((ip[1] << 8) | ip[0]) #else && *(u16 *)ref == *(u16 *)ip #endif ) { /* match found at *ref++ */ unsigned int len = 2; unsigned int maxlen = in_end - ip - len; maxlen = maxlen > LZF_MAX_REF ? LZF_MAX_REF : maxlen; if (expect_false (op + 3 + 1 >= out_end)) /* first a faster conservative test */ if (op - !lit + 3 + 1 >= out_end) /* second the exact but rare test */ return 0; op [- lit - 1] = lit - 1; /* stop run */ op -= !lit; /* undo run if length is zero */ for (;;) { if (expect_true (maxlen > 16)) { len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; } do len++; while (len < maxlen && ref[len] == ip[len]); break; } len -= 2; /* len is now #octets - 1 */ ip++; if (len < 7) { *op++ = (off >> 8) + (len << 5); } else { *op++ = (off >> 8) + ( 7 << 5); *op++ = len - 7; } *op++ = off; lit = 0; op++; /* start run */ ip += len + 1; if (expect_false (ip >= in_end - 2)) break; #if ULTRA_FAST || VERY_FAST --ip; # if VERY_FAST && !ULTRA_FAST --ip; # endif hval = FRST (ip); hval = NEXT (hval, ip); htab[IDX (hval)] = ip - LZF_HSLOT_BIAS; ip++; # if VERY_FAST && !ULTRA_FAST hval = NEXT (hval, ip); htab[IDX (hval)] = ip - LZF_HSLOT_BIAS; ip++; # endif #else ip -= len + 1; do { hval = NEXT (hval, ip); htab[IDX (hval)] = ip - LZF_HSLOT_BIAS; ip++; } while (len--); #endif } else { /* one more literal byte we must copy */ if (expect_false (op >= out_end)) return 0; lit++; *op++ = *ip++; if (expect_false (lit == LZF_MAX_LIT)) { op [- lit - 1] = lit - 1; /* stop run */ lit = 0; op++; /* start run */ } } } if (op + 3 > out_end) /* at most 3 bytes can be missing here */ return 0; while (ip < in_end) { lit++; *op++ = *ip++; if (expect_false (lit == LZF_MAX_LIT)) { op [- lit - 1] = lit - 1; /* stop run */ lit = 0; op++; /* start run */ } } op [- lit - 1] = lit - 1; /* end run */ op -= !lit; /* undo run if length is zero */ return op - (u8 *)out_data; } redis-6.0.2/liblzf/lzf_d.c0000644000175000000120000001417314515245367016152 0ustar pyatsukhnenkowheel/* * Copyright (c) 2000-2010 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #include "lzfP.h" #if AVOID_ERRNO # define SET_ERRNO(n) #else # include # define SET_ERRNO(n) errno = (n) #endif #if USE_REP_MOVSB /* small win on amd, big loss on intel */ #if (__i386 || __amd64) && __GNUC__ >= 3 # define lzf_movsb(dst, src, len) \ asm ("rep movsb" \ : "=D" (dst), "=S" (src), "=c" (len) \ : "0" (dst), "1" (src), "2" (len)); #endif #endif unsigned int lzf_decompress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len) { u8 const *ip = (const u8 *)in_data; u8 *op = (u8 *)out_data; u8 const *const in_end = ip + in_len; u8 *const out_end = op + out_len; do { unsigned int ctrl = *ip++; if (ctrl < (1 << 5)) /* literal run */ { ctrl++; if (op + ctrl > out_end) { SET_ERRNO (E2BIG); return 0; } #if CHECK_INPUT if (ip + ctrl > in_end) { SET_ERRNO (EINVAL); return 0; } #endif #ifdef lzf_movsb lzf_movsb (op, ip, ctrl); #elif OPTIMISE_SIZE while (ctrl--) *op++ = *ip++; #else switch (ctrl) { case 32: *op++ = *ip++; case 31: *op++ = *ip++; case 30: *op++ = *ip++; case 29: *op++ = *ip++; case 28: *op++ = *ip++; case 27: *op++ = *ip++; case 26: *op++ = *ip++; case 25: *op++ = *ip++; case 24: *op++ = *ip++; case 23: *op++ = *ip++; case 22: *op++ = *ip++; case 21: *op++ = *ip++; case 20: *op++ = *ip++; case 19: *op++ = *ip++; case 18: *op++ = *ip++; case 17: *op++ = *ip++; case 16: *op++ = *ip++; case 15: *op++ = *ip++; case 14: *op++ = *ip++; case 13: *op++ = *ip++; case 12: *op++ = *ip++; case 11: *op++ = *ip++; case 10: *op++ = *ip++; case 9: *op++ = *ip++; case 8: *op++ = *ip++; case 7: *op++ = *ip++; case 6: *op++ = *ip++; case 5: *op++ = *ip++; case 4: *op++ = *ip++; case 3: *op++ = *ip++; case 2: *op++ = *ip++; case 1: *op++ = *ip++; } #endif } else /* back reference */ { unsigned int len = ctrl >> 5; u8 *ref = op - ((ctrl & 0x1f) << 8) - 1; #if CHECK_INPUT if (ip >= in_end) { SET_ERRNO (EINVAL); return 0; } #endif if (len == 7) { len += *ip++; #if CHECK_INPUT if (ip >= in_end) { SET_ERRNO (EINVAL); return 0; } #endif } ref -= *ip++; if (op + len + 2 > out_end) { SET_ERRNO (E2BIG); return 0; } if (ref < (u8 *)out_data) { SET_ERRNO (EINVAL); return 0; } #ifdef lzf_movsb len += 2; lzf_movsb (op, ref, len); #elif OPTIMISE_SIZE len += 2; do *op++ = *ref++; while (--len); #else switch (len) { default: len += 2; if (op >= ref + len) { /* disjunct areas */ memcpy (op, ref, len); op += len; } else { /* overlapping, use octet by octet copying */ do *op++ = *ref++; while (--len); } break; case 9: *op++ = *ref++; case 8: *op++ = *ref++; case 7: *op++ = *ref++; case 6: *op++ = *ref++; case 5: *op++ = *ref++; case 4: *op++ = *ref++; case 3: *op++ = *ref++; case 2: *op++ = *ref++; case 1: *op++ = *ref++; case 0: *op++ = *ref++; /* two octets more */ *op++ = *ref++; } #endif } } while (ip < in_end); return op - (u8 *)out_data; } redis-6.0.2/tests/RedisArrayTest.php0000644000175000000120000004756214515245367020216 0ustar pyatsukhnenkowheel\d+)_\w+#", $str, $out)) { return $out['facebook_id']; } return $str; } function parseHostPort($str, &$host, &$port) { $pos = strrpos($str, ':'); $host = substr($str, 0, $pos); $port = substr($str, $pos+1); } function getRedisVersion($obj_r) { $arr_info = $obj_r->info(); if (!$arr_info || !isset($arr_info['redis_version'])) { return "0.0.0"; } return $arr_info['redis_version']; } /* Determine the lowest redis version attached to this RedisArray object */ function getMinVersion($obj_ra) { $min_version = "0.0.0"; foreach ($obj_ra->_hosts() as $host) { $version = getRedisVersion($obj_ra->_instance($host)); if (version_compare($version, $min_version) > 0) { $min_version = $version; } } return $min_version; } class Redis_Array_Test extends TestSuite { private $min_version; private $strings; public $ra = NULL; private $data = NULL; public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; $this->strings = array(); for($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } global $newRing, $oldRing, $useIndex; $options = ['previous' => $oldRing, 'index' => $useIndex]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } $this->ra = new RedisArray($newRing, $options); $this->min_version = getMinVersion($this->ra); } public function testMSet() { // run mset $this->assertTrue(TRUE === $this->ra->mset($this->strings)); // check each key individually using the array foreach($this->strings as $k => $v) { $this->assertTrue($v === $this->ra->get($k)); } // check each key individually using a new connection foreach($this->strings as $k => $v) { parseHostPort($this->ra->_target($k), $host, $port); $target = $this->ra->_target($k); $pos = strrpos($target, ':'); $host = substr($target, 0, $pos); $port = substr($target, $pos+1); $r = new Redis; $r->pconnect($host, (int)$port); if ($this->getAuth()) { $this->assertTrue($r->auth($this->getAuth())); } $this->assertTrue($v === $r->get($k)); } } public function testMGet() { $this->assertTrue(array_values($this->strings) === $this->ra->mget(array_keys($this->strings))); } private function addData($commonString) { $this->data = array(); for($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) { $k = rand().'_'.$commonString.'_'.rand(); $this->data[$k] = rand(); } $this->ra->mset($this->data); } private function checkCommonLocality() { // check that they're all on the same node. $lastNode = NULL; foreach($this->data as $k => $v) { $node = $this->ra->_target($k); if($lastNode) { $this->assertTrue($node === $lastNode); } $this->assertTrue($this->ra->get($k) == $v); $lastNode = $node; } } public function testKeyLocality() { // basic key locality with default hash $this->addData('{hashed part of the key}'); $this->checkCommonLocality(); // with common hashing function global $newRing, $oldRing, $useIndex; $options = ['previous' => $oldRing, 'index' => $useIndex, 'function' => 'custom_hash']; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } $this->ra = new RedisArray($newRing, $options); // basic key locality with custom hash $this->addData('fb'.rand()); $this->checkCommonLocality(); } public function customDistributor($key) { $a = unpack("N*", md5($key, true)); global $newRing; $pos = abs($a[1]) % count($newRing); return $pos; } public function testKeyDistributor() { global $newRing, $useIndex; $options = ['index' => $useIndex, 'function' => 'custom_hash', 'distributor' => [$this, "customDistributor"]]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } $this->ra = new RedisArray($newRing, $options); // custom key distribution function. $this->addData('fb'.rand()); // check that they're all on the expected node. $lastNode = NULL; foreach($this->data as $k => $v) { $node = $this->ra->_target($k); $pos = $this->customDistributor($k); $this->assertTrue($node === $newRing[$pos]); } } /* Scan a whole key and return the overall result */ protected function execKeyScan($cmd, $key) { $res = []; $it = NULL; do { $chunk = $this->ra->$cmd($key, $it); foreach ($chunk as $field => $value) { $res[$field] = $value; } } while ($it !== 0); return $res; } public function testKeyScanning() { $h_vals = ['foo' => 'bar', 'baz' => 'bop']; $z_vals = ['one' => 1, 'two' => 2, 'three' => 3]; $s_vals = ['mem1', 'mem2', 'mem3']; $this->ra->del(['scan-hash', 'scan-set', 'scan-zset']); $this->ra->hMSet('scan-hash', $h_vals); foreach ($z_vals as $k => $v) $this->ra->zAdd('scan-zset', $v, $k); $this->ra->sAdd('scan-set', ...$s_vals); $s_scan = $this->execKeyScan('sScan', 'scan-set'); $this->assertTrue(count(array_diff_key(array_flip($s_vals), array_flip($s_scan))) == 0); $this->assertEquals($h_vals, $this->execKeyScan('hScan', 'scan-hash')); $z_scan = $this->execKeyScan('zScan', 'scan-zset'); $this->assertTrue(count($z_scan) == count($z_vals) && count(array_diff_key($z_vals, $z_scan)) == 0 && array_sum($z_scan) == array_sum($z_vals)); } } class Redis_Rehashing_Test extends TestSuite { public $ra = NULL; private $useIndex; private $min_version; // data private $strings; private $sets; private $lists; private $hashes; private $zsets; public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; $this->strings = array(); for($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } // initialize sets for($i = 0; $i < $n; $i++) { // each set has 20 elements $this->sets['set-'.$i] = range($i, $i+20); } // initialize lists for($i = 0; $i < $n; $i++) { // each list has 20 elements $this->lists['list-'.$i] = range($i, $i+20); } // initialize hashes for($i = 0; $i < $n; $i++) { // each hash has 5 keys $this->hashes['hash-'.$i] = array('A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4); } // initialize sorted sets for($i = 0; $i < $n; $i++) { // each sorted sets has 5 elements $this->zsets['zset-'.$i] = array($i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E'); } global $newRing, $oldRing, $useIndex; $options = ['previous' => $oldRing, 'index' => $useIndex]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } // create array $this->ra = new RedisArray($newRing, $options); $this->min_version = getMinVersion($this->ra); } public function testFlush() { // flush all servers first. global $serverList; foreach($serverList as $s) { parseHostPort($s, $host, $port); $r = new Redis(); $r->pconnect($host, (int)$port, 0); if ($this->getAuth()) { $this->assertTrue($r->auth($this->getAuth())); } $r->flushdb(); } } private function distributeKeys() { // strings foreach($this->strings as $k => $v) { $this->ra->set($k, $v); } // sets foreach($this->sets as $k => $v) { call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v)); } // lists foreach($this->lists as $k => $v) { call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v)); } // hashes foreach($this->hashes as $k => $v) { $this->ra->hmset($k, $v); } // sorted sets foreach($this->zsets as $k => $v) { call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v)); } } public function testDistribution() { $this->distributeKeys(); } public function testSimpleRead() { $this->readAllvalues(); } private function readAllvalues() { // strings foreach($this->strings as $k => $v) { $this->assertTrue($this->ra->get($k) === $v); } // sets foreach($this->sets as $k => $v) { $ret = $this->ra->smembers($k); // get values // sort sets sort($v); sort($ret); $this->assertTrue($ret == $v); } // lists foreach($this->lists as $k => $v) { $ret = $this->ra->lrange($k, 0, -1); $this->assertTrue($ret == $v); } // hashes foreach($this->hashes as $k => $v) { $ret = $this->ra->hgetall($k); // get values $this->assertTrue($ret == $v); } // sorted sets foreach($this->zsets as $k => $v) { $ret = $this->ra->zrange($k, 0, -1, TRUE); // get values with scores // create assoc array from local dataset $tmp = array(); for($i = 0; $i < count($v); $i += 2) { $tmp[$v[$i+1]] = $v[$i]; } // compare to RA value $this->assertTrue($ret == $tmp); } } // add a new node. public function testCreateSecondRing() { global $newRing, $oldRing, $serverList; $oldRing = $newRing; // back up the original. $newRing = $serverList; // add a new node to the main ring. } public function testReadUsingFallbackMechanism() { $this->readAllvalues(); // some of the reads will fail and will go to another target node. } public function testRehash() { $this->ra->_rehash(); // this will redistribute the keys } public function testRehashWithCallback() { $total = 0; $this->ra->_rehash(function ($host, $count) use (&$total) { $total += $count; }); $this->assertTrue($total > 0); } public function testReadRedistributedKeys() { $this->readAllvalues(); // we shouldn't have any missed reads now. } } // Test auto-migration of keys class Redis_Auto_Rehashing_Test extends TestSuite { public $ra = NULL; private $min_version; // data private $strings; public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; $this->strings = array(); for($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } global $newRing, $oldRing, $useIndex; $options = ['previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } // create array $this->ra = new RedisArray($newRing, $options); $this->min_version = getMinVersion($this->ra); } public function testDistribute() { // strings foreach($this->strings as $k => $v) { $this->ra->set($k, $v); } } private function readAllvalues() { foreach($this->strings as $k => $v) { $this->assertTrue($this->ra->get($k) === $v); } } public function testReadAll() { $this->readAllvalues(); } // add a new node. public function testCreateSecondRing() { global $newRing, $oldRing, $serverList; $oldRing = $newRing; // back up the original. $newRing = $serverList; // add a new node to the main ring. } // Read and migrate keys on fallback, causing the whole ring to be rehashed. public function testReadAndMigrateAll() { $this->readAllvalues(); } // Read and migrate keys on fallback, causing the whole ring to be rehashed. public function testAllKeysHaveBeenMigrated() { foreach($this->strings as $k => $v) { parseHostPort($this->ra->_target($k), $host, $port); $r = new Redis; $r->pconnect($host, $port); if ($this->getAuth()) { $this->assertTrue($r->auth($this->getAuth())); } $this->assertTrue($v === $r->get($k)); // check that the key has actually been migrated to the new node. } } } // Test node-specific multi/exec class Redis_Multi_Exec_Test extends TestSuite { public $ra = NULL; private $min_version; public function setUp() { global $newRing, $oldRing, $useIndex; $options = ['previous' => $oldRing, 'index' => $useIndex]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } // create array $this->ra = new RedisArray($newRing, $options); $this->min_version = getMinVersion($this->ra); } public function testInit() { $this->ra->set('{groups}:managers', 2); $this->ra->set('{groups}:executives', 3); $this->ra->set('1_{employee:joe}_name', 'joe'); $this->ra->set('1_{employee:joe}_group', 2); $this->ra->set('1_{employee:joe}_salary', 2000); } public function testKeyDistribution() { // check that all of joe's keys are on the same instance $lastNode = NULL; foreach(array('name', 'group', 'salary') as $field) { $node = $this->ra->_target('1_{employee:joe}_'.$field); if($lastNode) { $this->assertTrue($node === $lastNode); } $lastNode = $node; } } public function testMultiExec() { // Joe gets a promotion $newGroup = $this->ra->get('{groups}:executives'); $newSalary = 4000; // change both in a transaction. $host = $this->ra->_target('{employee:joe}'); // transactions are per-node, so we need a reference to it. $tr = $this->ra->multi($host) ->set('1_{employee:joe}_group', $newGroup) ->set('1_{employee:joe}_salary', $newSalary) ->exec(); // check that the group and salary have been changed $this->assertTrue($this->ra->get('1_{employee:joe}_group') === $newGroup); $this->assertTrue($this->ra->get('1_{employee:joe}_salary') == $newSalary); } public function testMultiExecMSet() { global $newGroup, $newSalary; $newGroup = 1; $newSalary = 10000; // test MSET, making Joe a top-level executive $out = $this->ra->multi($this->ra->_target('{employee:joe}')) ->mset(array('1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary)) ->exec(); $this->assertTrue($out[0] === TRUE); } public function testMultiExecMGet() { global $newGroup, $newSalary; // test MGET $out = $this->ra->multi($this->ra->_target('{employee:joe}')) ->mget(array('1_{employee:joe}_group', '1_{employee:joe}_salary')) ->exec(); $this->assertTrue($out[0][0] == $newGroup); $this->assertTrue($out[0][1] == $newSalary); } public function testMultiExecDel() { // test DEL $out = $this->ra->multi($this->ra->_target('{employee:joe}')) ->del('1_{employee:joe}_group', '1_{employee:joe}_salary') ->exec(); $this->assertTrue($out[0] === 2); $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_group')); $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary')); } public function testMutliExecUnlink() { if (version_compare($this->min_version, "4.0.0", "lt")) { $this->markTestSkipped(); } $this->ra->set('{unlink}:key1', 'bar'); $this->ra->set('{unlink}:key2', 'bar'); $out = $this->ra->multi($this->ra->_target('{unlink}')) ->del('{unlink}:key1', '{unlink}:key2') ->exec(); $this->assertTrue($out[0] === 2); } public function testDiscard() { /* phpredis issue #87 */ $key = 'test_err'; $this->assertTrue($this->ra->set($key, 'test')); $this->assertTrue('test' === $this->ra->get($key)); $this->ra->watch($key); // After watch, same $this->assertTrue('test' === $this->ra->get($key)); // change in a multi/exec block. $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test1')->exec(); $this->assertTrue($ret === array(true)); // Get after exec, 'test1': $this->assertTrue($this->ra->get($key) === 'test1'); $this->ra->watch($key); // After second watch, still test1. $this->assertTrue($this->ra->get($key) === 'test1'); $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test2')->discard(); // Ret after discard: NULL"; $this->assertTrue($ret === NULL); // Get after discard, unchanged: $this->assertTrue($this->ra->get($key) === 'test1'); } } // Test custom distribution function class Redis_Distributor_Test extends TestSuite { public $ra = NULL; private $min_version; public function setUp() { global $newRing, $oldRing, $useIndex; $options = ['previous' => $oldRing, 'index' => $useIndex, 'distributor' => [$this, 'distribute']]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } // create array $this->ra = new RedisArray($newRing, $options); $this->min_version = getMinVersion($this->ra); } public function testInit() { $this->ra->set('{uk}test', 'joe'); $this->ra->set('{us}test', 'bob'); } public function distribute($key) { $matches = array(); if (preg_match('/{([^}]+)}.*/', $key, $matches) == 1) { $countries = array('uk' => 0, 'us' => 1); if (array_key_exists($matches[1], $countries)) { return $countries[$matches[1]]; } } return 2; // default server } public function testDistribution() { $ukServer = $this->ra->_target('{uk}test'); $usServer = $this->ra->_target('{us}test'); $deServer = $this->ra->_target('{de}test'); $defaultServer = $this->ra->_target('unknown'); $nodes = $this->ra->_hosts(); $this->assertTrue($ukServer === $nodes[0]); $this->assertTrue($usServer === $nodes[1]); $this->assertTrue($deServer === $nodes[2]); $this->assertTrue($defaultServer === $nodes[2]); } } function run_tests($className, $str_filter, $str_host, $auth) { // reset rings global $newRing, $oldRing, $serverList; $newRing = ["$str_host:6379", "$str_host:6380", "$str_host:6381"]; $oldRing = []; $serverList = ["$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"]; // run return TestSuite::run($className, $str_filter, $str_host, NULL, $auth); } ?> redis-6.0.2/tests/RedisClusterTest.php0000644000175000000120000007207014515245367020551 0ustar pyatsukhnenkowheelmarkTestSkipped(); } public function testSortDesc() { return $this->markTestSkipped(); } public function testWait() { return $this->markTestSkipped(); } public function testSelect() { return $this->markTestSkipped(); } public function testReconnectSelect() { return $this->markTestSkipped(); } public function testMultipleConnect() { return $this->markTestSkipped(); } public function testDoublePipeNoOp() { return $this->markTestSkipped(); } public function testSwapDB() { return $this->markTestSkipped(); } public function testConnectException() { return $this->markTestSkipped(); } public function testTlsConnect() { return $this->markTestSkipped(); } public function testReset() { return $this->markTestSkipped(); } public function testInvalidAuthArgs() { return $this->markTestSkipped(); } public function testScanErrors() { return $this->markTestSkipped(); } /* These 'directed node' commands work differently in RedisCluster */ public function testConfig() { return $this->markTestSkipped(); } public function testFlushDB() { return $this->markTestSkipped(); } public function testFunction() { return $this->markTestSkipped(); } /* Session locking feature is currently not supported in in context of Redis Cluster. The biggest issue for this is the distribution nature of Redis cluster */ public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); } public function testSession_lockingDisabledByDefault() { return $this->markTestSkipped(); } public function testSession_lockReleasedOnClose() { return $this->markTestSkipped(); } public function testSession_ttlMaxExecutionTime() { return $this->markTestSkipped(); } public function testSession_ttlLockExpire() { return $this->markTestSkipped(); } public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { return $this->markTestSkipped(); } public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { return $this->markTestSkipped(); } public function testSession_correctLockRetryCount() { return $this->markTestSkipped(); } public function testSession_defaultLockRetryCount() { return $this->markTestSkipped(); } public function testSession_noUnlockOfOtherProcess() { return $this->markTestSkipped(); } public function testSession_lockWaitTime() { return $this->markTestSkipped(); } /* Load our seeds on construction */ public function __construct($str_host, $i_port, $str_auth) { parent::__construct($str_host, $i_port, $str_auth); $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; if (!file_exists($str_nodemap_file)) { fprintf(STDERR, "Error: Can't find nodemap file for seeds!\n"); exit(1); } /* Store our node map */ if (!self::$_arr_node_map) { self::$_arr_node_map = array_filter( explode("\n", file_get_contents($str_nodemap_file) )); } } /* Override setUp to get info from a specific node */ public function setUp() { $this->redis = $this->newInstance(); $info = $this->redis->info(uniqid()); $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); } /* Override newInstance as we want a RedisCluster object */ protected function newInstance() { return new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth()); } /* Overrides for RedisTest where the function signature is different. This * is only true for a few commands, which by definition have to be directed * at a specific node */ public function testPing() { for ($i = 0; $i < 20; $i++) { $this->assertTrue($this->redis->ping("key:$i")); $this->assertEquals('BEEP', $this->redis->ping("key:$i", 'BEEP')); } /* Make sure both variations work in MULTI mode */ $this->redis->multi(); $this->redis->ping('{ping-test}'); $this->redis->ping('{ping-test}','BEEP'); $this->assertEquals([true, 'BEEP'], $this->redis->exec()); } public function testRandomKey() { /* Ensure some keys are present to test */ for ($i = 0; $i < 1000; $i++) { if (rand(1, 2) == 1) { $this->redis->set("key:$i", "val:$i"); } } for ($i = 0; $i < 1000; $i++) { $k = $this->redis->randomKey("key:$i"); $this->assertTrue($this->redis->exists($k)); } } public function testEcho() { $this->assertEquals($this->redis->echo('k1', 'hello'), 'hello'); $this->assertEquals($this->redis->echo('k2', 'world'), 'world'); $this->assertEquals($this->redis->echo('k3', " 0123 "), " 0123 "); } public function testSortPrefix() { $this->redis->setOption(Redis::OPT_PREFIX, 'some-prefix:'); $this->redis->del('some-item'); $this->redis->sadd('some-item', 1); $this->redis->sadd('some-item', 2); $this->redis->sadd('some-item', 3); $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item')); // Kill our set/prefix $this->redis->del('some-item'); $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testDBSize() { for ($i = 0; $i < 10; $i++) { $str_key = "key:$i"; $this->assertTrue($this->redis->flushdb($str_key)); $this->redis->set($str_key, "val:$i"); $this->assertEquals(1, $this->redis->dbsize($str_key)); } } public function testInfo() { $arr_check_keys = [ "redis_version", "arch_bits", "uptime_in_seconds", "uptime_in_days", "connected_clients", "connected_slaves", "used_memory", "total_connections_received", "total_commands_processed", "role" ]; for ($i = 0; $i < 3; $i++) { $arr_info = $this->redis->info("k:$i"); foreach ($arr_check_keys as $str_check_key) { $this->assertTrue(isset($arr_info[$str_check_key])); } } } public function testClient() { $str_key = 'key-' . rand(1,100); $this->assertTrue($this->redis->client($str_key, 'setname', 'cluster_tests')); $arr_clients = $this->redis->client($str_key, 'list'); $this->assertTrue(is_array($arr_clients)); /* Find us in the list */ $str_addr = NULL; foreach ($arr_clients as $arr_client) { if ($arr_client['name'] == 'cluster_tests') { $str_addr = $arr_client['addr']; break; } } /* We should be in there */ $this->assertFalse(empty($str_addr)); /* Kill our own client! */ $this->assertTrue($this->redis->client($str_key, 'kill', $str_addr)); } public function testTime() { $time_arr = $this->redis->time("k:" . rand(1,100)); $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && strval(intval($time_arr[0])) === strval($time_arr[0]) && strval(intval($time_arr[1])) === strval($time_arr[1])); } public function testScan() { $i_key_count = 0; $i_scan_count = 0; /* Have scan retry for us */ $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* Iterate over our masters, scanning each one */ foreach ($this->redis->_masters() as $arr_master) { /* Grab the number of keys we have */ $i_key_count += $this->redis->dbsize($arr_master); /* Scan the keys here */ $it = NULL; while ($arr_keys = $this->redis->scan($it, $arr_master)) { $i_scan_count += count($arr_keys); } } /* Our total key count should match */ $this->assertEquals($i_scan_count, $i_key_count); } public function testScanPrefix() { $arr_prefixes = ['prefix-a:', 'prefix-b:']; $str_id = uniqid(); $arr_keys = []; foreach ($arr_prefixes as $str_prefix) { $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix); $this->redis->set($str_id, "LOLWUT"); $arr_keys[$str_prefix] = $str_id; } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX); foreach ($arr_prefixes as $str_prefix) { $arr_prefix_keys = []; $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix); foreach ($this->redis->_masters() as $arr_master) { $it = NULL; while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) { foreach ($arr_iter as $str_key) { $arr_prefix_keys[$str_prefix] = $str_key; } } } $this->assertTrue(count($arr_prefix_keys) == 1 && isset($arr_prefix_keys[$str_prefix])); } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX); $arr_scan_keys = []; foreach ($this->redis->_masters() as $arr_master) { $it = NULL; while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) { foreach ($arr_iter as $str_key) { $arr_scan_keys[] = $str_key; } } } /* We should now have both prefixs' keys */ foreach ($arr_keys as $str_prefix => $str_id) { $this->assertTrue(in_array("{$str_prefix}{$str_id}", $arr_scan_keys)); } } // Run some simple tests against the PUBSUB command. This is problematic, as we // can't be sure what's going on in the instance, but we can do some things. public function testPubSub() { // PUBSUB CHANNELS ... $result = $this->redis->pubsub("somekey", "channels", "*"); $this->assertTrue(is_array($result)); $result = $this->redis->pubsub("somekey", "channels"); $this->assertTrue(is_array($result)); // PUBSUB NUMSUB $c1 = '{pubsub}-' . rand(1,100); $c2 = '{pubsub}-' . rand(1,100); $result = $this->redis->pubsub("{pubsub}", "numsub", $c1, $c2); // Should get an array back, with two elements $this->assertTrue(is_array($result)); $this->assertEquals(count($result), 4); $arr_zipped = []; for ($i = 0; $i <= count($result) / 2; $i+=2) { $arr_zipped[$result[$i]] = $result[$i+1]; } $result = $arr_zipped; // Make sure the elements are correct, and have zero counts foreach([$c1,$c2] as $channel) { $this->assertTrue(isset($result[$channel])); $this->assertEquals($result[$channel], 0); } // PUBSUB NUMPAT $result = $this->redis->pubsub("somekey", "numpat"); $this->assertTrue(is_int($result)); // Invalid call $this->assertFalse($this->redis->pubsub("somekey", "notacommand")); } /* Unlike Redis proper, MsetNX won't always totally fail if all keys can't * be set, but rather will only fail per-node when that is the case */ public function testMSetNX() { /* All of these keys should get set */ $this->redis->del('x','y','z'); $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']); $this->assertTrue(is_array($ret)); $this->assertEquals(array_sum($ret),count($ret)); /* Delete one key */ $this->redis->del('x'); $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']); $this->assertTrue(is_array($ret)); $this->assertEquals(array_sum($ret),1); $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE } /* Slowlog needs to take a key or [ip, port], to direct it to a node */ public function testSlowlog() { $str_key = uniqid() . '-' . rand(1, 1000); $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get'))); $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get', 10))); $this->assertTrue(is_int($this->redis->slowlog($str_key, 'len'))); $this->assertTrue($this->redis->slowlog($str_key, 'reset')); $this->assertFalse($this->redis->slowlog($str_key, 'notvalid')); } /* INFO COMMANDSTATS requires a key or ip:port for node direction */ public function testInfoCommandStats() { $str_key = uniqid() . '-' . rand(1,1000); $arr_info = $this->redis->info($str_key, "COMMANDSTATS"); $this->assertTrue(is_array($arr_info)); if (is_array($arr_info)) { foreach($arr_info as $k => $str_value) { $this->assertTrue(strpos($k, 'cmdstat_') !== false); } } } /* RedisCluster will always respond with an array, even if transactions * failed, because the commands could be coming from multiple nodes */ public function testFailedTransactions() { $this->redis->set('x', 42); // failed transaction $this->redis->watch('x'); $r = $this->newInstance(); // new instance, modifying `x'. $r->incr('x'); // This transaction should fail because the other client changed 'x' $ret = $this->redis->multi()->get('x')->exec(); $this->assertTrue($ret === [false]); // watch and unwatch $this->redis->watch('x'); $r->incr('x'); // other instance $this->redis->unwatch(); // cancel transaction watch // This should succeed as the watch has been cancelled $ret = $this->redis->multi()->get('x')->exec(); $this->assertTrue($ret === array('44')); } public function testDiscard() { /* start transaction */ $this->redis->multi(); /* Set and get in our transaction */ $this->redis->set('pipecount','over9000')->get('pipecount'); $this->assertTrue($this->redis->discard()); } /* RedisCluster::script() is a 'raw' command, which requires a key such that * we can direct it to a given node */ public function testScript() { $str_key = uniqid() . '-' . rand(1,1000); // Flush any scripts we have $this->assertTrue($this->redis->script($str_key, 'flush')); // Silly scripts to test against $s1_src = 'return 1'; $s1_sha = sha1($s1_src); $s2_src = 'return 2'; $s2_sha = sha1($s2_src); $s3_src = 'return 3'; $s3_sha = sha1($s3_src); // None should exist $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha); $this->assertTrue(is_array($result) && count($result) == 3); $this->assertTrue(is_array($result) && count(array_filter($result)) == 0); // Load them up $this->assertTrue($this->redis->script($str_key, 'load', $s1_src) == $s1_sha); $this->assertTrue($this->redis->script($str_key, 'load', $s2_src) == $s2_sha); $this->assertTrue($this->redis->script($str_key, 'load', $s3_src) == $s3_sha); // They should all exist $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha); $this->assertTrue(is_array($result) && count(array_filter($result)) == 3); } /* RedisCluster::EVALSHA needs a 'key' to let us know which node we want to * direct the command at */ public function testEvalSHA() { $str_key = uniqid() . '-' . rand(1,1000); // Flush any loaded scripts $this->redis->script($str_key, 'flush'); // Non existant script (but proper sha1), and a random (not) sha1 string $this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$str_key], 1)); $this->assertFalse($this->redis->evalsha('some-random-data'),[$str_key], 1); // Load a script $cb = uniqid(); // To ensure the script is new $scr = "local cb='$cb' return 1"; $sha = sha1($scr); // Run it when it doesn't exist, run it with eval, and then run it with sha1 $this->assertTrue(false === $this->redis->evalsha($scr,[$str_key], 1)); $this->assertTrue(1 === $this->redis->eval($scr,[$str_key], 1)); $this->assertTrue(1 === $this->redis->evalsha($sha,[$str_key], 1)); } public function testEvalBulkResponse() { $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; $this->redis->script($str_key1, 'flush'); $this->redis->script($str_key2, 'flush'); $scr = "return {KEYS[1],KEYS[2]}"; $result = $this->redis->eval($scr,[$str_key1, $str_key2], 2); $this->assertTrue($str_key1 === $result[0]); $this->assertTrue($str_key2 === $result[1]); } public function testEvalBulkResponseMulti() { $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; $this->redis->script($str_key1, 'flush'); $this->redis->script($str_key2, 'flush'); $scr = "return {KEYS[1],KEYS[2]}"; $this->redis->multi(); $this->redis->eval($scr, [$str_key1, $str_key2], 2); $result = $this->redis->exec(); $this->assertTrue($str_key1 === $result[0][0]); $this->assertTrue($str_key2 === $result[0][1]); } public function testEvalBulkEmptyResponse() { $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; $this->redis->script($str_key1, 'flush'); $this->redis->script($str_key2, 'flush'); $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; $result = $this->redis->eval($scr, [$str_key1, $str_key2], 2); $this->assertTrue(null === $result); } public function testEvalBulkEmptyResponseMulti() { $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; $this->redis->script($str_key1, 'flush'); $this->redis->script($str_key2, 'flush'); $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; $this->redis->multi(); $this->redis->eval($scr, [$str_key1, $str_key2], 2); $result = $this->redis->exec(); $this->assertTrue(null === $result[0]); } /* Cluster specific introspection stuff */ public function testIntrospection() { $arr_masters = $this->redis->_masters(); $this->assertTrue(is_array($arr_masters)); foreach ($arr_masters as $arr_info) { $this->assertTrue(is_array($arr_info)); $this->assertTrue(is_string($arr_info[0])); $this->assertTrue(is_long($arr_info[1])); } } protected function genKeyName($i_key_idx, $i_type) { switch ($i_type) { case Redis::REDIS_STRING: return "string-$i_key_idx"; case Redis::REDIS_SET: return "set-$i_key_idx"; case Redis::REDIS_LIST: return "list-$i_key_idx"; case Redis::REDIS_ZSET: return "zset-$i_key_idx"; case Redis::REDIS_HASH: return "hash-$i_key_idx"; default: return "unknown-$i_key_idx"; } } protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) { $str_key = $this->genKeyName($i_key_idx, $i_type); $this->redis->del($str_key); switch ($i_type) { case Redis::REDIS_STRING: $value = "$str_key-value"; $this->redis->set($str_key, $value); break; case Redis::REDIS_SET: $value = [ $str_key . '-mem1', $str_key . '-mem2', $str_key . '-mem3', $str_key . '-mem4', $str_key . '-mem5', $str_key . '-mem6' ]; $arr_args = $value; array_unshift($arr_args, $str_key); call_user_func_array([$this->redis, 'sadd'], $arr_args); break; case Redis::REDIS_HASH: $value = [ $str_key . '-mem1' => $str_key . '-val1', $str_key . '-mem2' => $str_key . '-val2', $str_key . '-mem3' => $str_key . '-val3' ]; $this->redis->hmset($str_key, $value); break; case Redis::REDIS_LIST: $value = [ $str_key . '-ele1', $str_key . '-ele2', $str_key . '-ele3', $str_key . '-ele4', $str_key . '-ele5', $str_key . '-ele6' ]; $arr_args = $value; array_unshift($arr_args, $str_key); call_user_func_array([$this->redis, 'rpush'], $arr_args); break; case Redis::REDIS_ZSET: $i_score = 1; $value = [ $str_key . '-mem1' => 1, $str_key . '-mem2' => 2, $str_key . '-mem3' => 3, $str_key . '-mem3' => 3 ]; foreach ($value as $str_mem => $i_score) { $this->redis->zadd($str_key, $i_score, $str_mem); } break; } /* Update our reference array so we can verify values */ $arr_ref[$str_key] = $value; return $str_key; } /* Verify that our ZSET values are identical */ protected function checkZSetEquality($a, $b) { /* If the count is off, the array keys are different or the sums are * different, we know there is something off */ $boo_diff = count($a) != count($b) || count(array_diff(array_keys($a), array_keys($b))) != 0 || array_sum($a) != array_sum($b); if ($boo_diff) { $this->assertEquals($a,$b); return; } } protected function checkKeyValue($str_key, $i_type, $value) { switch ($i_type) { case Redis::REDIS_STRING: $this->assertEquals($value, $this->redis->get($str_key)); break; case Redis::REDIS_SET: $arr_r_values = $this->redis->sMembers($str_key); $arr_l_values = $value; sort($arr_r_values); sort($arr_l_values); $this->assertEquals($arr_r_values, $arr_l_values); break; case Redis::REDIS_LIST: $this->assertEquals($value, $this->redis->lrange($str_key,0,-1)); break; case Redis::REDIS_HASH: $this->assertEquals($value, $this->redis->hgetall($str_key)); break; case Redis::REDIS_ZSET: $this->checkZSetEquality($value, $this->redis->zrange($str_key,0,-1,true)); break; default: throw new Exception("Unknown type " . $i_type); } } /* Test automatic load distributor */ public function testFailOver() { $arr_value_ref = []; $arr_type_ref = []; /* Set a bunch of keys of various redis types*/ for ($i = 0; $i < 200; $i++) { foreach ($this->_arr_redis_types as $i_type) { $str_key = $this->setKeyVals($i, $i_type, $arr_value_ref); $arr_type_ref[$str_key] = $i_type; } } /* Iterate over failover options */ foreach ($this->_arr_failover_types as $i_opt) { $this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $i_opt); foreach ($arr_value_ref as $str_key => $value) { $this->checkKeyValue($str_key, $arr_type_ref[$str_key], $value); } break; } } /* Test a 'raw' command */ public function testRawCommand() { $this->redis->rawCommand('mykey', 'set', 'mykey', 'my-value'); $this->assertEquals($this->redis->get('mykey'), 'my-value'); $this->redis->del('mylist'); $this->redis->rpush('mylist', 'A','B','C','D'); $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']); } protected function rawCommandArray($key, $args) { array_unshift($args, $key); return call_user_func_array([$this->redis, 'rawCommand'], $args); } /* Test that rawCommand and EVAL can be configured to return simple string values */ public function testReplyLiteral() { $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false); $this->assertTrue($this->redis->rawCommand('foo', 'set', 'foo', 'bar')); $this->assertTrue($this->redis->eval("return redis.call('set', KEYS[1], 'bar')", ['foo'], 1)); $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1); $this->assertEquals([true, true], $rv); $this->redis->setOption(Redis::OPT_REPLY_LITERAL, true); $this->assertEquals('OK', $this->redis->rawCommand('foo', 'set', 'foo', 'bar')); $this->assertEquals('OK', $this->redis->eval("return redis.call('set', KEYS[1], 'bar')", ['foo'], 1)); $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1); $this->assertEquals(['OK', 'PONG'], $rv); // Reset $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false); } /* Redis and RedisCluster use the same handler for the ACL command but verify we can direct the command to a specific node. */ public function testAcl() { if ( ! $this->minVersionCheck("6.0")) return $this->markTestSkipped(); $this->assertInArray('default', $this->redis->acl('foo', 'USERS')); } public function testSession() { @ini_set('session.save_handler', 'rediscluster'); @ini_set('session.save_path', $this->getFullHostPath() . '&failover=error'); if (!@session_start()) { return $this->markTestSkipped(); } session_write_close(); $this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id())); } /* Test that we are able to use the slot cache without issues */ public function testSlotCache() { ini_set('redis.clusters.cache_slots', 1); $pong = 0; for ($i = 0; $i < 10; $i++) { $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth()); $pong += $obj_rc->ping("key:$i"); } $this->assertEquals($pong, $i); ini_set('redis.clusters.cache_slots', 0); } /* Regression test for connection pool liveness checks */ public function testConnectionPool() { $prev_value = ini_get('redis.pconnect.pooling_enabled'); ini_set('redis.pconnect.pooling_enabled', 1); $pong = 0; for ($i = 0; $i < 10; $i++) { $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth()); $pong += $obj_rc->ping("key:$i"); } $this->assertEquals($pong, $i); ini_set('redis.pconnect.pooling_enabled', $prev_value); } /** * @inheritdoc */ protected function getFullHostPath() { $auth = $this->getAuthFragment(); return implode('&', array_map(function ($host) { return 'seed[]=' . $host; }, self::$_arr_node_map)) . ($auth ? "&$auth" : ''); } /* Test correct handling of null multibulk replies */ public function testNullArray() { $key = "key:arr"; $this->redis->del($key); foreach ([false => [], true => NULL] as $opt => $test) { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt); $r = $this->redis->rawCommand($key, "BLPOP", $key, .05); $this->assertEquals($test, $r); $this->redis->multi(); $this->redis->rawCommand($key, "BLPOP", $key, .05); $r = $this->redis->exec(); $this->assertEquals([$test], $r); } $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } } ?> redis-6.0.2/tests/RedisSentinelTest.php0000644000175000000120000000533514515245367020711 0ustar pyatsukhnenkowheel $this->getHost()]); } public function setUp() { $this->sentinel = $this->newInstance(); } public function testCkquorum() { $this->assertTrue($this->sentinel->ckquorum(self::NAME)); } public function testFailover() { $this->assertFalse($this->sentinel->failover(self::NAME)); } public function testFlushconfig() { $this->assertTrue($this->sentinel->flushconfig()); } public function testGetMasterAddrByName() { $result = $this->sentinel->getMasterAddrByName(self::NAME); $this->assertTrue(is_array($result)); $this->assertEquals(2, count($result)); } protected function checkFields(array $fields) { foreach ($this->fields as $k) { $this->assertTrue(array_key_exists($k, $fields)); } } public function testMaster() { $result = $this->sentinel->master(self::NAME); $this->assertTrue(is_array($result)); $this->checkFields($result); } public function testMasters() { $result = $this->sentinel->masters(); $this->assertTrue(is_array($result)); foreach ($result as $master) { $this->checkFields($master); } } public function testMyid() { $result = $this->sentinel->myid(); $this->assertTrue(is_string($result)); } public function testPing() { $this->assertTrue($this->sentinel->ping()); } public function testReset() { $this->assertEquals(1, $this->sentinel->reset('*')); } public function testSentinels() { $result = $this->sentinel->sentinels(self::NAME); $this->assertTrue(is_array($result)); foreach ($result as $sentinel) { $this->checkFields($sentinel); } } public function testSlaves() { $result = $this->sentinel->slaves(self::NAME); $this->assertTrue(is_array($result)); foreach ($result as $slave) { $this->checkFields($slave); } } } redis-6.0.2/tests/RedisTest.php0000644000175000000120000115643414515245367017217 0ustar pyatsukhnenkowheel [-121.837478, 39.728494], 'Sacramento' => [-121.494400, 38.581572], 'Gridley' => [-121.693583, 39.363777], 'Marysville' => [-121.591355, 39.145725], 'Cupertino' => [-122.032182, 37.322998] ]; protected $serializers = [ Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, ]; /** * @var Redis */ public $redis; /** * @var string */ protected $sessionPrefix = 'PHPREDIS_SESSION:'; /** * @var string */ protected $sessionSaveHandler = 'redis'; public function setUp() { $this->redis = $this->newInstance(); $info = $this->redis->info(); $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); if (defined('Redis::SERIALIZER_IGBINARY')) { $this->serializers[] = Redis::SERIALIZER_IGBINARY; } } protected function minVersionCheck($version) { return version_compare($this->version, $version) >= 0; } protected function mstime() { return round(microtime(true)*1000); } protected function getAuthParts(&$user, &$pass) { $user = $pass = NULL; $auth = $this->getAuth(); if ( ! $auth) return; if (is_array($auth)) { if (count($auth) > 1) { list($user, $pass) = $auth; } else { $pass = $auth[0]; } } else { $pass = $auth; } } protected function getAuthFragment() { static $_authidx = 0; $_authidx++; $this->getAuthParts($user, $pass); if ($user && $pass) { if ($_authidx % 2 == 0) return "auth[user]=$user&auth[pass]=$pass"; else return "auth[]=$user&auth[]=$pass"; } else if ($pass) { if ($_authidx % 3 == 0) return "auth[pass]=$pass"; if ($_authidx % 2 == 0) return "auth[]=$pass"; else return "auth=$pass"; } else { return NULL; } } protected function getFullHostPath() { $fullHostPath = parent::getFullHostPath(); $authFragment = $this->getAuthFragment(); if (isset($fullHostPath) && $authFragment) { $fullHostPath .= "?$authFragment"; } return $fullHostPath; } protected function newInstance() { $r = new Redis([ 'host' => $this->getHost(), 'port' => $this->getPort(), ]); if($this->getAuth()) { $this->assertTrue($r->auth($this->getAuth())); } return $r; } public function tearDown() { if($this->redis) { $this->redis->close(); } } public function reset() { $this->setUp(); $this->tearDown(); } /* Helper function to determine if the clsas has pipeline support */ protected function havePipeline() { $str_constant = get_class($this->redis) . '::PIPELINE'; return defined($str_constant); } public function testMinimumVersion() { // Minimum server version required for tests $this->assertTrue(version_compare($this->version, "2.4.0") >= 0); } public function testPing() { /* Reply literal off */ $this->assertTrue($this->redis->ping()); $this->assertTrue($this->redis->ping(NULL)); $this->assertEquals('BEEP', $this->redis->ping('BEEP')); /* Make sure we're good in MULTI mode */ $this->redis->multi(); $this->redis->ping(); $this->redis->ping('BEEP'); $this->assertEquals([true, 'BEEP'], $this->redis->exec()); } public function testPipelinePublish() { if (!$this->havePipeline()) { $this->markTestSkipped(); } $ret = $this->redis->pipeline() ->publish('chan', 'msg') ->exec(); $this->assertTrue(is_array($ret) && count($ret) === 1 && $ret[0] >= 0); } // Run some simple tests against the PUBSUB command. This is problematic, as we // can't be sure what's going on in the instance, but we can do some things. public function testPubSub() { // Only available since 2.8.0 if (version_compare($this->version, "2.8.0") < 0) { $this->markTestSkipped(); return; } // PUBSUB CHANNELS ... $result = $this->redis->pubsub("channels", "*"); $this->assertTrue(is_array($result)); $result = $this->redis->pubsub("channels"); $this->assertTrue(is_array($result)); // PUBSUB NUMSUB $c1 = uniqid() . '-' . rand(1,100); $c2 = uniqid() . '-' . rand(1,100); $result = $this->redis->pubsub("numsub", [$c1, $c2]); // Should get an array back, with two elements $this->assertTrue(is_array($result)); $this->assertEquals(count($result), 2); // Make sure the elements are correct, and have zero counts foreach([$c1,$c2] as $channel) { $this->assertTrue(isset($result[$channel])); $this->assertEquals($result[$channel], 0); } // PUBSUB NUMPAT $result = $this->redis->pubsub("numpat"); $this->assertTrue(is_int($result)); // Invalid calls $this->assertFalse(@$this->redis->pubsub("notacommand")); $this->assertFalse(@$this->redis->pubsub("numsub", "not-an-array")); } /* These test cases were generated randomly. We're just trying to test that PhpRedis handles all combination of arguments correctly. */ public function testBitcount() { /* key */ $this->redis->set('bitcountkey', hex2bin('bd906b854ca76cae')); $this->assertEquals(33, $this->redis->bitcount('bitcountkey')); /* key, start */ $this->redis->set('bitcountkey', hex2bin('400aac171382a29bebaab554f178')); $this->assertEquals(4, $this->redis->bitcount('bitcountkey', 13)); /* key, start, end */ $this->redis->set('bitcountkey', hex2bin('b1f32405')); $this->assertEquals(2, $this->redis->bitcount('bitcountkey', 3, 3)); /* key, start, end BYTE */ $this->redis->set('bitcountkey', hex2bin('10eb8939e68bfdb640260f0629f3')); $this->assertEquals(1, $this->redis->bitcount('bitcountkey', 8, 8, false)); /* key, start, end, BIT */ $this->redis->set('bitcountkey', hex2bin('cd0e4c80f9e4590d888a10')); $this->assertEquals(5, $this->redis->bitcount('bitcountkey', 0, 9, true)); } public function testBitop() { if (!$this->minVersionCheck('2.6.0')) $this->markTestSkipped(); $this->redis->set("{key}1", "foobar"); $this->redis->set("{key}2", "abcdef"); // Regression test for GitHub issue #2210 $this->assertEquals(6, $this->redis->bitop('AND', '{key}1', '{key}2')); // Make sure RedisCluster doesn't even send the command. We don't care // about what Redis returns @$this->redis->bitop('AND', 'key1', 'key2', 'key3'); $this->assertEquals(NULL, $this->redis->getLastError()); $this->redis->del('{key}1', '{key}2'); } public function testBitsets() { $this->redis->del('key'); $this->assertEquals(0, $this->redis->getBit('key', 0)); $this->assertEquals(FALSE, $this->redis->getBit('key', -1)); $this->assertEquals(0, $this->redis->getBit('key', 100000)); $this->redis->set('key', "\xff"); for($i = 0; $i < 8; $i++) { $this->assertEquals(1, $this->redis->getBit('key', $i)); } $this->assertEquals(0, $this->redis->getBit('key', 8)); // change bit 0 $this->assertEquals(1, $this->redis->setBit('key', 0, 0)); $this->assertEquals(0, $this->redis->setBit('key', 0, 0)); $this->assertEquals(0, $this->redis->getBit('key', 0)); $this->assertEquals("\x7f", $this->redis->get('key')); // change bit 1 $this->assertEquals(1, $this->redis->setBit('key', 1, 0)); $this->assertEquals(0, $this->redis->setBit('key', 1, 0)); $this->assertEquals(0, $this->redis->getBit('key', 1)); $this->assertEquals("\x3f", $this->redis->get('key')); // change bit > 1 $this->assertEquals(1, $this->redis->setBit('key', 2, 0)); $this->assertEquals(0, $this->redis->setBit('key', 2, 0)); $this->assertEquals(0, $this->redis->getBit('key', 2)); $this->assertEquals("\x1f", $this->redis->get('key')); // values above 1 are changed to 1 but don't overflow on bits to the right. $this->assertEquals(0, $this->redis->setBit('key', 0, 0xff)); $this->assertEquals("\x9f", $this->redis->get('key')); // Verify valid offset ranges $this->assertFalse($this->redis->getBit('key', -1)); $this->redis->setBit('key', 0x7fffffff, 1); $this->assertEquals(1, $this->redis->getBit('key', 0x7fffffff)); } public function testLcs() { $key1 = '{lcs}1'; $key2 = '{lcs}2'; $this->assertTrue($this->redis->set($key1, '12244447777777')); $this->assertTrue($this->redis->set($key2, '6666662244441')); $this->assertEquals('224444', $this->redis->lcs($key1, $key2)); $this->assertEquals( ['matches', [[[1, 6], [6, 11]]], 'len', 6], $this->redis->lcs($key1, $key2, ['idx']) ); $this->assertEquals( ['matches', [[[1, 6], [6, 11], 6]], 'len', 6], $this->redis->lcs($key1, $key2, ['idx', 'withmatchlen']) ); $this->assertEquals(6, $this->redis->lcs($key1, $key2, ['len'])); $this->redis->del([$key1, $key2]); } public function testLmpop() { if(version_compare($this->version, "7.0.0") < 0) { $this->markTestSkipped(); } $key1 = '{l}1'; $key2 = '{l}2'; $this->assertTrue($this->redis->del($key1, $key2) !== false); $this->assertEquals(6, $this->redis->rpush($key1, 'A', 'B', 'C', 'D', 'E', 'F')); $this->assertEquals(6, $this->redis->rpush($key2, 'F', 'E', 'D', 'C', 'B', 'A')); $this->assertEquals([$key1, ['A']], $this->redis->lmpop([$key1, $key2], 'LEFT')); $this->assertEquals([$key1, ['F']], $this->redis->lmpop([$key1, $key2], 'RIGHT')); $this->assertEquals([$key1, ['B', 'C', 'D']], $this->redis->lmpop([$key1, $key2], 'LEFT', 3)); $this->assertEquals(2, $this->redis->del($key1, $key2)); } public function testBLmpop() { if(version_compare($this->version, "7.0.0") < 0) { $this->markTestSkipped(); } $key1 = '{bl}1'; $key2 = '{bl}2'; $this->assertTrue($this->redis->del($key1, $key2) !== false); $this->assertEquals(2, $this->redis->rpush($key1, 'A', 'B')); $this->assertEquals(2, $this->redis->rpush($key2, 'C', 'D')); $this->assertEquals([$key1, ['B', 'A']], $this->redis->blmpop(.2, [$key1, $key2], 'RIGHT', 2)); $this->assertEquals([$key2, ['C']], $this->redis->blmpop(.2, [$key1, $key2], 'LEFT')); $this->assertEquals([$key2, ['D']], $this->redis->blmpop(.2, [$key1, $key2], 'LEFT')); $st = microtime(true); $this->assertFalse($this->redis->blmpop(.2, [$key1, $key2], 'LEFT')); $et = microtime(true); $this->assertTrue($et - $st >= .2); } function testZmpop() { if(version_compare($this->version, "7.0.0") < 0) { $this->markTestSkipped(); } $key1 = '{z}1'; $key2 = '{z}2'; $this->assertTrue($this->redis->del($key1, $key2) !== false); $this->assertEquals(4, $this->redis->zadd($key1, 0, 'zero', 2, 'two', 4, 'four', 6, 'six')); $this->assertEquals(4, $this->redis->zadd($key2, 1, 'one', 3, 'three', 5, 'five', 7, 'seven')); $this->assertEquals([$key1, ['zero' => 0.0]], $this->redis->zmpop([$key1, $key2], 'MIN')); $this->assertEquals([$key1, ['six' => 6.0]], $this->redis->zmpop([$key1, $key2], 'MAX')); $this->assertEquals([$key1, ['two' => 2.0, 'four' => 4.0]], $this->redis->zmpop([$key1, $key2], 'MIN', 3)); $this->assertEquals( [$key2, ['one' => 1.0, 'three' => 3.0, 'five' => 5.0, 'seven' => 7.0]], $this->redis->zmpop([$key1, $key2], 'MIN', 128) ); $this->assertFalse($this->redis->zmpop([$key1, $key2], 'MIN')); $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, true); $this->assertEquals(NULL, $this->redis->zmpop([$key1, $key2], 'MIN')); $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } function testBZmpop() { if(version_compare($this->version, "7.0.0") < 0) { $this->markTestSkipped(); } $key1 = '{z}1'; $key2 = '{z}2'; $this->assertTrue($this->redis->del($key1, $key2) !== false); $this->assertEquals(2, $this->redis->zadd($key1, 0, 'zero', 2, 'two')); $this->assertEquals(2, $this->redis->zadd($key2, 1, 'one', 3, 'three')); $this->assertEquals( [$key1, ['zero' => 0.0, 'two' => 2.0]], $this->redis->bzmpop(.1, [$key1, $key2], 'MIN', 2) ); $this->assertEquals([$key2, ['three' => 3.0]], $this->redis->bzmpop(.1, [$key1, $key2], 'MAX')); $this->assertEquals([$key2, ['one' => 1.0]], $this->redis->bzmpop(.1, [$key1, $key2], 'MAX')); $st = microtime(true); $this->assertFalse($this->redis->bzmpop(.2, [$key1, $key2], 'MIN')); $et = microtime(true); $this->assertTrue($et - $st >= .2); } public function testBitPos() { if (version_compare($this->version, "2.8.7") < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('bpkey'); $this->redis->set('bpkey', "\xff\xf0\x00"); $this->assertEquals($this->redis->bitpos('bpkey', 0), 12); $this->redis->set('bpkey', "\x00\xff\xf0"); $this->assertEquals($this->redis->bitpos('bpkey', 1, 0), 8); $this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 8); $this->redis->set('bpkey', "\x00\x00\x00"); $this->assertEquals($this->redis->bitpos('bpkey', 1), -1); if (!$this->minVersionCheck("7.0.0")) return; $this->redis->set('bpkey', "\xF"); $this->assertEquals(4, $this->redis->bitpos('bpkey', 1, 0, -1, true)); $this->assertEquals(-1, $this->redis->bitpos('bpkey', 1, 1, -1)); $this->assertEquals(-1, $this->redis->bitpos('bpkey', 1, 1, -1, false)); } public function test1000() { $s = str_repeat('A', 1000); $this->redis->set('x', $s); $this->assertEquals($s, $this->redis->get('x')); $s = str_repeat('A', 1000000); $this->redis->set('x', $s); $this->assertEquals($s, $this->redis->get('x')); } public function testEcho() { $this->assertEquals($this->redis->echo("hello"), "hello"); $this->assertEquals($this->redis->echo(""), ""); $this->assertEquals($this->redis->echo(" 0123 "), " 0123 "); } public function testErr() { $this->redis->set('x', '-ERR'); $this->assertEquals($this->redis->get('x'), '-ERR'); } public function testSet() { $this->assertEquals(TRUE, $this->redis->set('key', 'nil')); $this->assertEquals('nil', $this->redis->get('key')); $this->assertEquals(TRUE, $this->redis->set('key', 'val')); $this->assertEquals('val', $this->redis->get('key')); $this->assertEquals('val', $this->redis->get('key')); $this->redis->del('keyNotExist'); $this->assertEquals(FALSE, $this->redis->get('keyNotExist')); $this->redis->set('key2', 'val'); $this->assertEquals('val', $this->redis->get('key2')); $value = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; $this->redis->set('key2', $value); $this->assertEquals($value, $this->redis->get('key2')); $this->assertEquals($value, $this->redis->get('key2')); $this->redis->del('key'); $this->redis->del('key2'); $i = 66000; $value2 = 'X'; while($i--) { $value2 .= 'A'; } $value2 .= 'X'; $this->redis->set('key', $value2); $this->assertEquals($value2, $this->redis->get('key')); $this->redis->del('key'); $this->assertEquals(False, $this->redis->get('key')); $data = gzcompress('42'); $this->assertEquals(True, $this->redis->set('key', $data)); $this->assertEquals('42', gzuncompress($this->redis->get('key'))); $this->redis->del('key'); $data = gzcompress('value1'); $this->assertEquals(True, $this->redis->set('key', $data)); $this->assertEquals('value1', gzuncompress($this->redis->get('key'))); $this->redis->del('key'); $this->assertEquals(TRUE, $this->redis->set('key', 0)); $this->assertEquals('0', $this->redis->get('key')); $this->assertEquals(TRUE, $this->redis->set('key', 1)); $this->assertEquals('1', $this->redis->get('key')); $this->assertEquals(TRUE, $this->redis->set('key', 0.1)); $this->assertEquals('0.1', $this->redis->get('key')); $this->assertEquals(TRUE, $this->redis->set('key', '0.1')); $this->assertEquals('0.1', $this->redis->get('key')); $this->assertEquals(TRUE, $this->redis->set('key', TRUE)); $this->assertEquals('1', $this->redis->get('key')); $this->assertEquals(True, $this->redis->set('key', '')); $this->assertEquals('', $this->redis->get('key')); $this->assertEquals(True, $this->redis->set('key', NULL)); $this->assertEquals('', $this->redis->get('key')); $this->assertEquals(True, $this->redis->set('key', gzcompress('42'))); $this->assertEquals('42', gzuncompress($this->redis->get('key'))); } /* Extended SET options for Redis >= 2.6.12 */ public function testExtendedSet() { // Skip the test if we don't have a new enough version of Redis if (version_compare($this->version, '2.6.12') < 0) { $this->markTestSkipped(); return; } /* Legacy SETEX redirection */ $this->redis->del('foo'); $this->assertTrue($this->redis->set('foo','bar', 20)); $this->assertEquals($this->redis->get('foo'), 'bar'); $this->assertEquals($this->redis->ttl('foo'), 20); /* Should coerce doubles into long */ $this->assertTrue($this->redis->set('foo', 'bar-20.5', 20.5)); $this->assertEquals($this->redis->ttl('foo'), 20); $this->assertEquals($this->redis->get('foo'), 'bar-20.5'); /* Invalid third arguments */ $this->assertFalse(@$this->redis->set('foo','bar','baz')); $this->assertFalse(@$this->redis->set('foo','bar',new StdClass())); /* Set if not exist */ $this->redis->del('foo'); $this->assertTrue($this->redis->set('foo','bar', ['nx'])); $this->assertEquals($this->redis->get('foo'), 'bar'); $this->assertFalse($this->redis->set('foo','bar', ['nx'])); /* Set if exists */ $this->assertTrue($this->redis->set('foo','bar', ['xx'])); $this->assertEquals($this->redis->get('foo'), 'bar'); $this->redis->del('foo'); $this->assertFalse($this->redis->set('foo','bar', ['xx'])); /* Set with a TTL */ $this->assertTrue($this->redis->set('foo','bar', ['ex'=>100])); $this->assertEquals($this->redis->ttl('foo'), 100); /* Set with a PTTL */ $this->assertTrue($this->redis->set('foo','bar',['px'=>100000])); $this->assertTrue(100000 - $this->redis->pttl('foo') < 1000); /* Set if exists, with a TTL */ $this->assertTrue($this->redis->set('foo','bar',['xx','ex'=>105])); $this->assertEquals($this->redis->ttl('foo'), 105); $this->assertEquals($this->redis->get('foo'), 'bar'); /* Set if not exists, with a TTL */ $this->redis->del('foo'); $this->assertTrue($this->redis->set('foo','bar', ['nx', 'ex'=>110])); $this->assertEquals($this->redis->ttl('foo'), 110); $this->assertEquals($this->redis->get('foo'), 'bar'); $this->assertFalse($this->redis->set('foo','bar', ['nx', 'ex'=>110])); /* Throw some nonsense into the array, and check that the TTL came through */ $this->redis->del('foo'); $this->assertTrue($this->redis->set('foo','barbaz', ['not-valid','nx','invalid','ex'=>200])); $this->assertEquals($this->redis->ttl('foo'), 200); $this->assertEquals($this->redis->get('foo'), 'barbaz'); /* Pass NULL as the optional arguments which should be ignored */ $this->redis->del('foo'); $this->redis->set('foo','bar', NULL); $this->assertEquals($this->redis->get('foo'), 'bar'); $this->assertTrue($this->redis->ttl('foo')<0); /* Make sure we ignore bad/non-string options (regression test for #1835) */ $this->assertTrue($this->redis->set('foo', 'bar', [NULL, 'EX' => 60])); $this->assertTrue($this->redis->set('foo', 'bar', [NULL, new stdClass(), 'EX' => 60])); $this->assertFalse(@$this->redis->set('foo', 'bar', [NULL, 'EX' => []])); if (version_compare($this->version, "6.0.0") < 0) return; /* KEEPTTL works by itself */ $this->redis->set('foo', 'bar', ['EX' => 100]); $this->redis->set('foo', 'bar', ['KEEPTTL']); $this->assertTrue($this->redis->ttl('foo') > -1); /* Works with other options */ $this->redis->set('foo', 'bar', ['XX', 'KEEPTTL']); $this->assertTrue($this->redis->ttl('foo') > -1); $this->redis->set('foo', 'bar', ['XX']); $this->assertTrue($this->redis->ttl('foo') == -1); if (version_compare($this->version, "6.2.0") < 0) return; $this->assertTrue($this->redis->set('foo', 'baz', ['GET']) === 'bar'); } public function testGetSet() { $this->redis->del('key'); $this->assertTrue($this->redis->getSet('key', '42') === FALSE); $this->assertTrue($this->redis->getSet('key', '123') === '42'); $this->assertTrue($this->redis->getSet('key', '123') === '123'); } public function testRandomKey() { for($i = 0; $i < 1000; $i++) { $k = $this->redis->randomKey(); $this->assertEquals($this->redis->exists($k), 1); } } public function testRename() { // strings $this->redis->del('{key}0'); $this->redis->set('{key}0', 'val0'); $this->redis->rename('{key}0', '{key}1'); $this->assertEquals(FALSE, $this->redis->get('{key}0')); $this->assertEquals('val0', $this->redis->get('{key}1')); } public function testRenameNx() { // strings $this->redis->del('{key}0', '{key}1'); $this->redis->set('{key}0', 'val0'); $this->redis->set('{key}1', 'val1'); $this->assertTrue($this->redis->renameNx('{key}0', '{key}1') === FALSE); $this->assertTrue($this->redis->get('{key}0') === 'val0'); $this->assertTrue($this->redis->get('{key}1') === 'val1'); // lists $this->redis->del('{key}0'); $this->redis->del('{key}1'); $this->redis->lPush('{key}0', 'val0'); $this->redis->lPush('{key}0', 'val1'); $this->redis->lPush('{key}1', 'val1-0'); $this->redis->lPush('{key}1', 'val1-1'); $this->assertTrue($this->redis->renameNx('{key}0', '{key}1') === FALSE); $this->assertTrue($this->redis->lRange('{key}0', 0, -1) === ['val1', 'val0']); $this->assertTrue($this->redis->lRange('{key}1', 0, -1) === ['val1-1', 'val1-0']); $this->redis->del('{key}2'); $this->assertTrue($this->redis->renameNx('{key}0', '{key}2') === TRUE); $this->assertTrue($this->redis->lRange('{key}0', 0, -1) === []); $this->assertTrue($this->redis->lRange('{key}2', 0, -1) === ['val1', 'val0']); } public function testMultiple() { $this->redis->del('k1'); $this->redis->del('k2'); $this->redis->del('k3'); $this->redis->set('k1', 'v1'); $this->redis->set('k2', 'v2'); $this->redis->set('k3', 'v3'); $this->redis->set(1, 'test'); $this->assertEquals(['v1'], $this->redis->mget(['k1'])); $this->assertEquals(['v1', 'v3', false], $this->redis->mget(['k1', 'k3', 'NoKey'])); $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['k1', 'k2', 'k3'])); $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['k1', 'k2', 'k3'])); $this->redis->set('k5', '$1111111111'); $this->assertEquals([0 => '$1111111111'], $this->redis->mget(['k5'])); $this->assertEquals([0 => 'test'], $this->redis->mget([1])); // non-string } public function testMultipleBin() { $this->redis->del('k1'); $this->redis->del('k2'); $this->redis->del('k3'); $this->redis->set('k1', gzcompress('v1')); $this->redis->set('k2', gzcompress('v2')); $this->redis->set('k3', gzcompress('v3')); $this->assertEquals([gzcompress('v1'), gzcompress('v2'), gzcompress('v3')], $this->redis->mget(['k1', 'k2', 'k3'])); $this->assertEquals([gzcompress('v1'), gzcompress('v2'), gzcompress('v3')], $this->redis->mget(['k1', 'k2', 'k3'])); } public function testSetTimeout() { $this->redis->del('key'); $this->redis->set('key', 'value'); $this->assertEquals('value', $this->redis->get('key')); $this->redis->expire('key', 1); $this->assertEquals('value', $this->redis->get('key')); sleep(2); $this->assertEquals(False, $this->redis->get('key')); } /* This test is prone to failure in the Travis container, so attempt to mitigate this by running more than once */ public function testExpireAt() { $success = false; for ($i = 0; !$success && $i < 3; $i++) { $this->redis->del('key'); $this->redis->set('key', 'value'); $this->redis->expireAt('key', time() + 1); usleep(1500000); $success = FALSE === $this->redis->get('key'); } $this->assertTrue($success); } function testExpireOptions() { if (!$this->minVersionCheck('7.0.0')) return; $this->redis->set('eopts', 'value'); /* NX -- Only if expiry isn't set so success, then failure */ $this->assertTrue($this->redis->expire('eopts', 1000, 'NX')); $this->assertFalse($this->redis->expire('eopts', 1000, 'NX')); /* XX -- Only set if the key has an existing expiry */ $this->assertTrue($this->redis->expire('eopts', 1000, 'XX')); $this->assertTrue($this->redis->persist('eopts')); $this->assertFalse($this->redis->expire('eopts', 1000, 'XX')); /* GT -- Only set when new expiry > current expiry */ $this->assertTrue($this->redis->expire('eopts', 200)); $this->assertTrue($this->redis->expire('eopts', 300, 'GT')); $this->assertFalse($this->redis->expire('eopts', 100, 'GT')); /* LT -- Only set when expiry < current expiry */ $this->assertTrue($this->redis->expire('eopts', 200)); $this->assertTrue($this->redis->expire('eopts', 100, 'LT')); $this->assertFalse($this->redis->expire('eopts', 300, 'LT')); /* Sending a nonsensical mode fails without sending a command */ $this->redis->clearLastError(); $this->assertFalse(@$this->redis->expire('eopts', 999, 'nonsense')); $this->assertEquals(NULL, $this->redis->getLastError()); $this->redis->del('eopts'); } public function testExpiretime() { if(version_compare($this->version, "7.0.0") < 0) { $this->markTestSkipped(); } $now = time(); $this->assertTrue($this->redis->set('key1', 'value')); $this->assertTrue($this->redis->expireat('key1', $now + 10)); $this->assertEquals($now + 10, $this->redis->expiretime('key1')); $this->assertEquals(1000 * ($now + 10), $this->redis->pexpiretime('key1')); $this->redis->del('key1'); } public function testSetEx() { $this->redis->del('key'); $this->assertTrue($this->redis->setex('key', 7, 'val') === TRUE); $this->assertTrue($this->redis->ttl('key') ===7); $this->assertTrue($this->redis->get('key') === 'val'); } public function testPSetEx() { $this->redis->del('key'); $this->assertTrue($this->redis->psetex('key', 7 * 1000, 'val') === TRUE); $this->assertTrue($this->redis->ttl('key') ===7); $this->assertTrue($this->redis->get('key') === 'val'); } public function testSetNX() { $this->redis->set('key', 42); $this->assertTrue($this->redis->setnx('key', 'err') === FALSE); $this->assertTrue($this->redis->get('key') === '42'); $this->redis->del('key'); $this->assertTrue($this->redis->setnx('key', '42') === TRUE); $this->assertTrue($this->redis->get('key') === '42'); } public function testExpireAtWithLong() { if (PHP_INT_SIZE != 8) { $this->markTestSkipped('64 bits only'); } $longExpiryTimeExceedingInt = 3153600000; $this->redis->del('key'); $this->assertTrue($this->redis->setex('key', $longExpiryTimeExceedingInt, 'val') === TRUE); $this->assertTrue($this->redis->ttl('key') === $longExpiryTimeExceedingInt); } public function testIncr() { $this->redis->set('key', 0); $this->redis->incr('key'); $this->assertEquals(1, (int)$this->redis->get('key')); $this->redis->incr('key'); $this->assertEquals(2, (int)$this->redis->get('key')); $this->redis->incrBy('key', 3); $this->assertEquals(5, (int)$this->redis->get('key')); $this->redis->incrBy('key', 1); $this->assertEquals(6, (int)$this->redis->get('key')); $this->redis->incrBy('key', -1); $this->assertEquals(5, (int)$this->redis->get('key')); $this->redis->incr('key', 5); $this->assertEquals(10, (int)$this->redis->get('key')); $this->redis->del('key'); $this->redis->set('key', 'abc'); $this->redis->incr('key'); $this->assertTrue("abc" === $this->redis->get('key')); $this->redis->incr('key'); $this->assertTrue("abc" === $this->redis->get('key')); $this->redis->set('key', 0); $this->assertEquals(PHP_INT_MAX, $this->redis->incrby('key', PHP_INT_MAX)); } public function testIncrByFloat() { // incrbyfloat is new in 2.6.0 if (version_compare($this->version, "2.5.0") < 0) { $this->markTestSkipped(); } $this->redis->del('key'); $this->redis->set('key', 0); $this->redis->incrbyfloat('key', 1.5); $this->assertEquals('1.5', $this->redis->get('key')); $this->redis->incrbyfloat('key', 2.25); $this->assertEquals('3.75', $this->redis->get('key')); $this->redis->incrbyfloat('key', -2.25); $this->assertEquals('1.5', $this->redis->get('key')); $this->redis->set('key', 'abc'); $this->redis->incrbyfloat('key', 1.5); $this->assertTrue("abc" === $this->redis->get('key')); $this->redis->incrbyfloat('key', -1.5); $this->assertTrue("abc" === $this->redis->get('key')); // Test with prefixing $this->redis->setOption(Redis::OPT_PREFIX, 'someprefix:'); $this->redis->del('key'); $this->redis->incrbyfloat('key',1.8); $this->assertEquals(1.8, floatval($this->redis->get('key'))); // convert to float to avoid rounding issue on arm $this->redis->setOption(Redis::OPT_PREFIX, ''); $this->assertEquals(1, $this->redis->exists('someprefix:key')); $this->redis->del('someprefix:key'); } public function testDecr() { $this->redis->set('key', 5); $this->redis->decr('key'); $this->assertEquals(4, (int)$this->redis->get('key')); $this->redis->decr('key'); $this->assertEquals(3, (int)$this->redis->get('key')); $this->redis->decrBy('key', 2); $this->assertEquals(1, (int)$this->redis->get('key')); $this->redis->decrBy('key', 1); $this->assertEquals(0, (int)$this->redis->get('key')); $this->redis->decrBy('key', -10); $this->assertEquals(10, (int)$this->redis->get('key')); $this->redis->decr('key', 10); $this->assertEquals(0, (int)$this->redis->get('key')); } public function testExists() { /* Single key */ $this->redis->del('key'); $this->assertEquals(0, $this->redis->exists('key')); $this->redis->set('key', 'val'); $this->assertEquals(1, $this->redis->exists('key')); /* Add multiple keys */ $mkeys = []; for ($i = 0; $i < 10; $i++) { if (rand(1, 2) == 1) { $mkey = "{exists}key:$i"; $this->redis->set($mkey, $i); $mkeys[] = $mkey; } } /* Test passing an array as well as the keys variadic */ $this->assertEquals(count($mkeys), $this->redis->exists($mkeys)); $this->assertEquals(count($mkeys), $this->redis->exists(...$mkeys)); } public function testTouch() { if (!$this->minVersionCheck('3.2.1')) $this->markTestSkipped(); $this->redis->del('notakey'); $this->assertTrue($this->redis->mset(['{idle}1' => 'beep', '{idle}2' => 'boop'])); usleep(2100000); $this->assertTrue($this->redis->object('idletime', '{idle}1') >= 2); $this->assertTrue($this->redis->object('idletime', '{idle}2') >= 2); $this->assertEquals(2, $this->redis->touch('{idle}1', '{idle}2', '{idle}notakey')); $idle1 = $this->redis->object('idletime', '{idle}1'); $idle2 = $this->redis->object('idletime', '{idle}2'); /* We're not testing if idle is 0 because CPU scheduling on GitHub CI * potatoes can cause that to erroneously fail. */ $this->assertTrue($idle1 < 2); $this->assertTrue($idle2 < 2); } public function testKeys() { $pattern = 'keys-test-'; for($i = 1; $i < 10; $i++) { $this->redis->set($pattern.$i, $i); } $this->redis->del($pattern.'3'); $keys = $this->redis->keys($pattern.'*'); $this->redis->set($pattern.'3', 'something'); $keys2 = $this->redis->keys($pattern.'*'); $this->assertEquals((count($keys) + 1), count($keys2)); // empty array when no key matches $this->assertEquals([], $this->redis->keys(rand().rand().rand().'*')); } protected function genericDelUnlink($cmd) { $key = 'key' . rand(); $this->redis->set($key, 'val'); $this->assertEquals('val', $this->redis->get($key)); $this->assertEquals(1, $this->redis->$cmd($key)); $this->assertEquals(false, $this->redis->get($key)); // multiple, all existing $this->redis->set('x', 0); $this->redis->set('y', 1); $this->redis->set('z', 2); $this->assertEquals(3, $this->redis->$cmd('x', 'y', 'z')); $this->assertEquals(false, $this->redis->get('x')); $this->assertEquals(false, $this->redis->get('y')); $this->assertEquals(false, $this->redis->get('z')); // multiple, none existing $this->assertEquals(0, $this->redis->$cmd('x', 'y', 'z')); $this->assertEquals(false, $this->redis->get('x')); $this->assertEquals(false, $this->redis->get('y')); $this->assertEquals(false, $this->redis->get('z')); // multiple, some existing $this->redis->set('y', 1); $this->assertEquals(1, $this->redis->$cmd('x', 'y', 'z')); $this->assertEquals(false, $this->redis->get('y')); $this->redis->set('x', 0); $this->redis->set('y', 1); $this->assertEquals(2, $this->redis->$cmd(['x', 'y'])); } public function testDelete() { $this->genericDelUnlink("DEL"); } public function testUnlink() { if (version_compare($this->version, "4.0.0") < 0) { $this->markTestSkipped(); return; } $this->genericDelUnlink("UNLINK"); } public function testType() { // 0 => none, (key didn't exist) // 1=> string, // 2 => set, // 3 => list, // 4 => zset, // 5 => hash // 6 => stream // string $this->redis->set('key', 'val'); $this->assertEquals(Redis::REDIS_STRING, $this->redis->type('key')); // list $this->redis->lPush('keyList', 'val0'); $this->redis->lPush('keyList', 'val1'); $this->assertEquals(Redis::REDIS_LIST, $this->redis->type('keyList')); // set $this->redis->del('keySet'); $this->redis->sAdd('keySet', 'val0'); $this->redis->sAdd('keySet', 'val1'); $this->assertEquals(Redis::REDIS_SET, $this->redis->type('keySet')); // sadd with numeric key $this->redis->del(123); $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); $this->assertTrue(['val0'] === $this->redis->sMembers(123)); // zset $this->redis->del('keyZSet'); $this->redis->zAdd('keyZSet', 0, 'val0'); $this->redis->zAdd('keyZSet', 1, 'val1'); $this->assertEquals(Redis::REDIS_ZSET, $this->redis->type('keyZSet')); // hash $this->redis->del('keyHash'); $this->redis->hSet('keyHash', 'key0', 'val0'); $this->redis->hSet('keyHash', 'key1', 'val1'); $this->assertEquals(Redis::REDIS_HASH, $this->redis->type('keyHash')); // stream if ($this->minVersionCheck("5.0")) { $this->redis->del('stream'); $this->redis->xAdd('stream', '*', ['foo' => 'bar']); $this->assertEquals(Redis::REDIS_STREAM, $this->redis->type('stream')); } // None $this->redis->del('keyNotExists'); $this->assertEquals(Redis::REDIS_NOT_FOUND, $this->redis->type('keyNotExists')); } public function testStr() { $this->redis->set('key', 'val1'); $this->assertTrue($this->redis->append('key', 'val2') === 8); $this->assertTrue($this->redis->get('key') === 'val1val2'); $this->redis->del('keyNotExist'); $this->assertTrue($this->redis->append('keyNotExist', 'value') === 5); $this->assertTrue($this->redis->get('keyNotExist') === 'value'); $this->redis->set('key', 'This is a string') ; $this->assertTrue($this->redis->getRange('key', 0, 3) === 'This'); $this->assertTrue($this->redis->getRange('key', -6, -1) === 'string'); $this->assertTrue($this->redis->getRange('key', -6, 100000) === 'string'); $this->assertTrue($this->redis->get('key') === 'This is a string'); $this->redis->set('key', 'This is a string') ; $this->assertTrue($this->redis->strlen('key') === 16); $this->redis->set('key', 10) ; $this->assertTrue($this->redis->strlen('key') === 2); $this->redis->set('key', '') ; $this->assertTrue($this->redis->strlen('key') === 0); $this->redis->set('key', '000') ; $this->assertTrue($this->redis->strlen('key') === 3); } // PUSH, POP : LPUSH, LPOP public function testlPop() { // rpush => tail // lpush => head $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); $this->redis->rPush('list', 'val3'); // 'list' = [ 'val2', 'val', 'val3'] $this->assertEquals('val2', $this->redis->lPop('list')); if (version_compare($this->version, "6.2.0") < 0) { $this->assertEquals('val', $this->redis->lPop('list')); $this->assertEquals('val3', $this->redis->lPop('list')); } else { $this->assertEquals(['val', 'val3'], $this->redis->lPop('list', 2)); } $this->assertEquals(FALSE, $this->redis->lPop('list')); // testing binary data $this->redis->del('list'); $this->assertEquals(1, $this->redis->lPush('list', gzcompress('val1'))); $this->assertEquals(2, $this->redis->lPush('list', gzcompress('val2'))); $this->assertEquals(3, $this->redis->lPush('list', gzcompress('val3'))); $this->assertEquals('val3', gzuncompress($this->redis->lPop('list'))); $this->assertEquals('val2', gzuncompress($this->redis->lPop('list'))); $this->assertEquals('val1', gzuncompress($this->redis->lPop('list'))); } // PUSH, POP : RPUSH, RPOP public function testrPop() { $this->redis->del('list'); $this->redis->rPush('list', 'val'); $this->redis->rPush('list', 'val2'); $this->redis->lPush('list', 'val3'); $this->assertEquals('val2', $this->redis->rPop('list')); if (version_compare($this->version, "6.2.0") < 0) { $this->assertEquals('val', $this->redis->rPop('list')); $this->assertEquals('val3', $this->redis->rPop('list')); } else { $this->assertEquals(['val', 'val3'], $this->redis->rPop('list', 2)); } $this->assertEquals(FALSE, $this->redis->rPop('list')); $this->redis->del('list'); $this->assertEquals(1, $this->redis->rPush('list', gzcompress('val1'))); $this->assertEquals(2, $this->redis->rPush('list', gzcompress('val2'))); $this->assertEquals(3, $this->redis->rPush('list', gzcompress('val3'))); $this->assertEquals('val3', gzuncompress($this->redis->rPop('list'))); $this->assertEquals('val2', gzuncompress($this->redis->rPop('list'))); $this->assertEquals('val1', gzuncompress($this->redis->rPop('list'))); } /* Regression test for GH #2329 */ public function testrPopSerialization() { $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); $this->redis->del('rpopkey'); $this->redis->rpush('rpopkey', ['foo'], ['bar']); $this->assertEquals([['bar'], ['foo']], $this->redis->rpop('rpopkey', 2)); $this->redis->rpush('rpopkey', ['foo'], ['bar']); $this->assertEquals([['foo'], ['bar']], $this->redis->lpop('rpopkey', 2)); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); } public function testblockingPop() { /* Test with a double timeout in Redis >= 6.0.0 */ if (version_compare($this->version, "6.0.0") >= 0) { $this->redis->del('list'); $this->redis->lpush('list', 'val1', 'val2'); $this->assertEquals(['list', 'val2'], $this->redis->blpop(['list'], .1)); $this->assertEquals(['list', 'val1'], $this->redis->blpop(['list'], .1)); } // non blocking blPop, brPop $this->redis->del('list'); $this->redis->lPush('list', 'val1', 'val2'); $this->assertEquals(['list', 'val2'], $this->redis->blPop(['list'], 2)); $this->assertEquals(['list', 'val1'], $this->redis->blPop(['list'], 2)); $this->redis->del('list'); $this->redis->lPush('list', 'val1', 'val2'); $this->assertEquals(['list', 'val1'], $this->redis->brPop(['list'], 1)); $this->assertEquals(['list', 'val2'], $this->redis->brPop(['list'], 1)); // blocking blpop, brpop $this->redis->del('list'); /* Also test our option that we want *-1 to be returned as NULL */ foreach ([false => [], true => NULL] as $opt => $val) { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt); $this->assertEquals($val, $this->redis->blPop(['list'], 1)); $this->assertEquals($val, $this->redis->brPop(['list'], 1)); } $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } public function testllen() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->assertEquals(1, $this->redis->llen('list')); $this->redis->lPush('list', 'val2'); $this->assertEquals(2, $this->redis->llen('list')); $this->assertEquals('val2', $this->redis->lPop('list')); $this->assertEquals(1, $this->redis->llen('list')); $this->assertEquals('val', $this->redis->lPop('list')); $this->assertEquals(0, $this->redis->llen('list')); $this->assertEquals(FALSE, $this->redis->lPop('list')); $this->assertEquals(0, $this->redis->llen('list')); // empty returns 0 $this->redis->del('list'); $this->assertEquals(0, $this->redis->llen('list')); // non-existent returns 0 $this->redis->set('list', 'actually not a list'); $this->assertEquals(FALSE, $this->redis->llen('list'));// not a list returns FALSE } //lInsert, lPopx, rPopx public function testlPopx() { //test lPushx/rPushx $this->redis->del('keyNotExists'); $this->assertTrue($this->redis->lPushx('keyNotExists', 'value') === 0); $this->assertTrue($this->redis->rPushx('keyNotExists', 'value') === 0); $this->redis->del('key'); $this->redis->lPush('key', 'val0'); $this->assertTrue($this->redis->lPushx('key', 'val1') === 2); $this->assertTrue($this->redis->rPushx('key', 'val2') === 3); $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val1', 'val0', 'val2']); //test linsert $this->redis->del('key'); $this->redis->lPush('key', 'val0'); $this->assertTrue($this->redis->lInsert('keyNotExists', Redis::AFTER, 'val1', 'val2') === 0); $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, 'valX', 'val2') === -1); $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, 'val0', 'val1') === 2); $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, 'val0', 'val2') === 3); $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val2', 'val0', 'val1']); } public function testlPos() { $this->redis->del('key'); $this->redis->lPush('key', 'val0', 'val1', 'val1'); $this->assertEquals(2, $this->redis->lPos('key', 'val0')); $this->assertEquals(0, $this->redis->lPos('key', 'val1')); $this->assertEquals(1, $this->redis->lPos('key', 'val1', ['rank' => 2])); $this->assertEquals([0, 1], $this->redis->lPos('key', 'val1', ['count' => 2])); $this->assertEquals([0], $this->redis->lPos('key', 'val1', ['count' => 2, 'maxlen' => 1])); $this->assertEquals([], $this->redis->lPos('key', 'val2', ['count' => 1])); foreach ([[true, NULL], [false, false]] as $optpack) { list ($setting, $expected) = $optpack; $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $setting); $this->assertEquals($expected, $this->redis->lPos('key', 'val2')); } } // ltrim, lsize, lpop public function testltrim() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); $this->redis->lPush('list', 'val4'); $this->assertEquals(TRUE, $this->redis->ltrim('list', 0, 2)); $this->assertEquals(3, $this->redis->llen('list')); $this->redis->ltrim('list', 0, 0); $this->assertEquals(1, $this->redis->llen('list')); $this->assertEquals('val4', $this->redis->lPop('list')); $this->assertEquals(TRUE, $this->redis->ltrim('list', 10, 10000)); $this->assertEquals(TRUE, $this->redis->ltrim('list', 10000, 10)); // test invalid type $this->redis->set('list', 'not a list...'); $this->assertEquals(FALSE, $this->redis->ltrim('list', 0, 2)); } public function setupSort() { // people with name, age, salary $this->redis->set('person:name_1', 'Alice'); $this->redis->set('person:age_1', 27); $this->redis->set('person:salary_1', 2500); $this->redis->set('person:name_2', 'Bob'); $this->redis->set('person:age_2', 34); $this->redis->set('person:salary_2', 2000); $this->redis->set('person:name_3', 'Carol'); $this->redis->set('person:age_3', 25); $this->redis->set('person:salary_3', 2800); $this->redis->set('person:name_4', 'Dave'); $this->redis->set('person:age_4', 41); $this->redis->set('person:salary_4', 3100); // set-up $this->redis->del('person:id'); foreach([1,2,3,4] as $id) { $this->redis->lPush('person:id', $id); } } public function testSortPrefix() { // Make sure that sorting works with a prefix $this->redis->setOption(Redis::OPT_PREFIX, 'some-prefix:'); $this->redis->del('some-item'); $this->redis->sadd('some-item', 1); $this->redis->sadd('some-item', 2); $this->redis->sadd('some-item', 3); $this->assertEquals(['1','2','3'], $this->redis->sort('some-item', ['sort' => 'asc'])); $this->assertEquals(['3','2','1'], $this->redis->sort('some-item', ['sort' => 'desc'])); $this->assertEquals(['1','2','3'], $this->redis->sort('some-item')); // Kill our set/prefix $this->redis->del('some-item'); $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testSortAsc() { $this->setupSort(); // sort by age and get IDs $byAgeAsc = ['3','1','2','4']; $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*'])); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'sort' => 'asc'])); $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['by' => NULL])); // check that NULL works. $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['by' => NULL, 'get' => NULL])); // for all fields. $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['sort' => 'asc'])); // sort by age and get names $byAgeAsc = ['Carol','Alice','Bob','Dave']; $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*'])); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'])); $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2]])); $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'asc'])); $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2]])); $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'asc'])); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 4]])); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, "4"]])); // with strings $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => ["0", 4]])); // sort by salary and get ages $agesBySalaryAsc = ['34', '27', '25', '41']; $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => 'person:age_*'])); $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'])); $agesAndSalaries = $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => ['person:age_*', 'person:salary_*'], 'sort' => 'asc']); $this->assertEquals(['34', '2000', '27', '2500', '25', '2800', '41', '3100'], $agesAndSalaries); // sort non-alpha doesn't change all-string lists // list → [ghi, def, abc] $list = ['abc', 'def', 'ghi']; $this->redis->del('list'); foreach($list as $i) { $this->redis->lPush('list', $i); } // SORT list → [ghi, def, abc] if (version_compare($this->version, "2.5.0") < 0) { $this->assertEquals(array_reverse($list), $this->redis->sort('list')); $this->assertEquals(array_reverse($list), $this->redis->sort('list', ['sort' => 'asc'])); } else { // TODO rewrite, from 2.6.0 release notes: // SORT now will refuse to sort in numerical mode elements that can't be parsed // as numbers } // SORT list ALPHA → [abc, def, ghi] $this->assertEquals($list, $this->redis->sort('list', ['alpha' => TRUE])); $this->assertEquals($list, $this->redis->sort('list', ['sort' => 'asc', 'alpha' => TRUE])); } public function testSortDesc() { $this->setupSort(); // sort by age and get IDs $byAgeDesc = ['4','2','1','3']; $this->assertEquals($byAgeDesc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'sort' => 'desc'])); // sort by age and get names $byAgeDesc = ['Dave', 'Bob', 'Alice', 'Carol']; $this->assertEquals($byAgeDesc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'desc'])); $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'desc'])); $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'desc'])); // sort by salary and get ages $agesBySalaryDesc = ['41', '25', '27', '34']; $this->assertEquals($agesBySalaryDesc, $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'desc'])); // sort non-alpha doesn't change all-string lists $list = ['def', 'abc', 'ghi']; $this->redis->del('list'); foreach($list as $i) { $this->redis->lPush('list', $i); } // SORT list ALPHA → [abc, def, ghi] $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sort('list', ['sort' => 'desc', 'alpha' => TRUE])); } /* This test is just to make sure SORT and SORT_RO are both callable */ public function testSortHandler() { $this->redis->del('list'); $this->redis->rpush('list', 'c', 'b', 'a'); $methods = ['sort']; if ($this->minVersionCheck('7.0.0')) $methods[] = 'sort_ro'; foreach ($methods as $method) { $this->assertEquals(['a', 'b', 'c'], $this->redis->$method('list', ['sort' => 'asc', 'alpha' => true])); } } // LINDEX public function testLindex() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); $this->assertEquals('val3', $this->redis->lIndex('list', 0)); $this->assertEquals('val2', $this->redis->lIndex('list', 1)); $this->assertEquals('val', $this->redis->lIndex('list', 2)); $this->assertEquals('val', $this->redis->lIndex('list', -1)); $this->assertEquals('val2', $this->redis->lIndex('list', -2)); $this->assertEquals('val3', $this->redis->lIndex('list', -3)); $this->assertEquals(FALSE, $this->redis->lIndex('list', -4)); $this->redis->rPush('list', 'val4'); $this->assertEquals('val4', $this->redis->lIndex('list', 3)); $this->assertEquals('val4', $this->redis->lIndex('list', -1)); } public function testlMove() { if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); $this->redis->del('{list}0', '{list}1'); $this->redis->lPush('{list}0', 'a'); $this->redis->lPush('{list}0', 'b'); $this->redis->lPush('{list}0', 'c'); $return = $this->redis->lMove('{list}0', '{list}1', Redis::LEFT, Redis::RIGHT); $this->assertEquals('c', $return); $return = $this->redis->lMove('{list}0', '{list}1', Redis::RIGHT, Redis::LEFT); $this->assertEquals('a', $return); $this->assertEquals(['b'], $this->redis->lRange('{list}0', 0, -1)); $this->assertEquals(['a', 'c'], $this->redis->lRange('{list}1', 0, -1)); } public function testBlmove() { if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); $this->redis->del('{list}0', '{list}1'); $this->redis->rpush('{list}0', 'a'); $this->assertEquals('a', $this->redis->blmove('{list}0', '{list}1', Redis::LEFT, Redis::LEFT, 1.0)); $st = microtime(true); $ret = $this->redis->blmove('{list}0', '{list}1', Redis::LEFT, Redis::LEFT, .1); $et = microtime(true); $this->assertEquals(false, $ret); $this->assertTrue($et - $st >= .1); } // lRem testing public function testlrem() { $this->redis->del('list'); $this->redis->lPush('list', 'a'); $this->redis->lPush('list', 'b'); $this->redis->lPush('list', 'c'); $this->redis->lPush('list', 'c'); $this->redis->lPush('list', 'b'); $this->redis->lPush('list', 'c'); // ['c', 'b', 'c', 'c', 'b', 'a'] $return = $this->redis->lrem('list', 'b', 2); // ['c', 'c', 'c', 'a'] $this->assertEquals(2, $return); $this->assertEquals('c', $this->redis->lIndex('list', 0)); $this->assertEquals('c', $this->redis->lIndex('list', 1)); $this->assertEquals('c', $this->redis->lIndex('list', 2)); $this->assertEquals('a', $this->redis->lIndex('list', 3)); $this->redis->del('list'); $this->redis->lPush('list', 'a'); $this->redis->lPush('list', 'b'); $this->redis->lPush('list', 'c'); $this->redis->lPush('list', 'c'); $this->redis->lPush('list', 'b'); $this->redis->lPush('list', 'c'); // ['c', 'b', 'c', 'c', 'b', 'a'] $this->redis->lrem('list', 'c', -2); // ['c', 'b', 'b', 'a'] $this->assertEquals(2, $return); $this->assertEquals('c', $this->redis->lIndex('list', 0)); $this->assertEquals('b', $this->redis->lIndex('list', 1)); $this->assertEquals('b', $this->redis->lIndex('list', 2)); $this->assertEquals('a', $this->redis->lIndex('list', 3)); // remove each element $this->assertEquals(1, $this->redis->lrem('list', 'a', 0)); $this->assertEquals(0, $this->redis->lrem('list', 'x', 0)); $this->assertEquals(2, $this->redis->lrem('list', 'b', 0)); $this->assertEquals(1, $this->redis->lrem('list', 'c', 0)); $this->assertEquals(FALSE, $this->redis->get('list')); $this->redis->set('list', 'actually not a list'); $this->assertEquals(FALSE, $this->redis->lrem('list', 'x')); } public function testsAdd() { $this->redis->del('set'); $this->assertEquals(1, $this->redis->sAdd('set', 'val')); $this->assertEquals(0, $this->redis->sAdd('set', 'val')); $this->assertTrue($this->redis->sismember('set', 'val')); $this->assertFalse($this->redis->sismember('set', 'val2')); $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); $this->assertTrue($this->redis->sismember('set', 'val2')); } public function testscard() { $this->redis->del('set'); $this->assertEquals(1, $this->redis->sAdd('set', 'val')); $this->assertEquals(1, $this->redis->scard('set')); $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); $this->assertEquals(2, $this->redis->scard('set')); } public function testsrem() { $this->redis->del('set'); $this->redis->sAdd('set', 'val'); $this->redis->sAdd('set', 'val2'); $this->redis->srem('set', 'val'); $this->assertEquals(1, $this->redis->scard('set')); $this->redis->srem('set', 'val2'); $this->assertEquals(0, $this->redis->scard('set')); } public function testsMove() { $this->redis->del('{set}0'); $this->redis->del('{set}1'); $this->redis->sAdd('{set}0', 'val'); $this->redis->sAdd('{set}0', 'val2'); $this->assertTrue($this->redis->sMove('{set}0', '{set}1', 'val')); $this->assertFalse($this->redis->sMove('{set}0', '{set}1', 'val')); $this->assertFalse($this->redis->sMove('{set}0', '{set}1', 'val-what')); $this->assertEquals(1, $this->redis->scard('{set}0')); $this->assertEquals(1, $this->redis->scard('{set}1')); $this->assertEquals(['val2'], $this->redis->smembers('{set}0')); $this->assertEquals(['val'], $this->redis->smembers('{set}1')); } public function testsPop() { $this->redis->del('set0'); $this->assertTrue($this->redis->sPop('set0') === FALSE); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); $v0 = $this->redis->sPop('set0'); $this->assertTrue(1 === $this->redis->scard('set0')); $this->assertTrue($v0 === 'val' || $v0 === 'val2'); $v1 = $this->redis->sPop('set0'); $this->assertTrue(0 === $this->redis->scard('set0')); $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); $this->assertTrue($this->redis->sPop('set0') === FALSE); } public function testsPopWithCount() { if (!$this->minVersionCheck("3.2")) { return $this->markTestSkipped(); } $set = 'set0'; $prefix = 'member'; $count = 5; /* Add a few members */ $this->redis->del($set); for ($i = 0; $i < $count; $i++) { $this->redis->sadd($set, $prefix.$i); } /* Pop them all */ $ret = $this->redis->sPop($set, $i); /* Make sure we got an arary and the count is right */ if ($this->assertTrue(is_array($ret)) && $this->assertTrue(count($ret) == $count)) { /* Probably overkill but validate the actual returned members */ for ($i = 0; $i < $count; $i++) { $this->assertTrue(in_array($prefix.$i, $ret)); } } } public function testsRandMember() { $this->redis->del('set0'); $this->assertTrue($this->redis->sRandMember('set0') === FALSE); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); $got = []; while(true) { $v = $this->redis->sRandMember('set0'); $this->assertTrue(2 === $this->redis->scard('set0')); // no change. $this->assertTrue($v === 'val' || $v === 'val2'); $got[$v] = $v; if(count($got) == 2) { break; } } // // With and without count, while serializing // $this->redis->del('set0'); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); for($i=0;$i<5;$i++) { $member = "member:$i"; $this->redis->sAdd('set0', $member); $mems[] = $member; } $member = $this->redis->srandmember('set0'); $this->assertTrue(in_array($member, $mems)); $rmembers = $this->redis->srandmember('set0', $i); foreach($rmembers as $reply_mem) { $this->assertTrue(in_array($reply_mem, $mems)); } $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); } public function testSRandMemberWithCount() { // Make sure the set is nuked $this->redis->del('set0'); // Run with a count (positive and negative) on an empty set $ret_pos = $this->redis->sRandMember('set0', 10); $ret_neg = $this->redis->sRandMember('set0', -10); // Should both be empty arrays $this->assertTrue(is_array($ret_pos) && empty($ret_pos)); $this->assertTrue(is_array($ret_neg) && empty($ret_neg)); // Add a few items to the set for($i=0;$i<100;$i++) { $this->redis->sadd('set0', "member$i"); } // Get less than the size of the list $ret_slice = $this->redis->srandmember('set0', 20); // Should be an array with 20 items $this->assertTrue(is_array($ret_slice) && count($ret_slice) == 20); // Ask for more items than are in the list (but with a positive count) $ret_slice = $this->redis->srandmember('set0', 200); // Should be an array, should be however big the set is, exactly $this->assertTrue(is_array($ret_slice) && count($ret_slice) == $i); // Now ask for too many items but negative $ret_slice = $this->redis->srandmember('set0', -200); // Should be an array, should have exactly the # of items we asked for (will be dups) $this->assertTrue(is_array($ret_slice) && count($ret_slice) == 200); // // Test in a pipeline // if ($this->havePipeline()) { $pipe = $this->redis->pipeline(); $pipe->srandmember('set0', 20); $pipe->srandmember('set0', 200); $pipe->srandmember('set0', -200); $ret = $this->redis->exec(); $this->assertTrue(is_array($ret[0]) && count($ret[0]) == 20); $this->assertTrue(is_array($ret[1]) && count($ret[1]) == $i); $this->assertTrue(is_array($ret[2]) && count($ret[2]) == 200); // Kill the set $this->redis->del('set0'); } } public function testsismember() { $this->redis->del('set'); $this->redis->sAdd('set', 'val'); $this->assertTrue($this->redis->sismember('set', 'val')); $this->assertFalse($this->redis->sismember('set', 'val2')); } public function testsmembers() { $this->redis->del('set'); $this->redis->sAdd('set', 'val'); $this->redis->sAdd('set', 'val2'); $this->redis->sAdd('set', 'val3'); $array = ['val', 'val2', 'val3']; $smembers = $this->redis->smembers('set'); sort($smembers); $this->assertEquals($array, $smembers); $sMembers = $this->redis->sMembers('set'); sort($sMembers); $this->assertEquals($array, $sMembers); // test alias } public function testsMisMember() { // Only available since 6.2.0 if (version_compare($this->version, '6.2.0') < 0) { $this->markTestSkipped(); return; } $this->redis->del('set'); $this->redis->sAdd('set', 'val'); $this->redis->sAdd('set', 'val2'); $this->redis->sAdd('set', 'val3'); $misMembers = $this->redis->sMisMember('set', 'val', 'notamember', 'val3'); $this->assertEquals([1, 0, 1], $misMembers); $misMembers = $this->redis->sMisMember('wrongkey', 'val', 'val2', 'val3'); $this->assertEquals([0, 0, 0], $misMembers); } public function testlSet() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); $this->assertEquals($this->redis->lIndex('list', 0), 'val3'); $this->assertEquals($this->redis->lIndex('list', 1), 'val2'); $this->assertEquals($this->redis->lIndex('list', 2), 'val'); $this->assertEquals(TRUE, $this->redis->lSet('list', 1, 'valx')); $this->assertEquals($this->redis->lIndex('list', 0), 'val3'); $this->assertEquals($this->redis->lIndex('list', 1), 'valx'); $this->assertEquals($this->redis->lIndex('list', 2), 'val'); } public function testsInter() { $this->redis->del('{set}odd'); // set of odd numbers $this->redis->del('{set}prime'); // set of prime numbers $this->redis->del('{set}square'); // set of squares $this->redis->del('{set}seq'); // set of numbers of the form n^2 - 1 $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; foreach($x as $i) { $this->redis->sAdd('{set}odd', $i); } $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}prime', $i); } $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}square', $i); } $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}seq', $i); } $xy = $this->redis->sInter('{set}odd', '{set}prime'); // odd prime numbers foreach($xy as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); } $xy = $this->redis->sInter(['{set}odd', '{set}prime']); // odd prime numbers, as array. foreach($xy as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); } $yz = $this->redis->sInter('{set}prime', '{set}square'); // set of prime squares foreach($yz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($y, $z))); } $yz = $this->redis->sInter(['{set}prime', '{set}square']); // set of odd squares, as array foreach($yz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($y, $z))); } $zt = $this->redis->sInter('{set}square', '{set}seq'); // prime squares $this->assertTrue($zt === []); $zt = $this->redis->sInter(['{set}square', '{set}seq']); // prime squares, as array $this->assertTrue($zt === []); $xyz = $this->redis->sInter('{set}odd', '{set}prime', '{set}square');// odd prime squares $this->assertTrue($xyz === ['1']); $xyz = $this->redis->sInter(['{set}odd', '{set}prime', '{set}square']);// odd prime squares, with an array as a parameter $this->assertTrue($xyz === ['1']); $nil = $this->redis->sInter([]); $this->assertTrue($nil === FALSE); } public function testsInterStore() { $this->redis->del('{set}x'); // set of odd numbers $this->redis->del('{set}y'); // set of prime numbers $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; foreach($x as $i) { $this->redis->sAdd('{set}x', $i); } $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } /* Regression test for passing a single array */ $this->assertEquals($this->redis->sInterStore(['{set}k', '{set}x', '{set}y']), count(array_intersect($x,$y))); $count = $this->redis->sInterStore('{set}k', '{set}x', '{set}y'); // odd prime numbers $this->assertEquals($count, $this->redis->scard('{set}k')); foreach(array_intersect($x, $y) as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sInterStore('{set}k', '{set}y', '{set}z'); // set of odd squares $this->assertEquals($count, $this->redis->scard('{set}k')); foreach(array_intersect($y, $z) as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sInterStore('{set}k', '{set}z', '{set}t'); // squares of the form n^2 + 1 $this->assertEquals($count, 0); $this->assertEquals($count, $this->redis->scard('{set}k')); $this->redis->del('{set}z'); $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // only z missing, expect 0. $this->assertTrue($xyz === 0); $this->redis->del('{set}y'); $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // y and z missing, expect 0. $this->assertTrue($xyz === 0); $this->redis->del('{set}x'); $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // x y and z ALL missing, expect 0. $this->assertTrue($xyz === 0); } public function testsUnion() { $this->redis->del('{set}x'); // set of odd numbers $this->redis->del('{set}y'); // set of prime numbers $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; foreach($x as $i) { $this->redis->sAdd('{set}x', $i); } $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } $xy = $this->redis->sUnion('{set}x', '{set}y'); // x U y foreach($xy as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_merge($x, $y))); } $yz = $this->redis->sUnion('{set}y', '{set}z'); // y U Z foreach($yz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_merge($y, $z))); } $zt = $this->redis->sUnion('{set}z', '{set}t'); // z U t foreach($zt as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_merge($z, $t))); } $xyz = $this->redis->sUnion('{set}x', '{set}y', '{set}z'); // x U y U z foreach($xyz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_merge($x, $y, $z))); } } public function testsUnionStore() { $this->redis->del('{set}x'); // set of odd numbers $this->redis->del('{set}y'); // set of prime numbers $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; foreach($x as $i) { $this->redis->sAdd('{set}x', $i); } $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y'); // x U y $xy = array_unique(array_merge($x, $y)); $this->assertEquals($count, count($xy)); foreach($xy as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sUnionStore('{set}k', '{set}y', '{set}z'); // y U z $yz = array_unique(array_merge($y, $z)); $this->assertEquals($count, count($yz)); foreach($yz as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sUnionStore('{set}k', '{set}z', '{set}t'); // z U t $zt = array_unique(array_merge($z, $t)); $this->assertEquals($count, count($zt)); foreach($zt as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z $xyz = array_unique(array_merge($x, $y, $z)); $this->assertEquals($count, count($xyz)); foreach($xyz as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } $this->redis->del('{set}x'); // x missing now $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z $this->assertTrue($count === count(array_unique(array_merge($y, $z)))); $this->redis->del('{set}y'); // x and y missing $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z $this->assertTrue($count === count(array_unique($z))); $this->redis->del('{set}z'); // x, y, and z ALL missing $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z $this->assertTrue($count === 0); } public function testsDiff() { $this->redis->del('{set}x'); // set of odd numbers $this->redis->del('{set}y'); // set of prime numbers $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; foreach($x as $i) { $this->redis->sAdd('{set}x', $i); } $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } $xy = $this->redis->sDiff('{set}x', '{set}y'); // x U y foreach($xy as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_diff($x, $y))); } $yz = $this->redis->sDiff('{set}y', '{set}z'); // y U Z foreach($yz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_diff($y, $z))); } $zt = $this->redis->sDiff('{set}z', '{set}t'); // z U t foreach($zt as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_diff($z, $t))); } $xyz = $this->redis->sDiff('{set}x', '{set}y', '{set}z'); // x U y U z foreach($xyz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_diff($x, $y, $z))); } } public function testsDiffStore() { $this->redis->del('{set}x'); // set of odd numbers $this->redis->del('{set}y'); // set of prime numbers $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; foreach($x as $i) { $this->redis->sAdd('{set}x', $i); } $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y'); // x - y $xy = array_unique(array_diff($x, $y)); $this->assertEquals($count, count($xy)); foreach($xy as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sDiffStore('{set}k', '{set}y', '{set}z'); // y - z $yz = array_unique(array_diff($y, $z)); $this->assertEquals($count, count($yz)); foreach($yz as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sDiffStore('{set}k', '{set}z', '{set}t'); // z - t $zt = array_unique(array_diff($z, $t)); $this->assertEquals($count, count($zt)); foreach($zt as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z'); // x - y - z $xyz = array_unique(array_diff($x, $y, $z)); $this->assertEquals($count, count($xyz)); foreach($xyz as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } $this->redis->del('{set}x'); // x missing now $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z'); // x - y - z $this->assertTrue($count === 0); $this->redis->del('{set}y'); // x and y missing $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z'); // x - y - z $this->assertTrue($count === 0); $this->redis->del('{set}z'); // x, y, and z ALL missing $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z'); // x - y - z $this->assertTrue($count === 0); } public function testInterCard() { if(version_compare($this->version, "7.0.0") < 0) { $this->markTestSkipped(); } $set_data = [ ['aardvark', 'dog', 'fish', 'squirrel', 'tiger'], ['bear', 'coyote', 'fish', 'gorilla', 'dog'] ]; $ssets = $zsets = []; foreach ($set_data as $n => $values) { $sset = "s{set}:$n"; $zset = "z{set}:$n"; $this->redis->del([$sset, $zset]); $ssets[] = $sset; $zsets[] = $zset; foreach ($values as $score => $value) { $this->assertEquals(1, $this->redis->sAdd("s{set}:$n", $value)); $this->assertEquals(1, $this->redis->zAdd("z{set}:$n", $score, $value)); } } $exp = count(array_intersect(...$set_data)); $act = $this->redis->sintercard($ssets); $this->assertEquals($exp, $act); $act = $this->redis->zintercard($zsets); $this->assertEquals($exp, $act); $this->assertEquals(1, $this->redis->sintercard($ssets, 1)); $this->assertEquals(2, $this->redis->sintercard($ssets, 2)); $this->assertEquals(1, $this->redis->zintercard($zsets, 1)); $this->assertEquals(2, $this->redis->zintercard($zsets, 2)); $this->assertFalse(@$this->redis->sintercard($ssets, -1)); $this->assertFalse(@$this->redis->zintercard($ssets, -1)); $this->assertFalse(@$this->redis->sintercard([])); $this->assertFalse(@$this->redis->zintercard([])); $this->redis->del(array_merge($ssets, $zsets)); } public function testlrange() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); // pos : 0 1 2 // pos : -3 -2 -1 // list: [val3, val2, val] $this->assertEquals($this->redis->lrange('list', 0, 0), ['val3']); $this->assertEquals($this->redis->lrange('list', 0, 1), ['val3', 'val2']); $this->assertEquals($this->redis->lrange('list', 0, 2), ['val3', 'val2', 'val']); $this->assertEquals($this->redis->lrange('list', 0, 3), ['val3', 'val2', 'val']); $this->assertEquals($this->redis->lrange('list', 0, -1), ['val3', 'val2', 'val']); $this->assertEquals($this->redis->lrange('list', 0, -2), ['val3', 'val2']); $this->assertEquals($this->redis->lrange('list', -2, -1), ['val2', 'val']); $this->redis->del('list'); $this->assertEquals($this->redis->lrange('list', 0, -1), []); } public function testdbSize() { $this->assertTrue($this->redis->flushDB()); $this->redis->set('x', 'y'); $this->assertTrue($this->redis->dbSize() === 1); } public function testFlushDB() { $this->assertTrue($this->redis->flushdb()); $this->assertTrue($this->redis->flushdb(NULL)); $this->assertTrue($this->redis->flushdb(false)); $this->assertTrue($this->redis->flushdb(true)); } public function testttl() { $this->redis->set('x', 'y'); $this->redis->expire('x', 5); $ttl = $this->redis->ttl('x'); $this->assertTrue($ttl > 0 && $ttl <= 5); // A key with no TTL $this->redis->del('x'); $this->redis->set('x', 'bar'); $this->assertEquals($this->redis->ttl('x'), -1); // A key that doesn't exist (> 2.8 will return -2) if(version_compare($this->version, "2.8.0") >= 0) { $this->redis->del('x'); $this->assertEquals($this->redis->ttl('x'), -2); } } public function testPersist() { $this->redis->set('x', 'y'); $this->redis->expire('x', 100); $this->assertTrue(TRUE === $this->redis->persist('x')); // true if there is a timeout $this->assertTrue(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout $this->redis->del('x'); $this->assertTrue(FALSE === $this->redis->persist('x')); // false if the key doesn’t exist. } public function testClient() { /* CLIENT SETNAME */ $this->assertTrue($this->redis->client('setname', 'phpredis_unit_tests')); /* CLIENT LIST */ $arr_clients = $this->redis->client('list'); $this->assertTrue(is_array($arr_clients)); // Figure out which ip:port is us! $str_addr = NULL; foreach($arr_clients as $arr_client) { if($arr_client['name'] == 'phpredis_unit_tests') { $str_addr = $arr_client['addr']; } } // We should have found our connection $this->assertFalse(empty($str_addr)); /* CLIENT GETNAME */ $this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests'); if (version_compare($this->version, '5.0.0') >= 0) { $this->assertLess(0, $this->redis->client('id')); if (version_compare($this->version, '6.0.0') >= 0) { $this->assertEquals($this->redis->client('getredir'), -1); $this->assertTrue($this->redis->client('tracking', 'on', ['optin' => true])); $this->assertEquals($this->redis->client('getredir'), 0); $this->assertTrue($this->redis->client('caching', 'yes')); $this->assertTrue($this->redis->client('tracking', 'off')); if (version_compare($this->version, '6.2.0') >= 0) { $this->assertFalse(empty($this->redis->client('info'))); $this->assertEquals($this->redis->client('trackinginfo'), [ 'flags' => ['off'], 'redirect' => -1, 'prefixes' => [], ]); if (version_compare($this->version, '7.0.0') >= 0) { $this->assertTrue($this->redis->client('no-evict', 'on')); } } } } /* CLIENT KILL -- phpredis will reconnect, so we can do this */ $this->assertTrue($this->redis->client('kill', $str_addr)); } public function testSlowlog() { // We don't really know what's going to be in the slowlog, but make sure // the command returns proper types when called in various ways $this->assertTrue(is_array($this->redis->slowlog('get'))); $this->assertTrue(is_array($this->redis->slowlog('get', 10))); $this->assertTrue(is_int($this->redis->slowlog('len'))); $this->assertTrue($this->redis->slowlog('reset')); $this->assertFalse(@$this->redis->slowlog('notvalid')); } public function testWait() { // Closest we can check based on redis commmit history if(version_compare($this->version, '2.9.11') < 0) { $this->markTestSkipped(); return; } // We could have slaves here, so determine that $arr_slaves = $this->redis->info(); $i_slaves = $arr_slaves['connected_slaves']; // Send a couple commands $this->redis->set('wait-foo', 'over9000'); $this->redis->set('wait-bar', 'revo9000'); // Make sure we get the right replication count $this->assertEquals($this->redis->wait($i_slaves, 100), $i_slaves); // Pass more slaves than are connected $this->redis->set('wait-foo','over9000'); $this->redis->set('wait-bar','revo9000'); $this->assertTrue($this->redis->wait($i_slaves+1, 100) < $i_slaves+1); // Make sure when we pass with bad arguments we just get back false $this->assertFalse($this->redis->wait(-1, -1)); $this->assertFalse($this->redis->wait(-1, 20)); } public function testInfo() { foreach ([false, true] as $boo_multi) { if ($boo_multi) { $this->redis->multi(); $this->redis->info(); $info = $this->redis->exec(); $info = $info[0]; } else { $info = $this->redis->info(); } $keys = [ "redis_version", "arch_bits", "uptime_in_seconds", "uptime_in_days", "connected_clients", "connected_slaves", "used_memory", "total_connections_received", "total_commands_processed", "role" ]; if (version_compare($this->version, "2.5.0") < 0) { array_push($keys, "changes_since_last_save", "bgsave_in_progress", "last_save_time" ); } else { array_push($keys, "rdb_changes_since_last_save", "rdb_bgsave_in_progress", "rdb_last_save_time" ); } foreach($keys as $k) { $this->assertTrue(in_array($k, array_keys($info))); } } if (!$this->minVersionCheck("7.0.0")) return; $res = $this->redis->info('server', 'memory'); $this->assertTrue(is_array($res) && isset($res['redis_version']) && isset($res['used_memory'])); } public function testInfoCommandStats() { // INFO COMMANDSTATS is new in 2.6.0 if (version_compare($this->version, "2.5.0") < 0) { $this->markTestSkipped(); } $info = $this->redis->info("COMMANDSTATS"); $this->assertTrue(is_array($info)); if (is_array($info)) { foreach($info as $k => $value) { $this->assertTrue(strpos($k, 'cmdstat_') !== false); } } } public function testSelect() { $this->assertFalse($this->redis->select(-1)); $this->assertTrue($this->redis->select(0)); } public function testSwapDB() { if (version_compare($this->version, "4.0.0") < 0) { $this->markTestSkipped(); } $this->assertTrue($this->redis->swapdb(0, 1)); $this->assertTrue($this->redis->swapdb(0, 1)); } public function testMset() { $this->redis->del('x', 'y', 'z'); // remove x y z $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']); // check x y z $this->redis->del('x'); // delete just x $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']); // check x y z $this->assertFalse($this->redis->mset([])); // set ø → FALSE /* * Integer keys */ // No prefix $set_array = [-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three']; $this->redis->del(array_keys($set_array)); $this->assertTrue($this->redis->mset($set_array)); $this->assertEquals($this->redis->mget(array_keys($set_array)), array_values($set_array)); $this->redis->del(array_keys($set_array)); // With a prefix $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:'); $this->redis->del(array_keys($set_array)); $this->assertTrue($this->redis->mset($set_array)); $this->assertEquals($this->redis->mget(array_keys($set_array)), array_values($set_array)); $this->redis->del(array_keys($set_array)); $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testMsetNX() { $this->redis->del('x', 'y', 'z'); // remove x y z $this->assertTrue(TRUE === $this->redis->msetnx(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']); // check x y z $this->redis->del('x'); // delete just x $this->assertTrue(FALSE === $this->redis->msetnx(['x' => 'A', 'y' => 'B', 'z' => 'C'])); // set x y z $this->assertEquals($this->redis->mget(['x', 'y', 'z']), [FALSE, 'b', 'c']); // check x y z $this->assertFalse($this->redis->msetnx([])); // set ø → FALSE } public function testRpopLpush() { // standard case. $this->redis->del('{list}x', '{list}y'); $this->redis->lpush('{list}x', 'abc'); $this->redis->lpush('{list}x', 'def'); // x = [def, abc] $this->redis->lpush('{list}y', '123'); $this->redis->lpush('{list}y', '456'); // y = [456, 123] $this->assertEquals($this->redis->rpoplpush('{list}x', '{list}y'), 'abc'); // we RPOP x, yielding abc. $this->assertEquals($this->redis->lrange('{list}x', 0, -1), ['def']); // only def remains in x. $this->assertEquals($this->redis->lrange('{list}y', 0, -1), ['abc', '456', '123']); // abc has been lpushed to y. // with an empty source, expecting no change. $this->redis->del('{list}x', '{list}y'); $this->assertTrue(FALSE === $this->redis->rpoplpush('{list}x', '{list}y')); $this->assertTrue([] === $this->redis->lrange('{list}x', 0, -1)); $this->assertTrue([] === $this->redis->lrange('{list}y', 0, -1)); } public function testBRpopLpush() { // standard case. $this->redis->del('{list}x', '{list}y'); $this->redis->lpush('{list}x', 'abc'); $this->redis->lpush('{list}x', 'def'); // x = [def, abc] $this->redis->lpush('{list}y', '123'); $this->redis->lpush('{list}y', '456'); // y = [456, 123] $this->assertEquals($this->redis->brpoplpush('{list}x', '{list}y', 1), 'abc'); // we RPOP x, yielding abc. $this->assertEquals($this->redis->lrange('{list}x', 0, -1), ['def']); // only def remains in x. $this->assertEquals($this->redis->lrange('{list}y', 0, -1), ['abc', '456', '123']); // abc has been lpushed to y. // with an empty source, expecting no change. $this->redis->del('{list}x', '{list}y'); $this->assertTrue(FALSE === $this->redis->brpoplpush('{list}x', '{list}y', 1)); $this->assertTrue([] === $this->redis->lrange('{list}x', 0, -1)); $this->assertTrue([] === $this->redis->lrange('{list}y', 0, -1)); if (!$this->minVersionCheck('6.0.0')) return; // Redis >= 6.0.0 allows floating point timeouts $st = microtime(true); $this->assertEquals(FALSE, $this->redis->brpoplpush('{list}x', '{list}y', .1)); $et = microtime(true); $this->assertTrue($et - $st < 1.0); } public function testZAddFirstArg() { $this->redis->del('key'); $zsetName = 100; // not a string! $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1')); $this->assertTrue(['val0', 'val1'] === $this->redis->zRange($zsetName, 0, -1)); } public function testZaddIncr() { $this->redis->del('zset'); $this->assertEquals(10.0, $this->redis->zAdd('zset', ['incr'], 10, 'value')); $this->assertEquals(20.0, $this->redis->zAdd('zset', ['incr'], 10, 'value')); $this->assertFalse($this->redis->zAdd('zset', ['incr'], 10, 'value', 20, 'value2')); } public function testZX() { $this->redis->del('key'); $this->assertTrue([] === $this->redis->zRange('key', 0, -1)); $this->assertTrue([] === $this->redis->zRange('key', 0, -1, true)); $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters if (version_compare($this->version, "3.0.2") < 0) { $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); } else { $this->assertTrue(1 === $this->redis->zAdd('key', [], 1, 'val1')); // empty options $this->assertTrue(1 === $this->redis->zAdd('key', ['nx'], 3, 'val3')); // nx option $this->assertTrue(0 === $this->redis->zAdd('key', ['xx'], 3, 'val3')); // xx option if (version_compare($this->version, "6.2.0") >= 0) { $this->assertTrue(0 === $this->redis->zAdd('key', ['lt'], 4, 'val3')); // lt option $this->assertTrue(0 === $this->redis->zAdd('key', ['gt'], 2, 'val3')); // gt option } } $this->assertTrue(['val0', 'val1', 'val2', 'val3', 'val4', 'val5'] === $this->redis->zRange('key', 0, -1)); // withscores $ret = $this->redis->zRange('key', 0, -1, true); $this->assertTrue(count($ret) == 6); $this->assertTrue($ret['val0'] == 0); $this->assertTrue($ret['val1'] == 1); $this->assertTrue($ret['val2'] == 2); $this->assertTrue($ret['val3'] == 3); $this->assertTrue($ret['val4'] == 4); $this->assertTrue($ret['val5'] == 5); $this->assertTrue(0 === $this->redis->zRem('key', 'valX')); $this->assertTrue(1 === $this->redis->zRem('key', 'val3')); $this->assertTrue(1 === $this->redis->zRem('key', 'val4')); $this->assertTrue(1 === $this->redis->zRem('key', 'val5')); $this->assertTrue(['val0', 'val1', 'val2'] === $this->redis->zRange('key', 0, -1)); // zGetReverseRange $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); $this->assertTrue(['val0', 'val1', 'val2', 'aal3', 'val3'] === $zero_to_three || ['val0', 'val1', 'val2', 'val3', 'aal3'] === $zero_to_three); $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); $this->assertTrue(array_reverse(['val0', 'val1', 'val2', 'aal3', 'val3']) === $three_to_zero || array_reverse(['val0', 'val1', 'val2', 'val3', 'aal3']) === $three_to_zero); $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); // withscores $this->redis->zRem('key', 'aal3'); $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]); $this->assertTrue(['val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3] == $zero_to_three); $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); // limit $this->assertTrue(['val0'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 1]])); $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 2]])); $this->assertTrue(['val1', 'val2'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]])); $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]])); if ($this->minVersionCheck('6.2.0')) $this->assertEquals(['val0', 'val1'], $this->redis->zrange('key', 0, 1, ['byscore', 'limit' => [0, 100]])); // limits as references $limit = [0, 100]; foreach ($limit as &$val) {} $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => $limit])); $this->assertTrue(['val3'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 1]])); $this->assertTrue(['val3', 'val2'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 2]])); $this->assertTrue(['val2', 'val1'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]])); $this->assertTrue(['val1', 'val0'] === $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]])); if ($this->minVersionCheck('6.2.0')) { $this->assertEquals(['val1', 'val0'], $this->redis->zrange('key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); $this->assertEquals(2, $this->redis->zrangestore('dst{key}', 'key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); $this->assertEquals(['val0', 'val1'], $this->redis->zRange('dst{key}', 0, -1)); $this->assertEquals(1, $this->redis->zrangestore('dst{key}', 'key', 1, 0, ['byscore', 'rev', 'limit' => [0, 1]])); $this->assertEquals(['val1'], $this->redis->zrange('dst{key}', 0, -1)); } $this->assertTrue(4 === $this->redis->zCard('key')); $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); $this->assertFalse($this->redis->zScore('key', 'val')); $this->assertFalse($this->redis->zScore(3, 2)); // with () and +inf, -inf $this->redis->del('zset'); $this->redis->zAdd('zset', 1, 'foo'); $this->redis->zAdd('zset', 2, 'bar'); $this->redis->zAdd('zset', 3, 'biz'); $this->redis->zAdd('zset', 4, 'foz'); $this->assertTrue(['foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4] == $this->redis->zRangeByScore('zset', '-inf', '+inf', ['withscores' => TRUE])); $this->assertTrue(['foo' => 1, 'bar' => 2] == $this->redis->zRangeByScore('zset', 1, 2, ['withscores' => TRUE])); $this->assertTrue(['bar' => 2] == $this->redis->zRangeByScore('zset', '(1', 2, ['withscores' => TRUE])); $this->assertTrue([] == $this->redis->zRangeByScore('zset', '(1', '(2', ['withscores' => TRUE])); $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2')); // zincrby $this->redis->del('key'); $this->assertTrue(1.0 === $this->redis->zIncrBy('key', 1, 'val1')); $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); $this->assertTrue(2.5 === $this->redis->zIncrBy('key', 1.5, 'val1')); $this->assertTrue(2.5 === $this->redis->zScore('key', 'val1')); // zUnionStore $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); $this->redis->del('{zset}3'); $this->redis->del('{zset}U'); $this->redis->zAdd('{zset}1', 0, 'val0'); $this->redis->zAdd('{zset}1', 1, 'val1'); $this->redis->zAdd('{zset}2', 2, 'val2'); $this->redis->zAdd('{zset}2', 3, 'val3'); $this->redis->zAdd('{zset}3', 4, 'val4'); $this->redis->zAdd('{zset}3', 5, 'val5'); $this->assertTrue(4 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}3'])); $this->assertTrue(['val0', 'val1', 'val4', 'val5'] === $this->redis->zRange('{zset}U', 0, -1)); // Union on non existing keys $this->redis->del('{zset}U'); $this->assertTrue(0 === $this->redis->zUnionStore('{zset}U', ['{zset}X', '{zset}Y'])); $this->assertTrue([] === $this->redis->zRange('{zset}U', 0, -1)); // !Exist U Exist → copy of existing zset. $this->redis->del('{zset}U', 'X'); $this->assertTrue(2 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}X'])); // test weighted zUnion $this->redis->del('{zset}Z'); $this->assertEquals(4, $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [1, 1])); $this->assertTrue(['val0', 'val1', 'val2', 'val3'] === $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->zRemRangeByScore('{zset}Z', 0, 10); $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [5, 1])); $this->assertTrue(['val0', 'val2', 'val3', 'val1'] === $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); $this->redis->del('{zset}3'); //test zUnion with weights and aggegration function $this->redis->zadd('{zset}1', 1, 'duplicate'); $this->redis->zadd('{zset}2', 2, 'duplicate'); $this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], [1,1], 'MIN'); $this->assertTrue($this->redis->zScore('{zset}U', 'duplicate')===1.0); $this->redis->del('{zset}U'); //now test zUnion *without* weights but with aggregrate function $this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], null, 'MIN'); $this->assertTrue($this->redis->zScore('{zset}U', 'duplicate')===1.0); $this->redis->del('{zset}U', '{zset}1', '{zset}2'); // test integer and float weights (GitHub issue #109). $this->redis->del('{zset}1', '{zset}2', '{zset}3'); $this->redis->zadd('{zset}1', 1, 'one'); $this->redis->zadd('{zset}1', 2, 'two'); $this->redis->zadd('{zset}2', 1, 'one'); $this->redis->zadd('{zset}2', 2, 'two'); $this->redis->zadd('{zset}2', 3, 'three'); $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [2, 3.0]) === 3); $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); $this->redis->del('{zset}3'); // Test 'inf', '-inf', and '+inf' weights (GitHub issue #336) $this->redis->zadd('{zset}1', 1, 'one', 2, 'two', 3, 'three'); $this->redis->zadd('{zset}2', 3, 'three', 4, 'four', 5, 'five'); // Make sure phpredis handles these weights $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, 'inf']) === 5); $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '-inf']) === 5); $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '+inf']) === 5); // Now, confirm that they're being sent, and that it works $arr_weights = ['inf','-inf','+inf']; foreach($arr_weights as $str_weight) { $r = $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1,$str_weight]); $this->assertTrue($r===5); $r = $this->redis->zrangebyscore('{zset}3', '(-inf', '(inf',['withscores'=>true]); $this->assertTrue(count($r)===2); $this->assertTrue(isset($r['one'])); $this->assertTrue(isset($r['two'])); } $this->redis->del('{zset}1','{zset}2','{zset}3'); $this->redis->zadd('{zset}1', 2000.1, 'one'); $this->redis->zadd('{zset}1', 3000.1, 'two'); $this->redis->zadd('{zset}1', 4000.1, 'three'); $ret = $this->redis->zRange('{zset}1', 0, -1, TRUE); $this->assertTrue(count($ret) === 3); $retValues = array_keys($ret); $this->assertTrue(['one', 'two', 'three'] === $retValues); // + 0 converts from string to float OR integer $this->assertTrue(is_float($ret['one'] + 0)); $this->assertTrue(is_float($ret['two'] + 0)); $this->assertTrue(is_float($ret['three'] + 0)); $this->redis->del('{zset}1'); // ZREMRANGEBYRANK $this->redis->zAdd('{zset}1', 1, 'one'); $this->redis->zAdd('{zset}1', 2, 'two'); $this->redis->zAdd('{zset}1', 3, 'three'); $this->assertTrue(2 === $this->redis->zremrangebyrank('{zset}1', 0, 1)); $this->assertTrue(['three' => 3] == $this->redis->zRange('{zset}1', 0, -1, TRUE)); $this->redis->del('{zset}1'); // zInterStore $this->redis->zAdd('{zset}1', 0, 'val0'); $this->redis->zAdd('{zset}1', 1, 'val1'); $this->redis->zAdd('{zset}1', 3, 'val3'); $this->redis->zAdd('{zset}2', 2, 'val1'); $this->redis->zAdd('{zset}2', 3, 'val3'); $this->redis->zAdd('{zset}3', 4, 'val3'); $this->redis->zAdd('{zset}3', 5, 'val5'); $this->redis->del('{zset}I'); $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'])); $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); // Union on non existing keys $this->assertTrue(0 === $this->redis->zInterStore('{zset}X', ['{zset}X', '{zset}Y'])); $this->assertTrue([] === $this->redis->zRange('{zset}X', 0, -1)); // !Exist U Exist $this->assertTrue(0 === $this->redis->zInterStore('{zset}Y', ['{zset}1', '{zset}X'])); $this->assertTrue([] === $this->redis->zRange('keyY', 0, -1)); // test weighted zInterStore $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); $this->redis->del('{zset}3'); $this->redis->zAdd('{zset}1', 0, 'val0'); $this->redis->zAdd('{zset}1', 1, 'val1'); $this->redis->zAdd('{zset}1', 3, 'val3'); $this->redis->zAdd('{zset}2', 2, 'val1'); $this->redis->zAdd('{zset}2', 1, 'val3'); $this->redis->zAdd('{zset}3', 7, 'val1'); $this->redis->zAdd('{zset}3', 3, 'val3'); $this->redis->del('{zset}I'); $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'], [1, 1])); $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'min')); $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'max')); $this->assertTrue(['val3', 'val1'] === $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], null, 'max')); $this->assertTrue($this->redis->zScore('{zset}I', 'val1') === floatval(7)); // zrank, zrevrank $this->redis->del('z'); $this->redis->zadd('z', 1, 'one'); $this->redis->zadd('z', 2, 'two'); $this->redis->zadd('z', 5, 'five'); $this->assertTrue(0 === $this->redis->zRank('z', 'one')); $this->assertTrue(1 === $this->redis->zRank('z', 'two')); $this->assertTrue(2 === $this->redis->zRank('z', 'five')); $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); } public function testZRangeScoreArg() { $this->redis->del('{z}'); $arr_mems = ['one' => 1.0, 'two' => 2.0, 'three' => 3.0]; foreach ($arr_mems as $str_mem => $score) { $this->redis->zAdd('{z}', $score, $str_mem); } /* Verify we can pass true and ['withscores' => true] */ $this->assertEquals($arr_mems, $this->redis->zRange('{z}', 0, -1, true)); $this->assertEquals($arr_mems, $this->redis->zRange('{z}', 0, -1, ['withscores' => true])); } public function testZRangeByLex() { /* ZRANGEBYLEX available on versions >= 2.8.9 */ if(version_compare($this->version, "2.8.9") < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('key'); foreach(range('a', 'g') as $c) { $this->redis->zAdd('key', 0, $c); } $this->assertEquals(['a', 'b', 'c'], $this->redis->zRangeByLex('key', '-', '[c')); $this->assertEquals(['f', 'g'], $this->redis->zRangeByLex('key', '(e', '+')); // with limit offset $this->assertEquals(['b', 'c'], $this->redis->zRangeByLex('key', '-', '[c', 1, 2) ); $this->assertEquals(['b'], $this->redis->zRangeByLex('key', '-', '(c', 1, 2)); /* Test getting the same functionality via ZRANGE and options */ if ($this->minVersionCheck("6.2.0")) { $this->assertEquals(['a','b','c'], $this->redis->zRange('key', '-', '[c', ['BYLEX'])); $this->assertEquals(['b', 'c'], $this->redis->zRange('key', '-', '[c', ['BYLEX', 'LIMIT' => [1, 2]])); $this->assertEquals(['b'], $this->redis->zRange('key', '-', '(c', ['BYLEX', 'LIMIT' => [1, 2]])); $this->assertEquals(['b', 'a'], $this->redis->zRange('key', '[c', '-', ['BYLEX', 'REV', 'LIMIT' => [1, 2]])); } } public function testZLexCount() { if (version_compare($this->version, "2.8.9") < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('key'); foreach (range('a', 'g') as $c) { $entries[] = $c; $this->redis->zAdd('key', 0, $c); } /* Special -/+ values */ $this->assertEquals($this->redis->zLexCount('key', '-', '-'), 0); $this->assertEquals($this->redis->zLexCount('key', '-', '+'), count($entries)); /* Verify invalid arguments return FALSE */ $this->assertFalse(@$this->redis->zLexCount('key', '[a', 'bad')); $this->assertFalse(@$this->redis->zLexCount('key', 'bad', '[a')); /* Now iterate through */ $start = $entries[0]; for ($i = 1; $i < count($entries); $i++) { $end = $entries[$i]; $this->assertEquals($this->redis->zLexCount('key', "[$start", "[$end"), $i + 1); $this->assertEquals($this->redis->zLexCount('key', "[$start", "($end"), $i); $this->assertEquals($this->redis->zLexCount('key', "($start", "($end"), $i - 1); } } public function testzDiff() { // Only available since 6.2.0 if (version_compare($this->version, '6.2.0') < 0) { $this->markTestSkipped(); return; } $this->redis->del('key'); foreach (range('a', 'c') as $c) { $this->redis->zAdd('key', 1, $c); } $this->assertEquals(['a', 'b', 'c'], $this->redis->zDiff(['key'])); $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zDiff(['key'], ['withscores' => true])); } public function testzInter() { // Only available since 6.2.0 if (version_compare($this->version, '6.2.0') < 0) { $this->markTestSkipped(); return; } $this->redis->del('key'); foreach (range('a', 'c') as $c) { $this->redis->zAdd('key', 1, $c); } $this->assertEquals(['a', 'b', 'c'], $this->redis->zInter(['key'])); $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zInter(['key'], null, ['withscores' => true])); } public function testzUnion() { // Only available since 6.2.0 if (version_compare($this->version, '6.2.0') < 0) { $this->markTestSkipped(); return; } $this->redis->del('key'); foreach (range('a', 'c') as $c) { $this->redis->zAdd('key', 1, $c); } $this->assertEquals(['a', 'b', 'c'], $this->redis->zUnion(['key'])); $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zUnion(['key'], null, ['withscores' => true])); } public function testzDiffStore() { // Only available since 6.2.0 if (version_compare($this->version, '6.2.0') < 0) { $this->markTestSkipped(); return; } $this->redis->del('{zkey}src'); foreach (range('a', 'c') as $c) { $this->redis->zAdd('{zkey}src', 1, $c); } $this->assertEquals(3, $this->redis->zDiffStore('{zkey}dst', ['{zkey}src'])); $this->assertEquals(['a', 'b', 'c'], $this->redis->zRange('{zkey}dst', 0, -1)); } public function testzMscore() { // Only available since 6.2.0 if (version_compare($this->version, '6.2.0') < 0) { $this->markTestSkipped(); return; } $this->redis->del('key'); foreach (range('a', 'c') as $c) { $this->redis->zAdd('key', 1, $c); } $scores = $this->redis->zMscore('key', 'a', 'notamember', 'c'); $this->assertEquals([1.0, false, 1.0], $scores); $scores = $this->redis->zMscore('wrongkey', 'a', 'b', 'c'); $this->assertEquals([false, false, false], $scores); } public function testZRemRangeByLex() { if (version_compare($this->version, "2.8.9") < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c'); $this->assertEquals($this->redis->zRemRangeByLex('key', '-', '+'), 3); $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c'); $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 3); $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c'); $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '(a'), 0); $this->assertEquals($this->redis->zRemRangeByLex('key', '(a', '(c'), 1); $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 2); } public function testBZPop() { if (version_compare($this->version, "5.0.0") < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('{zs}1', '{zs}2'); $this->redis->zAdd('{zs}1', 0, 'a', 1, 'b', 2, 'c'); $this->redis->zAdd('{zs}2', 3, 'A', 4, 'B', 5, 'D'); $this->assertEquals(Array('{zs}1', 'a', '0'), $this->redis->bzPopMin('{zs}1', '{zs}2', 0)); $this->assertEquals(Array('{zs}1', 'c', '2'), $this->redis->bzPopMax(Array('{zs}1', '{zs}2'), 0)); $this->assertEquals(Array('{zs}2', 'A', '3'), $this->redis->bzPopMin('{zs}2', '{zs}1', 0)); /* Verify timeout is being sent */ $this->redis->del('{zs}1', '{zs}2'); $st = microtime(true) * 1000; $this->redis->bzPopMin('{zs}1', '{zs}2', 1); $et = microtime(true) * 1000; $this->assertTrue($et - $st > 100); } public function testZPop() { if (version_compare($this->version, "5.0.0") < 0) { $this->MarkTestSkipped(); return; } // zPopMax and zPopMin without a COUNT argument $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e'); $this->assertTrue(array('e' => 4.0) === $this->redis->zPopMax('key')); $this->assertTrue(array('a' => 0.0) === $this->redis->zPopMin('key')); // zPopMax with a COUNT argument $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e'); $this->assertTrue(array('e' => 4.0, 'd' => 3.0, 'c' => 2.0) === $this->redis->zPopMax('key', 3)); // zPopMin with a COUNT argument $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e'); $this->assertTrue(array('a' => 0.0, 'b' => 1.0, 'c' => 2.0) === $this->redis->zPopMin('key', 3)); } public function testZRandMember() { if (version_compare($this->version, "6.2.0") < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e'); $this->assertInArray($this->redis->zRandMember('key'), ['a', 'b', 'c', 'd', 'e']); $result = $this->redis->zRandMember('key', ['count' => 3]); $this->assertEquals(3, count($result)); $this->assertEquals(array_intersect($result, ['a', 'b', 'c', 'd', 'e']), $result); $result = $this->redis->zRandMember('key', ['count' => 2, 'withscores' => true]); $this->assertEquals(2, count($result)); $this->assertEquals(array_intersect_key($result, ['a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4]), $result); } public function testHashes() { $this->redis->del('h', 'key'); $this->assertTrue(0 === $this->redis->hLen('h')); $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); $this->assertTrue(1 === $this->redis->hLen('h')); $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); $this->assertTrue(2 === $this->redis->hLen('h')); $this->assertTrue('a-value' === $this->redis->hGet('h', 'a')); // simple get $this->assertTrue('b-value' === $this->redis->hGet('h', 'b')); // simple get $this->assertTrue(0 === $this->redis->hSet('h', 'a', 'another-value')); // replacement $this->assertTrue('another-value' === $this->redis->hGet('h', 'a')); // get the new value $this->assertTrue('b-value' === $this->redis->hGet('h', 'b')); // simple get $this->assertTrue(FALSE === $this->redis->hGet('h', 'c')); // unknown hash member $this->assertTrue(FALSE === $this->redis->hGet('key', 'c')); // unknownkey // hDel $this->assertTrue(1 === $this->redis->hDel('h', 'a')); // 1 on success $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure $this->redis->del('h'); $this->redis->hSet('h', 'x', 'a'); $this->redis->hSet('h', 'y', 'b'); $this->assertTrue(2 === $this->redis->hDel('h', 'x', 'y')); // variadic // hsetnx $this->redis->del('h'); $this->assertTrue(TRUE === $this->redis->hSetNx('h', 'x', 'a')); $this->assertTrue(TRUE === $this->redis->hSetNx('h', 'y', 'b')); $this->assertTrue(FALSE === $this->redis->hSetNx('h', 'x', '?')); $this->assertTrue(FALSE === $this->redis->hSetNx('h', 'y', '?')); $this->assertTrue('a' === $this->redis->hGet('h', 'x')); $this->assertTrue('b' === $this->redis->hGet('h', 'y')); // keys $keys = $this->redis->hKeys('h'); $this->assertTrue($keys === ['x', 'y'] || $keys === ['y', 'x']); // values $values = $this->redis->hVals('h'); $this->assertTrue($values === ['a', 'b'] || $values === ['b', 'a']); // keys + values $all = $this->redis->hGetAll('h'); $this->assertTrue($all === ['x' => 'a', 'y' => 'b'] || $all === ['y' => 'b', 'x' => 'a']); // hExists $this->assertTrue(TRUE === $this->redis->hExists('h', 'x')); $this->assertTrue(TRUE === $this->redis->hExists('h', 'y')); $this->assertTrue(FALSE === $this->redis->hExists('h', 'w')); $this->redis->del('h'); $this->assertTrue(FALSE === $this->redis->hExists('h', 'x')); // hIncrBy $this->redis->del('h'); $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); $this->assertTrue("2" === $this->redis->hGet('h', 'x')); $this->assertTrue(PHP_INT_MAX === $this->redis->hIncrBy('h', 'x', PHP_INT_MAX-2)); $this->assertTrue("".PHP_INT_MAX === $this->redis->hGet('h', 'x')); $this->redis->hSet('h', 'y', 'not-a-number'); $this->assertTrue(FALSE === $this->redis->hIncrBy('h', 'y', 1)); if (version_compare($this->version, "2.5.0") >= 0) { // hIncrByFloat $this->redis->del('h'); $this->assertTrue(1.5 === $this->redis->hIncrByFloat('h','x', 1.5)); $this->assertTrue(3.0 === $this->redis->hincrByFloat('h','x', 1.5)); $this->assertTrue(1.5 === $this->redis->hincrByFloat('h','x', -1.5)); $this->assertTrue(1000000000001.5 === $this->redis->hincrByFloat('h','x', 1000000000000)); $this->redis->hset('h','y','not-a-number'); $this->assertTrue(FALSE === $this->redis->hIncrByFloat('h', 'y', 1.5)); } // hmset $this->redis->del('h'); $this->assertTrue(TRUE === $this->redis->hMset('h', ['x' => 123, 'y' => 456, 'z' => 'abc'])); $this->assertTrue('123' === $this->redis->hGet('h', 'x')); $this->assertTrue('456' === $this->redis->hGet('h', 'y')); $this->assertTrue('abc' === $this->redis->hGet('h', 'z')); $this->assertTrue(FALSE === $this->redis->hGet('h', 't')); // hmget $this->assertTrue(['x' => '123', 'y' => '456'] === $this->redis->hMget('h', ['x', 'y'])); $this->assertTrue(['z' => 'abc'] === $this->redis->hMget('h', ['z'])); $this->assertTrue(['x' => '123', 't' => FALSE, 'y' => '456'] === $this->redis->hMget('h', ['x', 't', 'y'])); $this->assertFalse([123 => 'x'] === $this->redis->hMget('h', [123])); $this->assertTrue([123 => FALSE] === $this->redis->hMget('h', [123])); // Test with an array populated with things we can't use as keys $this->assertTrue($this->redis->hmget('h', [false,NULL,false]) === FALSE); // Test with some invalid keys mixed in (which should just be ignored) $this->assertTrue(['x'=>'123','y'=>'456','z'=>'abc'] === $this->redis->hMget('h',['x',null,'y','','z',false])); // hmget/hmset with numeric fields $this->redis->del('h'); $this->assertTrue(TRUE === $this->redis->hMset('h', [123 => 'x', 'y' => 456])); $this->assertTrue('x' === $this->redis->hGet('h', 123)); $this->assertTrue('x' === $this->redis->hGet('h', '123')); $this->assertTrue('456' === $this->redis->hGet('h', 'y')); $this->assertTrue([123 => 'x', 'y' => '456'] === $this->redis->hMget('h', ['123', 'y'])); // references $keys = [123, 'y']; foreach ($keys as &$key) {} $this->assertTrue([123 => 'x', 'y' => '456'] === $this->redis->hMget('h', $keys)); // check non-string types. $this->redis->del('h1'); $this->assertTrue(TRUE === $this->redis->hMSet('h1', ['x' => 0, 'y' => [], 'z' => new stdclass(), 't' => NULL])); $h1 = $this->redis->hGetAll('h1'); $this->assertTrue('0' === $h1['x']); $this->assertTrue('Array' === $h1['y']); $this->assertTrue('Object' === $h1['z']); $this->assertTrue('' === $h1['t']); // hstrlen if (version_compare($this->version, '3.2.0') >= 0) { $this->redis->del('h'); $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // key doesn't exist $this->redis->hSet('h', 'foo', 'bar'); $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // field is not present in the hash $this->assertTrue(3 === $this->redis->hStrLen('h', 'foo')); } } public function testHRandField() { if (version_compare($this->version, "6.2.0") < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('key'); $this->redis->hMSet('key', ['a' => 0, 'b' => 1, 'c' => 'foo', 'd' => 'bar', 'e' => null]); $this->assertInArray($this->redis->hRandField('key'), ['a', 'b', 'c', 'd', 'e']); $result = $this->redis->hRandField('key', ['count' => 3]); $this->assertEquals(3, count($result)); $this->assertEquals(array_intersect($result, ['a', 'b', 'c', 'd', 'e']), $result); $result = $this->redis->hRandField('key', ['count' => 2, 'withvalues' => true]); $this->assertEquals(2, count($result)); $this->assertEquals(array_intersect_key($result, ['a' => 0, 'b' => 1, 'c' => 'foo', 'd' => 'bar', 'e' => null]), $result); } public function testSetRange() { $this->redis->del('key'); $this->redis->set('key', 'hello world'); $this->redis->setRange('key', 6, 'redis'); $this->assertTrue('hello redis' === $this->redis->get('key')); $this->redis->setRange('key', 6, 'you'); // don't cut off the end $this->assertTrue('hello youis' === $this->redis->get('key')); $this->redis->set('key', 'hello world'); // $this->assertTrue(11 === $this->redis->setRange('key', -6, 'redis')); // works with negative offsets too! (disabled because not all versions support this) // $this->assertTrue('hello redis' === $this->redis->get('key')); // fill with zeros if needed $this->redis->del('key'); $this->redis->setRange('key', 6, 'foo'); $this->assertTrue("\x00\x00\x00\x00\x00\x00foo" === $this->redis->get('key')); } public function testObject() { /* Version 3.0.0 (represented as >= 2.9.0 in redis info) and moving * forward uses "embstr" instead of "raw" for small string values */ if (version_compare($this->version, "2.9.0") < 0) { $str_small_encoding = "raw"; } else { $str_small_encoding = "embstr"; } $this->redis->del('key'); $this->assertTrue($this->redis->object('encoding', 'key') === FALSE); $this->assertTrue($this->redis->object('refcount', 'key') === FALSE); $this->assertTrue($this->redis->object('idletime', 'key') === FALSE); $this->redis->set('key', 'value'); $this->assertTrue($this->redis->object('encoding', 'key') === $str_small_encoding); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->lpush('key', 'value'); $str_encoding = $this->redis->object('encoding', 'key'); if (version_compare($this->version, '7.1.240') >= 0) { /* Since redis 7.2-rc1 */ $valid = ['listpack']; } else { /* Newer versions of redis are going to encode lists as 'quicklists', * so 'quicklist' or 'ziplist' or 'listpack' are valid here */ $valid = ['ziplist', 'quicklist']; } $this->assertTrue(in_array($str_encoding, $valid), $str_encoding); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->sadd('key', 'value'); $str_encoding = $this->redis->object('encoding', 'key'); if (version_compare($this->version, '7.1.240') >= 0) { /* Since redis 7.2-rc1 */ $valid = ['listpack']; } else { $valid = ['hashtable']; } $this->assertTrue(in_array($str_encoding, $valid), $str_encoding); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->sadd('key', 42); $this->redis->sadd('key', 1729); $this->assertTrue($this->redis->object('encoding', 'key') === "intset"); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->lpush('key', str_repeat('A', pow(10,6))); // 1M elements, too big for a ziplist. $str_encoding = $this->redis->object('encoding', 'key'); $this->assertTrue($str_encoding === "linkedlist" || $str_encoding == "quicklist"); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); } public function testMultiExec() { $this->sequence(Redis::MULTI); $this->differentType(Redis::MULTI); // with prefix as well $this->redis->setOption(Redis::OPT_PREFIX, "test:"); $this->sequence(Redis::MULTI); $this->differentType(Redis::MULTI); $this->redis->setOption(Redis::OPT_PREFIX, ""); $this->redis->set('x', '42'); $this->assertTrue(TRUE === $this->redis->watch('x')); $ret = $this->redis->multi()->get('x')->exec(); // successful transaction $this->assertTrue($ret === ['42']); } public function testFailedTransactions() { $this->redis->set('x', 42); // failed transaction $this->redis->watch('x'); $r = $this->newInstance(); // new instance, modifying `x'. $r->incr('x'); $ret = $this->redis->multi()->get('x')->exec(); $this->assertTrue($ret === FALSE); // failed because another client changed our watched key between WATCH and EXEC. // watch and unwatch $this->redis->watch('x'); $r->incr('x'); // other instance $this->redis->unwatch(); // cancel transaction watch $ret = $this->redis->multi()->get('x')->exec(); $this->assertTrue($ret === ['44']); // succeeded since we've cancel the WATCH command. } public function testPipeline() { if (!$this->havePipeline()) { $this->markTestSkipped(); } $this->sequence(Redis::PIPELINE); $this->differentType(Redis::PIPELINE); // with prefix as well $this->redis->setOption(Redis::OPT_PREFIX, "test:"); $this->sequence(Redis::PIPELINE); $this->differentType(Redis::PIPELINE); $this->redis->setOption(Redis::OPT_PREFIX, ""); } public function testPipelineMultiExec() { if (!$this->havePipeline()) { $this->markTestSkipped(); } $ret = $this->redis->pipeline()->multi()->exec()->exec(); $this->assertTrue(is_array($ret)); $this->assertEquals(1, count($ret)); // empty transaction $ret = $this->redis->pipeline() ->ping() ->multi()->set('x', 42)->incr('x')->exec() ->ping() ->multi()->get('x')->del('x')->exec() ->ping() ->exec(); $this->assertTrue(is_array($ret)); $this->assertEquals(5, count($ret)); // should be 5 atomic operations } /* Github issue #1211 (ignore redundant calls to pipeline or multi) */ public function testDoublePipeNoOp() { /* Only the first pipeline should be honored */ for ($i = 0; $i < 6; $i++) { $this->redis->pipeline(); } /* Set and get in our pipeline */ $this->redis->set('pipecount','over9000')->get('pipecount'); $data = $this->redis->exec(); $this->assertEquals([true,'over9000'], $data); /* Only the first MULTI should be honored */ for ($i = 0; $i < 6; $i++) { $this->redis->multi(); } /* Set and get in our MULTI block */ $this->redis->set('multicount', 'over9000')->get('multicount'); $data = $this->redis->exec(); $this->assertEquals([true, 'over9000'], $data); } public function testDiscard() { foreach ([Redis::PIPELINE, Redis::MULTI] as $mode) { /* start transaction */ $this->redis->multi($mode); /* Set and get in our transaction */ $this->redis->set('pipecount','over9000')->get('pipecount'); /* first call closes transaction and clears commands queue */ $this->assertTrue($this->redis->discard()); /* next call fails because mode is ATOMIC */ $this->assertFalse($this->redis->discard()); } } protected function sequence($mode) { $ret = $this->redis->multi($mode) ->set('x', 42) ->type('x') ->get('x') ->exec(); $this->assertTrue(is_array($ret)); $i = 0; $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] === Redis::REDIS_STRING); $this->assertTrue($ret[$i] === '42' || $ret[$i] === 42); $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer $ret = $this->redis->multi($mode) ->del('{key}1') ->set('{key}1', 'value1') ->get('{key}1') ->getSet('{key}1', 'value2') ->get('{key}1') ->set('{key}2', 4) ->incr('{key}2') ->get('{key}2') ->decr('{key}2') ->get('{key}2') ->rename('{key}2', '{key}3') ->get('{key}3') ->renameNx('{key}3', '{key}1') ->rename('{key}3', '{key}2') ->incrby('{key}2', 5) ->get('{key}2') ->decrby('{key}2', 5) ->get('{key}2') ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 'value1'); $this->assertTrue($ret[$i++] == 'value1'); $this->assertTrue($ret[$i++] == 'value2'); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 5); $this->assertTrue($ret[$i++] == 5); $this->assertTrue($ret[$i++] == 4); $this->assertTrue($ret[$i++] == 4); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 4); $this->assertTrue($ret[$i++] == FALSE); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 9); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 4); $this->assertTrue(count($ret) == $i); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $ret = $this->redis->multi($mode) ->del('{key}1') ->del('{key}2') ->set('{key}1', 'val1') ->setnx('{key}1', 'valX') ->setnx('{key}2', 'valX') ->exists('{key}1') ->exists('{key}3') ->exec(); $this->assertTrue(is_array($ret)); $this->assertTrue($ret[0] == TRUE); $this->assertTrue($ret[1] == TRUE); $this->assertTrue($ret[2] == TRUE); $this->assertTrue($ret[3] == FALSE); $this->assertTrue($ret[4] == TRUE); $this->assertTrue($ret[5] == TRUE); $this->assertTrue($ret[6] == FALSE); // ttl, mget, mset, msetnx, expire, expireAt $this->redis->del('key'); $ret = $this->redis->multi($mode) ->ttl('key') ->mget(['{key}1', '{key}2', '{key}3']) ->mset(['{key}3' => 'value3', '{key}4' => 'value4']) ->set('key', 'value') ->expire('key', 5) ->ttl('key') ->expireAt('key', '0000') ->exec(); $this->assertTrue(is_array($ret)); $i = 0; $ttl = $ret[$i++]; $this->assertTrue($ttl === -1 || $ttl === -2); $this->assertTrue($ret[$i++] === ['val1', 'valX', FALSE]); // mget $this->assertTrue($ret[$i++] === TRUE); // mset $this->assertTrue($ret[$i++] === TRUE); // set $this->assertTrue($ret[$i++] === TRUE); // expire $this->assertTrue($ret[$i++] === 5); // ttl $this->assertTrue($ret[$i++] === TRUE); // expireAt $this->assertTrue(count($ret) == $i); $ret = $this->redis->multi($mode) ->set('{list}lkey', 'x') ->set('{list}lDest', 'y') ->del('{list}lkey', '{list}lDest') ->rpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') ->rpoplpush('{list}lkey', '{list}lDest') ->lrange('{list}lDest', 0, -1) ->lpop('{list}lkey') ->llen('{list}lkey') ->lrem('{list}lkey', 'lvalue', 3) ->llen('{list}lkey') ->lIndex('{list}lkey', 0) ->lrange('{list}lkey', 0, -1) ->lSet('{list}lkey', 1, "newValue") // check errors on key not exists ->lrange('{list}lkey', 0, -1) ->llen('{list}lkey') ->exec(); $this->assertTrue(is_array($ret)); $i = 0; $this->assertTrue($ret[$i++] === TRUE); // SET $this->assertTrue($ret[$i++] === TRUE); // SET $this->assertTrue($ret[$i++] === 2); // deleting 2 keys $this->assertTrue($ret[$i++] === 1); // rpush, now 1 element $this->assertTrue($ret[$i++] === 2); // lpush, now 2 elements $this->assertTrue($ret[$i++] === 3); // lpush, now 3 elements $this->assertTrue($ret[$i++] === 4); // lpush, now 4 elements $this->assertTrue($ret[$i++] === 5); // lpush, now 5 elements $this->assertTrue($ret[$i++] === 6); // lpush, now 6 elements $this->assertTrue($ret[$i++] === 'lvalue'); // rpoplpush returns the element: "lvalue" $this->assertTrue($ret[$i++] === ['lvalue']); // lDest contains only that one element. $this->assertTrue($ret[$i++] === 'lvalue'); // removing a second element from lkey, now 4 elements left ↓ $this->assertTrue($ret[$i++] === 4); // 4 elements left, after 2 pops. $this->assertTrue($ret[$i++] === 3); // removing 3 elements, now 1 left. $this->assertTrue($ret[$i++] === 1); // 1 element left $this->assertTrue($ret[$i++] === "lvalue"); // this is the current head. $this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list. $this->assertTrue($ret[$i++] === FALSE); // updating a non-existent element fails. $this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list. $this->assertTrue($ret[$i++] === 1); // 1 element left $this->assertTrue(count($ret) == $i); $ret = $this->redis->multi($mode) ->del('{list}lkey', '{list}lDest') ->rpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') ->rpoplpush('{list}lkey', '{list}lDest') ->lrange('{list}lDest', 0, -1) ->lpop('{list}lkey') ->exec(); $this->assertTrue(is_array($ret)); $i = 0; $this->assertTrue($ret[$i++] <= 2); // deleted 0, 1, or 2 items $this->assertTrue($ret[$i++] === 1); // 1 element in the list $this->assertTrue($ret[$i++] === 2); // 2 elements in the list $this->assertTrue($ret[$i++] === 3); // 3 elements in the list $this->assertTrue($ret[$i++] === 'lvalue'); // rpoplpush returns the element: "lvalue" $this->assertTrue($ret[$i++] === ['lvalue']); // rpoplpush returns the element: "lvalue" $this->assertTrue($ret[$i++] === 'lvalue'); // pop returns the front element: "lvalue" $this->assertTrue(count($ret) == $i); $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer $ret = $this->redis->multi($mode) ->del('{key}1') ->set('{key}1', 'value1') ->get('{key}1') ->getSet('{key}1', 'value2') ->get('{key}1') ->set('{key}2', 4) ->incr('{key}2') ->get('{key}2') ->decr('{key}2') ->get('{key}2') ->rename('{key}2', '{key}3') ->get('{key}3') ->renameNx('{key}3', '{key}1') ->rename('{key}3', '{key}2') ->incrby('{key}2', 5) ->get('{key}2') ->decrby('{key}2', 5) ->get('{key}2') ->set('{key}3', 'value3') ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i]) && $ret[$i] <= 1); $i++; $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 'value1'); $this->assertTrue($ret[$i++] == 'value1'); $this->assertTrue($ret[$i++] == 'value2'); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 5); $this->assertTrue($ret[$i++] == 5); $this->assertTrue($ret[$i++] == 4); $this->assertTrue($ret[$i++] == 4); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 4); $this->assertTrue($ret[$i++] == FALSE); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 9); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 4); $this->assertTrue($ret[$i++]); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $ret = $this->redis->multi($mode) ->del('{key}1') ->del('{key}2') ->del('{key}3') ->set('{key}1', 'val1') ->setnx('{key}1', 'valX') ->setnx('{key}2', 'valX') ->exists('{key}1') ->exists('{key}3') ->exec(); $this->assertTrue(is_array($ret)); $this->assertTrue($ret[0] == TRUE); $this->assertTrue($ret[1] == TRUE); $this->assertTrue($ret[2] == TRUE); $this->assertTrue($ret[3] == TRUE); $this->assertTrue($ret[4] == FALSE); $this->assertTrue($ret[5] == TRUE); $this->assertTrue($ret[6] == TRUE); $this->assertTrue($ret[7] == FALSE); // ttl, mget, mset, msetnx, expire, expireAt $ret = $this->redis->multi($mode) ->ttl('key') ->mget(['{key}1', '{key}2', '{key}3']) ->mset(['{key}3' => 'value3', '{key}4' => 'value4']) ->set('key', 'value') ->expire('key', 5) ->ttl('key') ->expireAt('key', '0000') ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 3); // mget $i++; $this->assertTrue($ret[$i++] === TRUE); // mset always returns TRUE $this->assertTrue($ret[$i++] === TRUE); // set always returns TRUE $this->assertTrue($ret[$i++] === TRUE); // expire always returns TRUE $this->assertTrue($ret[$i++] === 5); // TTL was just set. $this->assertTrue($ret[$i++] === TRUE); // expireAt returns TRUE for an existing key $this->assertTrue(count($ret) === $i); // lists $ret = $this->redis->multi($mode) ->del('{l}key', '{l}Dest') ->rpush('{l}key', 'lvalue') ->lpush('{l}key', 'lvalue') ->lpush('{l}key', 'lvalue') ->lpush('{l}key', 'lvalue') ->lpush('{l}key', 'lvalue') ->lpush('{l}key', 'lvalue') ->rpoplpush('{l}key', '{l}Dest') ->lrange('{l}Dest', 0, -1) ->lpop('{l}key') ->llen('{l}key') ->lrem('{l}key', 'lvalue', 3) ->llen('{l}key') ->lIndex('{l}key', 0) ->lrange('{l}key', 0, -1) ->lSet('{l}key', 1, "newValue") // check errors on missing key ->lrange('{l}key', 0, -1) ->llen('{l}key') ->exec(); $this->assertTrue(is_array($ret)); $i = 0; $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // del $i++; $this->assertTrue($ret[$i++] === 1); // 1 value $this->assertTrue($ret[$i++] === 2); // 2 values $this->assertTrue($ret[$i++] === 3); // 3 values $this->assertTrue($ret[$i++] === 4); // 4 values $this->assertTrue($ret[$i++] === 5); // 5 values $this->assertTrue($ret[$i++] === 6); // 6 values $this->assertTrue($ret[$i++] === 'lvalue'); $this->assertTrue($ret[$i++] === ['lvalue']); // 1 value only in lDest $this->assertTrue($ret[$i++] === 'lvalue'); // now 4 values left $this->assertTrue($ret[$i++] === 4); $this->assertTrue($ret[$i++] === 3); // removing 3 elements. $this->assertTrue($ret[$i++] === 1); // length is now 1 $this->assertTrue($ret[$i++] === 'lvalue'); // this is the head $this->assertTrue($ret[$i++] === ['lvalue']); // 1 value only in lkey $this->assertTrue($ret[$i++] === FALSE); // can't set list[1] if we only have a single value in it. $this->assertTrue($ret[$i++] === ['lvalue']); // the previous error didn't touch anything. $this->assertTrue($ret[$i++] === 1); // the previous error didn't change the length $this->assertTrue(count($ret) === $i); // sets $ret = $this->redis->multi($mode) ->del('{s}key1', '{s}key2', '{s}keydest', '{s}keyUnion', '{s}DiffDest') ->sadd('{s}key1', 'sValue1') ->sadd('{s}key1', 'sValue2') ->sadd('{s}key1', 'sValue3') ->sadd('{s}key1', 'sValue4') ->sadd('{s}key2', 'sValue1') ->sadd('{s}key2', 'sValue2') ->scard('{s}key1') ->srem('{s}key1', 'sValue2') ->scard('{s}key1') ->sMove('{s}key1', '{s}key2', 'sValue4') ->scard('{s}key2') ->sismember('{s}key2', 'sValue4') ->sMembers('{s}key1') ->sMembers('{s}key2') ->sInter('{s}key1', '{s}key2') ->sInterStore('{s}keydest', '{s}key1', '{s}key2') ->sMembers('{s}keydest') ->sUnion('{s}key2', '{s}keydest') ->sUnionStore('{s}keyUnion', '{s}key2', '{s}keydest') ->sMembers('{s}keyUnion') ->sDiff('{s}key1', '{s}key2') ->sDiffStore('{s}DiffDest', '{s}key1', '{s}key2') ->sMembers('{s}DiffDest') ->sPop('{s}key2') ->scard('{s}key2') ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleted at most 5 values. $this->assertTrue($ret[$i++] === 1); // skey1 now has 1 element. $this->assertTrue($ret[$i++] === 1); // skey1 now has 2 elements. $this->assertTrue($ret[$i++] === 1); // skey1 now has 3 elements. $this->assertTrue($ret[$i++] === 1); // skey1 now has 4 elements. $this->assertTrue($ret[$i++] === 1); // skey2 now has 1 element. $this->assertTrue($ret[$i++] === 1); // skey2 now has 2 elements. $this->assertTrue($ret[$i++] === 4); $this->assertTrue($ret[$i++] === 1); // we did remove that value. $this->assertTrue($ret[$i++] === 3); // now 3 values only. $this->assertTrue($ret[$i++] === TRUE); // the move did succeed. $this->assertTrue($ret[$i++] === 3); // sKey2 now has 3 values. $this->assertTrue($ret[$i++] === TRUE); // sKey2 does contain sValue4. foreach(['sValue1', 'sValue3'] as $k) { // sKey1 contains sValue1 and sValue3. $this->assertTrue(in_array($k, $ret[$i])); } $this->assertTrue(count($ret[$i++]) === 2); foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // sKey2 contains sValue1, sValue2, and sValue4. $this->assertTrue(in_array($k, $ret[$i])); } $this->assertTrue(count($ret[$i++]) === 3); $this->assertTrue($ret[$i++] === ['sValue1']); // intersection $this->assertTrue($ret[$i++] === 1); // intersection + store → 1 value in the destination set. $this->assertTrue($ret[$i++] === ['sValue1']); // sinterstore destination contents foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeydest U sKey2) contains sValue1, sValue2, and sValue4. $this->assertTrue(in_array($k, $ret[$i])); } $this->assertTrue(count($ret[$i++]) === 3); // union size $this->assertTrue($ret[$i++] === 3); // unionstore size foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4. $this->assertTrue(in_array($k, $ret[$i])); } $this->assertTrue(count($ret[$i++]) === 3); // skeyUnion size $this->assertTrue($ret[$i++] === ['sValue3']); // diff skey1, skey2 : only sValue3 is not shared. $this->assertTrue($ret[$i++] === 1); // sdiffstore size == 1 $this->assertTrue($ret[$i++] === ['sValue3']); // contents of sDiffDest $this->assertTrue(in_array($ret[$i++], ['sValue1', 'sValue2', 'sValue4'])); // we removed an element from sKey2 $this->assertTrue($ret[$i++] === 2); // sKey2 now has 2 elements only. $this->assertTrue(count($ret) === $i); // sorted sets $ret = $this->redis->multi($mode) ->del('{z}key1', '{z}key2', '{z}key5', '{z}Inter', '{z}Union') ->zadd('{z}key1', 1, 'zValue1') ->zadd('{z}key1', 5, 'zValue5') ->zadd('{z}key1', 2, 'zValue2') ->zRange('{z}key1', 0, -1) ->zRem('{z}key1', 'zValue2') ->zRange('{z}key1', 0, -1) ->zadd('{z}key1', 11, 'zValue11') ->zadd('{z}key1', 12, 'zValue12') ->zadd('{z}key1', 13, 'zValue13') ->zadd('{z}key1', 14, 'zValue14') ->zadd('{z}key1', 15, 'zValue15') ->zRemRangeByScore('{z}key1', 11, 13) ->zrange('{z}key1', 0, -1) ->zRevRange('{z}key1', 0, -1) ->zRangeByScore('{z}key1', 1, 6) ->zCard('{z}key1') ->zScore('{z}key1', 'zValue15') ->zadd('{z}key2', 5, 'zValue5') ->zadd('{z}key2', 2, 'zValue2') ->zInterStore('{z}Inter', ['{z}key1', '{z}key2']) ->zRange('{z}key1', 0, -1) ->zRange('{z}key2', 0, -1) ->zRange('{z}Inter', 0, -1) ->zUnionStore('{z}Union', ['{z}key1', '{z}key2']) ->zRange('{z}Union', 0, -1) ->zadd('{z}key5', 5, 'zValue5') ->zIncrBy('{z}key5', 3, 'zValue5') // fix this ->zScore('{z}key5', 'zValue5') ->zScore('{z}key5', 'unknown') ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleting at most 5 keys $this->assertTrue($ret[$i++] === 1); $this->assertTrue($ret[$i++] === 1); $this->assertTrue($ret[$i++] === 1); $this->assertTrue($ret[$i++] === ['zValue1', 'zValue2', 'zValue5']); $this->assertTrue($ret[$i++] === 1); $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5']); $this->assertTrue($ret[$i++] === 1); // adding zValue11 $this->assertTrue($ret[$i++] === 1); // adding zValue12 $this->assertTrue($ret[$i++] === 1); // adding zValue13 $this->assertTrue($ret[$i++] === 1); // adding zValue14 $this->assertTrue($ret[$i++] === 1); // adding zValue15 $this->assertTrue($ret[$i++] === 3); // deleted zValue11, zValue12, zValue13 $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5', 'zValue14', 'zValue15']); $this->assertTrue($ret[$i++] === ['zValue15', 'zValue14', 'zValue5', 'zValue1']); $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5']); $this->assertTrue($ret[$i++] === 4); // 4 elements $this->assertTrue($ret[$i++] === 15.0); $this->assertTrue($ret[$i++] === 1); // added value $this->assertTrue($ret[$i++] === 1); // added value $this->assertTrue($ret[$i++] === 1); // zinter only has 1 value $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5', 'zValue14', 'zValue15']); // {z}key1 contents $this->assertTrue($ret[$i++] === ['zValue2', 'zValue5']); // {z}key2 contents $this->assertTrue($ret[$i++] === ['zValue5']); // {z}inter contents $this->assertTrue($ret[$i++] === 5); // {z}Union has 5 values (1,2,5,14,15) $this->assertTrue($ret[$i++] === ['zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15']); // {z}Union contents $this->assertTrue($ret[$i++] === 1); // added value to {z}key5, with score 5 $this->assertTrue($ret[$i++] === 8.0); // incremented score by 3 → it is now 8. $this->assertTrue($ret[$i++] === 8.0); // current score is 8. $this->assertTrue($ret[$i++] === FALSE); // score for unknown element. $this->assertTrue(count($ret) === $i); // hash $ret = $this->redis->multi($mode) ->del('hkey1') ->hset('hkey1', 'key1', 'value1') ->hset('hkey1', 'key2', 'value2') ->hset('hkey1', 'key3', 'value3') ->hmget('hkey1', ['key1', 'key2', 'key3']) ->hget('hkey1', 'key1') ->hlen('hkey1') ->hdel('hkey1', 'key2') ->hdel('hkey1', 'key2') ->hexists('hkey1', 'key2') ->hkeys('hkey1') ->hvals('hkey1') ->hgetall('hkey1') ->hset('hkey1', 'valn', 1) ->hset('hkey1', 'val-fail', 'non-string') ->hget('hkey1', 'val-fail') ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue($ret[$i++] <= 1); // delete $this->assertTrue($ret[$i++] === 1); // added 1 element $this->assertTrue($ret[$i++] === 1); // added 1 element $this->assertTrue($ret[$i++] === 1); // added 1 element $this->assertTrue($ret[$i++] === ['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3']); // hmget, 3 elements $this->assertTrue($ret[$i++] === 'value1'); // hget $this->assertTrue($ret[$i++] === 3); // hlen $this->assertTrue($ret[$i++] === 1); // hdel succeeded $this->assertTrue($ret[$i++] === 0); // hdel failed $this->assertTrue($ret[$i++] === FALSE); // hexists didn't find the deleted key $this->assertTrue($ret[$i] === ['key1', 'key3'] || $ret[$i] === ['key3', 'key1']); $i++; // hkeys $this->assertTrue($ret[$i] === ['value1', 'value3'] || $ret[$i] === ['value3', 'value1']); $i++; // hvals $this->assertTrue($ret[$i] === ['key1' => 'value1', 'key3' => 'value3'] || $ret[$i] === ['key3' => 'value3', 'key1' => 'value1']); $i++; // hgetall $this->assertTrue($ret[$i++] === 1); // added 1 element $this->assertTrue($ret[$i++] === 1); // added the element, so 1. $this->assertTrue($ret[$i++] === 'non-string'); // hset succeeded $this->assertTrue(count($ret) === $i); $ret = $this->redis->multi($mode) // default to MULTI, not PIPELINE. ->del('test') ->set('test', 'xyz') ->get('test') ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue($ret[$i++] <= 1); // delete $this->assertTrue($ret[$i++] === TRUE); // added 1 element $this->assertTrue($ret[$i++] === 'xyz'); $this->assertTrue(count($ret) === $i); // GitHub issue 78 $this->redis->del('test'); for($i = 1; $i <= 5; $i++) $this->redis->zadd('test', $i, (string)$i); $result = $this->redis->multi($mode) ->zscore('test', "1") ->zscore('test', "6") ->zscore('test', "8") ->zscore('test', "2") ->exec(); $this->assertTrue($result === [1.0, FALSE, FALSE, 2.0]); } protected function differentType($mode) { // string $key = '{hash}string'; $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->set($key, 'value') // lists I/F ->rPush($key, 'lvalue') ->lPush($key, 'lvalue') ->lLen($key) ->lPop($key) ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) ->lSet($key, 0, "newValue") ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) ->rPoplPush($key, $dkey . 'lkey1') // sets I/F ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') ->sPop($key) ->sMove($key, $dkey . 'skey1', 'sValue1') ->scard($key) ->sismember($key, 'sValue1') ->sInter($key, $dkey . 'skey2') ->sUnion($key, $dkey . 'skey4') ->sDiff($key, $dkey . 'skey7') ->sMembers($key) ->sRandMember($key) // sorted sets I/F ->zAdd($key, 1, 'zValue1') ->zRem($key, 'zValue1') ->zIncrBy($key, 1, 'zValue1') ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') ->zRange($key, 0, -1) ->zRevRange($key, 0, -1) ->zRangeByScore($key, 1, 2) ->zCount($key, 0, -1) ->zCard($key) ->zScore($key, 'zValue1') ->zRemRangeByRank($key, 1, 2) ->zRemRangeByScore($key, 1, 2) // hash I/F ->hSet($key, 'key1', 'value1') ->hGet($key, 'key1') ->hMGet($key, ['key1']) ->hMSet($key, ['key1' => 'value1']) ->hIncrBy($key, 'key2', 1) ->hExists($key, 'key2') ->hDel($key, 'key2') ->hLen($key) ->hKeys($key) ->hVals($key) ->hGetAll($key) ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); // delete $this->assertTrue($ret[$i++] === TRUE); // set $this->assertTrue($ret[$i++] === FALSE); // rpush $this->assertTrue($ret[$i++] === FALSE); // lpush $this->assertTrue($ret[$i++] === FALSE); // llen $this->assertTrue($ret[$i++] === FALSE); // lpop $this->assertTrue($ret[$i++] === FALSE); // lrange $this->assertTrue($ret[$i++] === FALSE); // ltrim $this->assertTrue($ret[$i++] === FALSE); // lindex $this->assertTrue($ret[$i++] === FALSE); // lset $this->assertTrue($ret[$i++] === FALSE); // lremove $this->assertTrue($ret[$i++] === FALSE); // lpop $this->assertTrue($ret[$i++] === FALSE); // rpop $this->assertTrue($ret[$i++] === FALSE); // rpoplush $this->assertTrue($ret[$i++] === FALSE); // sadd $this->assertTrue($ret[$i++] === FALSE); // sremove $this->assertTrue($ret[$i++] === FALSE); // spop $this->assertTrue($ret[$i++] === FALSE); // smove $this->assertTrue($ret[$i++] === FALSE); // scard $this->assertTrue($ret[$i++] === FALSE); // sismember $this->assertTrue($ret[$i++] === FALSE); // sinter $this->assertTrue($ret[$i++] === FALSE); // sunion $this->assertTrue($ret[$i++] === FALSE); // sdiff $this->assertTrue($ret[$i++] === FALSE); // smembers $this->assertTrue($ret[$i++] === FALSE); // srandmember $this->assertTrue($ret[$i++] === FALSE); // zadd $this->assertTrue($ret[$i++] === FALSE); // zrem $this->assertTrue($ret[$i++] === FALSE); // zincrby $this->assertTrue($ret[$i++] === FALSE); // zrank $this->assertTrue($ret[$i++] === FALSE); // zrevrank $this->assertTrue($ret[$i++] === FALSE); // zrange $this->assertTrue($ret[$i++] === FALSE); // zreverserange $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore $this->assertTrue($ret[$i++] === FALSE); // zcount $this->assertTrue($ret[$i++] === FALSE); // zcard $this->assertTrue($ret[$i++] === FALSE); // zscore $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore $this->assertTrue($ret[$i++] === FALSE); // hset $this->assertTrue($ret[$i++] === FALSE); // hget $this->assertTrue($ret[$i++] === FALSE); // hmget $this->assertTrue($ret[$i++] === FALSE); // hmset $this->assertTrue($ret[$i++] === FALSE); // hincrby $this->assertTrue($ret[$i++] === FALSE); // hexists $this->assertTrue($ret[$i++] === FALSE); // hdel $this->assertTrue($ret[$i++] === FALSE); // hlen $this->assertTrue($ret[$i++] === FALSE); // hkeys $this->assertTrue($ret[$i++] === FALSE); // hvals $this->assertTrue($ret[$i++] === FALSE); // hgetall $this->assertEquals($i, count($ret)); // list $key = '{hash}list'; $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->lpush($key, 'lvalue') // string I/F ->get($key) ->getset($key, 'value2') ->append($key, 'append') ->getRange($key, 0, 8) ->mget([$key]) ->incr($key) ->incrBy($key, 1) ->decr($key) ->decrBy($key, 1) // sets I/F ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') ->sPop($key) ->sMove($key, $dkey . 'skey1', 'sValue1') ->scard($key) ->sismember($key, 'sValue1') ->sInter($key, $dkey . 'skey2') ->sUnion($key, $dkey . 'skey4') ->sDiff($key, $dkey . 'skey7') ->sMembers($key) ->sRandMember($key) // sorted sets I/F ->zAdd($key, 1, 'zValue1') ->zRem($key, 'zValue1') ->zIncrBy($key, 1, 'zValue1') ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') ->zRange($key, 0, -1) ->zRevRange($key, 0, -1) ->zRangeByScore($key, 1, 2) ->zCount($key, 0, -1) ->zCard($key) ->zScore($key, 'zValue1') ->zRemRangeByRank($key, 1, 2) ->zRemRangeByScore($key, 1, 2) // hash I/F ->hSet($key, 'key1', 'value1') ->hGet($key, 'key1') ->hMGet($key, ['key1']) ->hMSet($key, ['key1' => 'value1']) ->hIncrBy($key, 'key2', 1) ->hExists($key, 'key2') ->hDel($key, 'key2') ->hLen($key) ->hKeys($key) ->hVals($key) ->hGetAll($key) ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); // delete $this->assertTrue($ret[$i++] === 1); // lpush $this->assertTrue($ret[$i++] === FALSE); // get $this->assertTrue($ret[$i++] === FALSE); // getset $this->assertTrue($ret[$i++] === FALSE); // append $this->assertTrue($ret[$i++] === FALSE); // getRange $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget $i++; $this->assertTrue($ret[$i++] === FALSE); // incr $this->assertTrue($ret[$i++] === FALSE); // incrBy $this->assertTrue($ret[$i++] === FALSE); // decr $this->assertTrue($ret[$i++] === FALSE); // decrBy $this->assertTrue($ret[$i++] === FALSE); // sadd $this->assertTrue($ret[$i++] === FALSE); // sremove $this->assertTrue($ret[$i++] === FALSE); // spop $this->assertTrue($ret[$i++] === FALSE); // smove $this->assertTrue($ret[$i++] === FALSE); // scard $this->assertTrue($ret[$i++] === FALSE); // sismember $this->assertTrue($ret[$i++] === FALSE); // sinter $this->assertTrue($ret[$i++] === FALSE); // sunion $this->assertTrue($ret[$i++] === FALSE); // sdiff $this->assertTrue($ret[$i++] === FALSE); // smembers $this->assertTrue($ret[$i++] === FALSE); // srandmember $this->assertTrue($ret[$i++] === FALSE); // zadd $this->assertTrue($ret[$i++] === FALSE); // zrem $this->assertTrue($ret[$i++] === FALSE); // zincrby $this->assertTrue($ret[$i++] === FALSE); // zrank $this->assertTrue($ret[$i++] === FALSE); // zrevrank $this->assertTrue($ret[$i++] === FALSE); // zrange $this->assertTrue($ret[$i++] === FALSE); // zreverserange $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore $this->assertTrue($ret[$i++] === FALSE); // zcount $this->assertTrue($ret[$i++] === FALSE); // zcard $this->assertTrue($ret[$i++] === FALSE); // zscore $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore $this->assertTrue($ret[$i++] === FALSE); // hset $this->assertTrue($ret[$i++] === FALSE); // hget $this->assertTrue($ret[$i++] === FALSE); // hmget $this->assertTrue($ret[$i++] === FALSE); // hmset $this->assertTrue($ret[$i++] === FALSE); // hincrby $this->assertTrue($ret[$i++] === FALSE); // hexists $this->assertTrue($ret[$i++] === FALSE); // hdel $this->assertTrue($ret[$i++] === FALSE); // hlen $this->assertTrue($ret[$i++] === FALSE); // hkeys $this->assertTrue($ret[$i++] === FALSE); // hvals $this->assertTrue($ret[$i++] === FALSE); // hgetall $this->assertEquals($i, count($ret)); // set $key = '{hash}set'; $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->sAdd($key, 'sValue') // string I/F ->get($key) ->getset($key, 'value2') ->append($key, 'append') ->getRange($key, 0, 8) ->mget([$key]) ->incr($key) ->incrBy($key, 1) ->decr($key) ->decrBy($key, 1) // lists I/F ->rPush($key, 'lvalue') ->lPush($key, 'lvalue') ->lLen($key) ->lPop($key) ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) ->lSet($key, 0, "newValue") ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) ->rPoplPush($key, $dkey . 'lkey1') // sorted sets I/F ->zAdd($key, 1, 'zValue1') ->zRem($key, 'zValue1') ->zIncrBy($key, 1, 'zValue1') ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') ->zRange($key, 0, -1) ->zRevRange($key, 0, -1) ->zRangeByScore($key, 1, 2) ->zCount($key, 0, -1) ->zCard($key) ->zScore($key, 'zValue1') ->zRemRangeByRank($key, 1, 2) ->zRemRangeByScore($key, 1, 2) // hash I/F ->hSet($key, 'key1', 'value1') ->hGet($key, 'key1') ->hMGet($key, ['key1']) ->hMSet($key, ['key1' => 'value1']) ->hIncrBy($key, 'key2', 1) ->hExists($key, 'key2') ->hDel($key, 'key2') ->hLen($key) ->hKeys($key) ->hVals($key) ->hGetAll($key) ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); // delete $this->assertTrue($ret[$i++] === 1); // zadd $this->assertTrue($ret[$i++] === FALSE); // get $this->assertTrue($ret[$i++] === FALSE); // getset $this->assertTrue($ret[$i++] === FALSE); // append $this->assertTrue($ret[$i++] === FALSE); // getRange $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget $i++; $this->assertTrue($ret[$i++] === FALSE); // incr $this->assertTrue($ret[$i++] === FALSE); // incrBy $this->assertTrue($ret[$i++] === FALSE); // decr $this->assertTrue($ret[$i++] === FALSE); // decrBy $this->assertTrue($ret[$i++] === FALSE); // rpush $this->assertTrue($ret[$i++] === FALSE); // lpush $this->assertTrue($ret[$i++] === FALSE); // llen $this->assertTrue($ret[$i++] === FALSE); // lpop $this->assertTrue($ret[$i++] === FALSE); // lrange $this->assertTrue($ret[$i++] === FALSE); // ltrim $this->assertTrue($ret[$i++] === FALSE); // lindex $this->assertTrue($ret[$i++] === FALSE); // lset $this->assertTrue($ret[$i++] === FALSE); // lremove $this->assertTrue($ret[$i++] === FALSE); // lpop $this->assertTrue($ret[$i++] === FALSE); // rpop $this->assertTrue($ret[$i++] === FALSE); // rpoplush $this->assertTrue($ret[$i++] === FALSE); // zadd $this->assertTrue($ret[$i++] === FALSE); // zrem $this->assertTrue($ret[$i++] === FALSE); // zincrby $this->assertTrue($ret[$i++] === FALSE); // zrank $this->assertTrue($ret[$i++] === FALSE); // zrevrank $this->assertTrue($ret[$i++] === FALSE); // zrange $this->assertTrue($ret[$i++] === FALSE); // zreverserange $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore $this->assertTrue($ret[$i++] === FALSE); // zcount $this->assertTrue($ret[$i++] === FALSE); // zcard $this->assertTrue($ret[$i++] === FALSE); // zscore $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore $this->assertTrue($ret[$i++] === FALSE); // hset $this->assertTrue($ret[$i++] === FALSE); // hget $this->assertTrue($ret[$i++] === FALSE); // hmget $this->assertTrue($ret[$i++] === FALSE); // hmset $this->assertTrue($ret[$i++] === FALSE); // hincrby $this->assertTrue($ret[$i++] === FALSE); // hexists $this->assertTrue($ret[$i++] === FALSE); // hdel $this->assertTrue($ret[$i++] === FALSE); // hlen $this->assertTrue($ret[$i++] === FALSE); // hkeys $this->assertTrue($ret[$i++] === FALSE); // hvals $this->assertTrue($ret[$i++] === FALSE); // hgetall $this->assertEquals($i, count($ret)); // sorted set $key = '{hash}sortedset'; $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->zAdd($key, 0, 'zValue') // string I/F ->get($key) ->getset($key, 'value2') ->append($key, 'append') ->getRange($key, 0, 8) ->mget([$key]) ->incr($key) ->incrBy($key, 1) ->decr($key) ->decrBy($key, 1) // lists I/F ->rPush($key, 'lvalue') ->lPush($key, 'lvalue') ->lLen($key) ->lPop($key) ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) ->lSet($key, 0, "newValue") ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) ->rPoplPush($key, $dkey . 'lkey1') // sets I/F ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') ->sPop($key) ->sMove($key, $dkey . 'skey1', 'sValue1') ->scard($key) ->sismember($key, 'sValue1') ->sInter($key, $dkey . 'skey2') ->sUnion($key, $dkey . 'skey4') ->sDiff($key, $dkey . 'skey7') ->sMembers($key) ->sRandMember($key) // hash I/F ->hSet($key, 'key1', 'value1') ->hGet($key, 'key1') ->hMGet($key, ['key1']) ->hMSet($key, ['key1' => 'value1']) ->hIncrBy($key, 'key2', 1) ->hExists($key, 'key2') ->hDel($key, 'key2') ->hLen($key) ->hKeys($key) ->hVals($key) ->hGetAll($key) ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); // delete $this->assertTrue($ret[$i++] === 1); // zadd $this->assertTrue($ret[$i++] === FALSE); // get $this->assertTrue($ret[$i++] === FALSE); // getset $this->assertTrue($ret[$i++] === FALSE); // append $this->assertTrue($ret[$i++] === FALSE); // getRange $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget $i++; $this->assertTrue($ret[$i++] === FALSE); // incr $this->assertTrue($ret[$i++] === FALSE); // incrBy $this->assertTrue($ret[$i++] === FALSE); // decr $this->assertTrue($ret[$i++] === FALSE); // decrBy $this->assertTrue($ret[$i++] === FALSE); // rpush $this->assertTrue($ret[$i++] === FALSE); // lpush $this->assertTrue($ret[$i++] === FALSE); // llen $this->assertTrue($ret[$i++] === FALSE); // lpop $this->assertTrue($ret[$i++] === FALSE); // lrange $this->assertTrue($ret[$i++] === FALSE); // ltrim $this->assertTrue($ret[$i++] === FALSE); // lindex $this->assertTrue($ret[$i++] === FALSE); // lset $this->assertTrue($ret[$i++] === FALSE); // lremove $this->assertTrue($ret[$i++] === FALSE); // lpop $this->assertTrue($ret[$i++] === FALSE); // rpop $this->assertTrue($ret[$i++] === FALSE); // rpoplush $this->assertTrue($ret[$i++] === FALSE); // sadd $this->assertTrue($ret[$i++] === FALSE); // sremove $this->assertTrue($ret[$i++] === FALSE); // spop $this->assertTrue($ret[$i++] === FALSE); // smove $this->assertTrue($ret[$i++] === FALSE); // scard $this->assertTrue($ret[$i++] === FALSE); // sismember $this->assertTrue($ret[$i++] === FALSE); // sinter $this->assertTrue($ret[$i++] === FALSE); // sunion $this->assertTrue($ret[$i++] === FALSE); // sdiff $this->assertTrue($ret[$i++] === FALSE); // smembers $this->assertTrue($ret[$i++] === FALSE); // srandmember $this->assertTrue($ret[$i++] === FALSE); // hset $this->assertTrue($ret[$i++] === FALSE); // hget $this->assertTrue($ret[$i++] === FALSE); // hmget $this->assertTrue($ret[$i++] === FALSE); // hmset $this->assertTrue($ret[$i++] === FALSE); // hincrby $this->assertTrue($ret[$i++] === FALSE); // hexists $this->assertTrue($ret[$i++] === FALSE); // hdel $this->assertTrue($ret[$i++] === FALSE); // hlen $this->assertTrue($ret[$i++] === FALSE); // hkeys $this->assertTrue($ret[$i++] === FALSE); // hvals $this->assertTrue($ret[$i++] === FALSE); // hgetall $this->assertEquals($i, count($ret)); // hash $key = '{hash}hash'; $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->hset($key, 'key1', 'hValue') // string I/F ->get($key) ->getset($key, 'value2') ->append($key, 'append') ->getRange($key, 0, 8) ->mget([$key]) ->incr($key) ->incrBy($key, 1) ->decr($key) ->decrBy($key, 1) // lists I/F ->rPush($key, 'lvalue') ->lPush($key, 'lvalue') ->lLen($key) ->lPop($key) ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) ->lSet($key, 0, "newValue") ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) ->rPoplPush($key, $dkey . 'lkey1') // sets I/F ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') ->sPop($key) ->sMove($key, $dkey . 'skey1', 'sValue1') ->scard($key) ->sismember($key, 'sValue1') ->sInter($key, $dkey . 'skey2') ->sUnion($key, $dkey . 'skey4') ->sDiff($key, $dkey . 'skey7') ->sMembers($key) ->sRandMember($key) // sorted sets I/F ->zAdd($key, 1, 'zValue1') ->zRem($key, 'zValue1') ->zIncrBy($key, 1, 'zValue1') ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') ->zRange($key, 0, -1) ->zRevRange($key, 0, -1) ->zRangeByScore($key, 1, 2) ->zCount($key, 0, -1) ->zCard($key) ->zScore($key, 'zValue1') ->zRemRangeByRank($key, 1, 2) ->zRemRangeByScore($key, 1, 2) ->exec(); $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); // delete $this->assertTrue($ret[$i++] === 1); // hset $this->assertTrue($ret[$i++] === FALSE); // get $this->assertTrue($ret[$i++] === FALSE); // getset $this->assertTrue($ret[$i++] === FALSE); // append $this->assertTrue($ret[$i++] === FALSE); // getRange $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget $i++; $this->assertTrue($ret[$i++] === FALSE); // incr $this->assertTrue($ret[$i++] === FALSE); // incrBy $this->assertTrue($ret[$i++] === FALSE); // decr $this->assertTrue($ret[$i++] === FALSE); // decrBy $this->assertTrue($ret[$i++] === FALSE); // rpush $this->assertTrue($ret[$i++] === FALSE); // lpush $this->assertTrue($ret[$i++] === FALSE); // llen $this->assertTrue($ret[$i++] === FALSE); // lpop $this->assertTrue($ret[$i++] === FALSE); // lrange $this->assertTrue($ret[$i++] === FALSE); // ltrim $this->assertTrue($ret[$i++] === FALSE); // lindex $this->assertTrue($ret[$i++] === FALSE); // lset $this->assertTrue($ret[$i++] === FALSE); // lremove $this->assertTrue($ret[$i++] === FALSE); // lpop $this->assertTrue($ret[$i++] === FALSE); // rpop $this->assertTrue($ret[$i++] === FALSE); // rpoplush $this->assertTrue($ret[$i++] === FALSE); // sadd $this->assertTrue($ret[$i++] === FALSE); // sremove $this->assertTrue($ret[$i++] === FALSE); // spop $this->assertTrue($ret[$i++] === FALSE); // smove $this->assertTrue($ret[$i++] === FALSE); // scard $this->assertTrue($ret[$i++] === FALSE); // sismember $this->assertTrue($ret[$i++] === FALSE); // sinter $this->assertTrue($ret[$i++] === FALSE); // sunion $this->assertTrue($ret[$i++] === FALSE); // sdiff $this->assertTrue($ret[$i++] === FALSE); // smembers $this->assertTrue($ret[$i++] === FALSE); // srandmember $this->assertTrue($ret[$i++] === FALSE); // zadd $this->assertTrue($ret[$i++] === FALSE); // zrem $this->assertTrue($ret[$i++] === FALSE); // zincrby $this->assertTrue($ret[$i++] === FALSE); // zrank $this->assertTrue($ret[$i++] === FALSE); // zrevrank $this->assertTrue($ret[$i++] === FALSE); // zrange $this->assertTrue($ret[$i++] === FALSE); // zreverserange $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore $this->assertTrue($ret[$i++] === FALSE); // zcount $this->assertTrue($ret[$i++] === FALSE); // zcard $this->assertTrue($ret[$i++] === FALSE); // zscore $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore $this->assertEquals($i, count($ret)); } public function testDifferentTypeString() { $key = '{hash}string'; $dkey = '{hash}' . __FUNCTION__; $this->redis->del($key); $this->assertEquals(TRUE, $this->redis->set($key, 'value')); // lists I/F $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue')); $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue')); $this->assertEquals(FALSE, $this->redis->lLen($key)); $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->rPop($key)); $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); // sets I/F $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sPop($key)); $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); $this->assertEquals(FALSE, $this->redis->scard($key)); $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey. 'skey2')); $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4')); $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7')); $this->assertEquals(FALSE, $this->redis->sMembers($key)); $this->assertEquals(FALSE, $this->redis->sRandMember($key)); // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1')); $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1'])); $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1'])); $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1)); $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2')); $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2')); $this->assertEquals(FALSE, $this->redis->hLen($key)); $this->assertEquals(FALSE, $this->redis->hKeys($key)); $this->assertEquals(FALSE, $this->redis->hVals($key)); $this->assertEquals(FALSE, $this->redis->hGetAll($key)); } public function testDifferentTypeList() { $key = '{hash}list'; $dkey = '{hash}' . __FUNCTION__; $this->redis->del($key); $this->assertEquals(1, $this->redis->lPush($key, 'value')); // string I/F $this->assertEquals(FALSE, $this->redis->get($key)); $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); $this->assertEquals(FALSE, $this->redis->append($key, 'append')); $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); $this->assertEquals(FALSE, $this->redis->decr($key)); $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); // sets I/F $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sPop($key)); $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); $this->assertEquals(FALSE, $this->redis->scard($key)); $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2')); $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4')); $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7')); $this->assertEquals(FALSE, $this->redis->sMembers($key)); $this->assertEquals(FALSE, $this->redis->sRandMember($key)); // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1')); $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1'])); $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1'])); $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1)); $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2')); $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2')); $this->assertEquals(FALSE, $this->redis->hLen($key)); $this->assertEquals(FALSE, $this->redis->hKeys($key)); $this->assertEquals(FALSE, $this->redis->hVals($key)); $this->assertEquals(FALSE, $this->redis->hGetAll($key)); } public function testDifferentTypeSet() { $key = '{hash}set'; $dkey = '{hash}' . __FUNCTION__; $this->redis->del($key); $this->assertEquals(1, $this->redis->sAdd($key, 'value')); // string I/F $this->assertEquals(FALSE, $this->redis->get($key)); $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); $this->assertEquals(FALSE, $this->redis->append($key, 'append')); $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); $this->assertEquals(FALSE, $this->redis->decr($key)); $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); // lists I/F $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue')); $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue')); $this->assertEquals(FALSE, $this->redis->lLen($key)); $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->rPop($key)); $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1')); $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1'])); $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1'])); $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1)); $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2')); $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2')); $this->assertEquals(FALSE, $this->redis->hLen($key)); $this->assertEquals(FALSE, $this->redis->hKeys($key)); $this->assertEquals(FALSE, $this->redis->hVals($key)); $this->assertEquals(FALSE, $this->redis->hGetAll($key)); } public function testDifferentTypeSortedSet() { $key = '{hash}sortedset'; $dkey = '{hash}' . __FUNCTION__; $this->redis->del($key); $this->assertEquals(1, $this->redis->zAdd($key, 0, 'value')); // string I/F $this->assertEquals(FALSE, $this->redis->get($key)); $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); $this->assertEquals(FALSE, $this->redis->append($key, 'append')); $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); $this->assertEquals(FALSE, $this->redis->decr($key)); $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); // lists I/F $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue')); $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue')); $this->assertEquals(FALSE, $this->redis->lLen($key)); $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->rPop($key)); $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); // sets I/F $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sPop($key)); $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); $this->assertEquals(FALSE, $this->redis->scard($key)); $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2')); $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4')); $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7')); $this->assertEquals(FALSE, $this->redis->sMembers($key)); $this->assertEquals(FALSE, $this->redis->sRandMember($key)); // hash I/F $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1')); $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1'])); $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1'])); $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1)); $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2')); $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2')); $this->assertEquals(FALSE, $this->redis->hLen($key)); $this->assertEquals(FALSE, $this->redis->hKeys($key)); $this->assertEquals(FALSE, $this->redis->hVals($key)); $this->assertEquals(FALSE, $this->redis->hGetAll($key)); } public function testDifferentTypeHash() { $key = '{hash}hash'; $dkey = '{hash}hash'; $this->redis->del($key); $this->assertEquals(1, $this->redis->hSet($key, 'key', 'value')); // string I/F $this->assertEquals(FALSE, $this->redis->get($key)); $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); $this->assertEquals(FALSE, $this->redis->append($key, 'append')); $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); $this->assertEquals(FALSE, $this->redis->decr($key)); $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); // lists I/F $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue')); $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue')); $this->assertEquals(FALSE, $this->redis->lLen($key)); $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->rPop($key)); $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); // sets I/F $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sPop($key)); $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); $this->assertEquals(FALSE, $this->redis->scard($key)); $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2')); $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4')); $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7')); $this->assertEquals(FALSE, $this->redis->sMembers($key)); $this->assertEquals(FALSE, $this->redis->sRandMember($key)); // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); } public function testSerializerPHP() { $this->checkSerializer(Redis::SERIALIZER_PHP); // with prefix $this->redis->setOption(Redis::OPT_PREFIX, "test:"); $this->checkSerializer(Redis::SERIALIZER_PHP); $this->redis->setOption(Redis::OPT_PREFIX, ""); } public function testSerializerIGBinary() { if(defined('Redis::SERIALIZER_IGBINARY')) { $this->checkSerializer(Redis::SERIALIZER_IGBINARY); // with prefix $this->redis->setOption(Redis::OPT_PREFIX, "test:"); $this->checkSerializer(Redis::SERIALIZER_IGBINARY); $this->redis->setOption(Redis::OPT_PREFIX, ""); /* Test our igbinary header check logic. The check allows us to do simple INCR type operations even with the serializer enabled, and should also protect against igbinary-like data from being erroneously deserialized */ $this->redis->del('incrkey'); $this->redis->set('spoof-1', "\x00\x00\x00\x00"); $this->redis->set('spoof-2', "\x00\x00\x00\x00bad-version1"); $this->redis->set('spoof-3', "\x00\x00\x00\x05bad-version2"); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); $this->assertEquals(16, $this->redis->incrby('incrkey', 16)); $this->assertEquals('16', $this->redis->get('incrkey')); $this->assertEquals("\x00\x00\x00\x00", $this->redis->get('spoof-1')); $this->assertEquals("\x00\x00\x00\x00bad-version1", $this->redis->get('spoof-2')); $this->assertEquals("\x00\x00\x00\x05bad-version2", $this->redis->get('spoof-3')); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); $this->redis->del('incrkey', 'spoof-1', 'spoof-2', 'spoof-3'); } } public function testSerializerMsgPack() { if(defined('Redis::SERIALIZER_MSGPACK')) { $this->checkSerializer(Redis::SERIALIZER_MSGPACK); // with prefix $this->redis->setOption(Redis::OPT_PREFIX, "test:"); $this->checkSerializer(Redis::SERIALIZER_MSGPACK); $this->redis->setOption(Redis::OPT_PREFIX, ""); } } public function testSerializerJSON() { $this->checkSerializer(Redis::SERIALIZER_JSON); // with prefix $this->redis->setOption(Redis::OPT_PREFIX, "test:"); $this->checkSerializer(Redis::SERIALIZER_JSON); $this->redis->setOption(Redis::OPT_PREFIX, ""); } private function checkSerializer($mode) { $this->redis->del('key'); $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // default $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, $mode) === TRUE); // set ok $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === $mode); // get ok // lPush, rPush $a = ['hello world', 42, TRUE, ['' => 1729]]; $this->redis->del('key'); $this->redis->lPush('key', $a[0]); $this->redis->rPush('key', $a[1]); $this->redis->rPush('key', $a[2]); $this->redis->rPush('key', $a[3]); // lrange $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); // lIndex $this->assertTrue($a[0] === $this->redis->lIndex('key', 0)); $this->assertTrue($a[1] === $this->redis->lIndex('key', 1)); $this->assertTrue($a[2] === $this->redis->lIndex('key', 2)); $this->assertTrue($a[3] === $this->redis->lIndex('key', 3)); // lrem $this->assertTrue($this->redis->lrem('key', $a[3]) === 1); $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1)); // lSet $a[0] = ['k' => 'v']; // update $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); $this->assertTrue($a[0] === $this->redis->lIndex('key', 0)); // lInsert $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], [1,2,3]) === 4); $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], [4,5,6]) === 5); $a = [[1,2,3], $a[0], [4,5,6], $a[1], $a[2]]; $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); // sAdd $this->redis->del('{set}key'); $s = [1,'a', [1,2,3], ['k' => 'v']]; $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[0])); $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[1])); $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[2])); $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[3])); // variadic sAdd $this->redis->del('k'); $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); // srem $this->assertTrue(1 === $this->redis->srem('{set}key', $s[3])); $this->assertTrue(0 === $this->redis->srem('{set}key', $s[3])); // variadic $this->redis->del('k'); $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); $this->assertEquals(0, $this->redis->exists('k')); // sismember $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[0])); $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[1])); $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[2])); $this->assertTrue(FALSE === $this->redis->sismember('{set}key', $s[3])); unset($s[3]); // sMove $this->redis->del('{set}tmp'); $this->redis->sMove('{set}key', '{set}tmp', $s[0]); $this->assertTrue(FALSE === $this->redis->sismember('{set}key', $s[0])); $this->assertTrue(TRUE === $this->redis->sismember('{set}tmp', $s[0])); unset($s[0]); // sorted sets $z = ['z0', ['k' => 'v'], FALSE, NULL]; $this->redis->del('key'); // zAdd $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0])); $this->assertTrue(1 === $this->redis->zAdd('key', 1, $z[1])); $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2])); $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3])); // zRem $this->assertTrue(1 === $this->redis->zRem('key', $z[3])); $this->assertTrue(0 === $this->redis->zRem('key', $z[3])); unset($z[3]); // variadic $this->redis->del('k'); $this->redis->zAdd('k', 0, 'a'); $this->redis->zAdd('k', 1, 'b'); $this->redis->zAdd('k', 2, 'c'); $this->assertTrue(2 === $this->redis->zRem('k', 'a', 'c')); $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); $this->assertTrue($this->redis->zRange('k', 0, -1, true) == ['b' => 1.0]); // zRange $this->assertTrue($z === $this->redis->zRange('key', 0, -1)); // zScore $this->assertTrue(0.0 === $this->redis->zScore('key', $z[0])); $this->assertTrue(1.0 === $this->redis->zScore('key', $z[1])); $this->assertTrue(2.0 === $this->redis->zScore('key', $z[2])); // zRank $this->assertTrue(0 === $this->redis->zRank('key', $z[0])); $this->assertTrue(1 === $this->redis->zRank('key', $z[1])); $this->assertTrue(2 === $this->redis->zRank('key', $z[2])); // zRevRank $this->assertTrue(2 === $this->redis->zRevRank('key', $z[0])); $this->assertTrue(1 === $this->redis->zRevRank('key', $z[1])); $this->assertTrue(0 === $this->redis->zRevRank('key', $z[2])); // zIncrBy $this->assertTrue(3.0 === $this->redis->zIncrBy('key', 1.0, $z[2])); $this->assertTrue(3.0 === $this->redis->zScore('key', $z[2])); $this->assertTrue(5.0 === $this->redis->zIncrBy('key', 2.0, $z[2])); $this->assertTrue(5.0 === $this->redis->zScore('key', $z[2])); $this->assertTrue(2.0 === $this->redis->zIncrBy('key', -3.0, $z[2])); $this->assertTrue(2.0 === $this->redis->zScore('key', $z[2])); // mset $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']]; $this->assertTrue(TRUE === $this->redis->mset($a)); foreach($a as $k => $v) { $this->assertTrue($this->redis->get($k) === $v); } $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']]; // hSet $this->redis->del('key'); foreach($a as $k => $v) { $this->assertTrue(1 === $this->redis->hSet('key', $k, $v)); } // hGet foreach($a as $k => $v) { $this->assertTrue($v === $this->redis->hGet('key', $k)); } // hGetAll $this->assertTrue($a === $this->redis->hGetAll('key')); $this->assertTrue(TRUE === $this->redis->hExists('key', 'k0')); $this->assertTrue(TRUE === $this->redis->hExists('key', 'k1')); $this->assertTrue(TRUE === $this->redis->hExists('key', 'k2')); $this->assertTrue(TRUE === $this->redis->hExists('key', 'k3')); $this->assertTrue(TRUE === $this->redis->hExists('key', 'k4')); // hMSet $this->redis->del('key'); $this->redis->hMSet('key', $a); foreach($a as $k => $v) { $this->assertTrue($v === $this->redis->hGet('key', $k)); } // hMget $hmget = $this->redis->hMget('key', array_keys($a)); foreach($hmget as $k => $v) { $this->assertTrue($v === $a[$k]); } // getMultiple $this->redis->set('a', NULL); $this->redis->set('b', FALSE); $this->redis->set('c', 42); $this->redis->set('d', ['x' => 'y']); $this->assertTrue([NULL, FALSE, 42, ['x' => 'y']] === $this->redis->mGet(['a', 'b', 'c', 'd'])); // pipeline if ($this->havePipeline()) { $this->sequence(Redis::PIPELINE); } // multi-exec $this->sequence(Redis::MULTI); // keys $this->assertTrue(is_array($this->redis->keys('*'))); // issue #62, hgetall $this->redis->del('hash1'); $this->redis->hSet('hash1','data', 'test 1'); $this->redis->hSet('hash1','session_id', 'test 2'); $data = $this->redis->hGetAll('hash1'); $this->assertTrue($data['data'] === 'test 1'); $this->assertTrue($data['session_id'] === 'test 2'); // issue #145, serializer with objects. $this->redis->set('x', [new stdClass, new stdClass]); $x = $this->redis->get('x'); $this->assertTrue(is_array($x)); if ($mode === Redis::SERIALIZER_JSON) { $this->assertTrue(is_array($x[0])); $this->assertTrue(is_array($x[1])); } else { $this->assertTrue(is_object($x[0]) && get_class($x[0]) === 'stdClass'); $this->assertTrue(is_object($x[1]) && get_class($x[1]) === 'stdClass'); } // revert $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE); // set ok $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok } public function testCompressionLZF() { if (!defined('Redis::COMPRESSION_LZF')) { $this->markTestSkipped(); } /* Don't crash on improperly compressed LZF data */ $payload = 'not-actually-lzf-compressed'; $this->redis->set('badlzf', $payload); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_LZF); $this->assertEquals($payload, $this->redis->get('badlzf')); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); $this->checkCompression(Redis::COMPRESSION_LZF, 0); } public function testCompressionZSTD() { if (!defined('Redis::COMPRESSION_ZSTD')) { $this->markTestSkipped(); } /* Issue 1936 regression. Make sure we don't overflow on bad data */ $this->redis->del('badzstd'); $this->redis->set('badzstd', '123'); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_ZSTD); $this->assertEquals('123', $this->redis->get('badzstd')); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); $this->checkCompression(Redis::COMPRESSION_ZSTD, 0); $this->checkCompression(Redis::COMPRESSION_ZSTD, 9); } public function testCompressionLZ4() { if (!defined('Redis::COMPRESSION_LZ4')) { $this->markTestSkipped(); } $this->checkCompression(Redis::COMPRESSION_LZ4, 0); $this->checkCompression(Redis::COMPRESSION_LZ4, 9); } private function checkCompression($mode, $level) { $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION, $mode) === TRUE); // set ok $this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION) === $mode); // get ok $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION_LEVEL, $level) === TRUE); $this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION_LEVEL) === $level); $val = 'xxxxxxxxxx'; $this->redis->set('key', $val); $this->assertEquals($val, $this->redis->get('key')); /* Empty data */ $this->redis->set('key', ''); $this->assertEquals('', $this->redis->get('key')); /* Iterate through class sizes */ for ($i = 1; $i <= 65536; $i *= 2) { foreach ([str_repeat('A', $i), random_bytes($i)] as $val) { $this->redis->set('key', $val); $this->assertEquals($val, $this->redis->get('key')); } } // Issue 1945. Ensure we decompress data with hmget. $this->redis->hset('hkey', 'data', 'abc'); $this->assertEquals('abc', current($this->redis->hmget('hkey', ['data']))); } public function testDumpRestore() { if (version_compare($this->version, "2.5.0") < 0) { $this->markTestSkipped(); } $this->redis->del('foo'); $this->redis->del('bar'); $this->redis->set('foo', 'this-is-foo'); $this->redis->set('bar', 'this-is-bar'); $d_foo = $this->redis->dump('foo'); $d_bar = $this->redis->dump('bar'); $this->redis->del('foo'); $this->redis->del('bar'); // Assert returns from restore $this->assertTrue($this->redis->restore('foo', 0, $d_bar)); $this->assertTrue($this->redis->restore('bar', 0, $d_foo)); // Now check that the keys have switched $this->assertTrue($this->redis->get('foo') === 'this-is-bar'); $this->assertTrue($this->redis->get('bar') === 'this-is-foo'); /* Test that we can REPLACE a key */ $this->assertTrue($this->redis->set('foo', 'some-value')); $this->assertTrue($this->redis->restore('foo', 0, $d_bar, ['REPLACE'])); /* Ensure we can set an absolute TTL */ $this->assertTrue($this->redis->restore('foo', time() + 10, $d_bar, ['REPLACE', 'ABSTTL'])); $this->assertTrue($this->redis->ttl('foo') <= 10); /* Ensure we can set an IDLETIME */ $this->assertTrue($this->redis->restore('foo', 0, $d_bar, ['REPLACE', 'IDLETIME' => 200])); $this->assertTrue($this->redis->object('idletime', 'foo') > 100); /* We can't neccissarily check this depending on LRU policy, but at least attempt to use the FREQ option */ $this->assertTrue($this->redis->restore('foo', 0, $d_bar, ['REPLACE', 'FREQ' => 200])); $this->redis->del('foo'); $this->redis->del('bar'); } public function testGetLastError() { // We shouldn't have any errors now $this->assertTrue($this->redis->getLastError() === NULL); // test getLastError with a regular command $this->redis->set('x', 'a'); $this->assertFalse($this->redis->incr('x')); $incrError = $this->redis->getLastError(); $this->assertTrue(strlen($incrError) > 0); // clear error $this->redis->clearLastError(); $this->assertTrue($this->redis->getLastError() === NULL); } // Helper function to compare nested results -- from the php.net array_diff page, I believe private function array_diff_recursive($aArray1, $aArray2) { $aReturn = []; foreach ($aArray1 as $mKey => $mValue) { if (array_key_exists($mKey, $aArray2)) { if (is_array($mValue)) { $aRecursiveDiff = $this->array_diff_recursive($mValue, $aArray2[$mKey]); if (count($aRecursiveDiff)) { $aReturn[$mKey] = $aRecursiveDiff; } } else { if ($mValue != $aArray2[$mKey]) { $aReturn[$mKey] = $mValue; } } } else { $aReturn[$mKey] = $mValue; } } return $aReturn; } public function testScript() { if (version_compare($this->version, "2.5.0") < 0) { $this->markTestSkipped(); } // Flush any scripts we have $this->assertTrue($this->redis->script('flush')); // Silly scripts to test against $s1_src = 'return 1'; $s1_sha = sha1($s1_src); $s2_src = 'return 2'; $s2_sha = sha1($s2_src); $s3_src = 'return 3'; $s3_sha = sha1($s3_src); // None should exist $result = $this->redis->script('exists', $s1_sha, $s2_sha, $s3_sha); $this->assertTrue(is_array($result) && count($result) == 3); $this->assertTrue(is_array($result) && count(array_filter($result)) == 0); // Load them up $this->assertTrue($this->redis->script('load', $s1_src) == $s1_sha); $this->assertTrue($this->redis->script('load', $s2_src) == $s2_sha); $this->assertTrue($this->redis->script('load', $s3_src) == $s3_sha); // They should all exist $result = $this->redis->script('exists', $s1_sha, $s2_sha, $s3_sha); $this->assertTrue(is_array($result) && count(array_filter($result)) == 3); } public function testEval() { if (version_compare($this->version, "2.5.0") < 0) { $this->markTestSkipped(); } /* The eval_ro method uses the same underlying handlers as eval so we only need to verify we can call it. */ if ($this->minVersionCheck('7.0.0')) $this->assertEquals('1.55', $this->redis->eval_ro("return '1.55'")); // Basic single line response tests $this->assertTrue(1 == $this->redis->eval('return 1')); $this->assertTrue(1.55 == $this->redis->eval("return '1.55'")); $this->assertTrue("hello, world" == $this->redis->eval("return 'hello, world'")); /* * Keys to be incorporated into lua results */ // Make a list $this->redis->del('{eval-key}-list'); $this->redis->rpush('{eval-key}-list', 'a'); $this->redis->rpush('{eval-key}-list', 'b'); $this->redis->rpush('{eval-key}-list', 'c'); // Make a set $this->redis->del('{eval-key}-zset'); $this->redis->zadd('{eval-key}-zset', 0, 'd'); $this->redis->zadd('{eval-key}-zset', 1, 'e'); $this->redis->zadd('{eval-key}-zset', 2, 'f'); // Basic keys $this->redis->set('{eval-key}-str1', 'hello, world'); $this->redis->set('{eval-key}-str2', 'hello again!'); // Use a script to return our list, and verify its response $list = $this->redis->eval("return redis.call('lrange', KEYS[1], 0, -1)", ['{eval-key}-list'], 1); $this->assertTrue($list === ['a','b','c']); // Use a script to return our zset $zset = $this->redis->eval("return redis.call('zrange', KEYS[1], 0, -1)", ['{eval-key}-zset'], 1); $this->assertTrue($zset == ['d','e','f']); // Test an empty MULTI BULK response $this->redis->del('{eval-key}-nolist'); $empty_resp = $this->redis->eval("return redis.call('lrange', '{eval-key}-nolist', 0, -1)", ['{eval-key}-nolist'], 1); $this->assertTrue(is_array($empty_resp) && empty($empty_resp)); // Now test a nested reply $nested_script = " return { 1,2,3, { redis.call('get', '{eval-key}-str1'), redis.call('get', '{eval-key}-str2'), redis.call('lrange', 'not-any-kind-of-list', 0, -1), { redis.call('zrange','{eval-key}-zset', 0, -1), redis.call('lrange', '{eval-key}-list', 0, -1) } } } "; $expected = [ 1, 2, 3, [ 'hello, world', 'hello again!', [], [ ['d','e','f'], ['a','b','c'] ] ] ]; // Now run our script, and check our values against each other $eval_result = $this->redis->eval($nested_script, ['{eval-key}-str1', '{eval-key}-str2', '{eval-key}-zset', '{eval-key}-list'], 4); $this->assertTrue(is_array($eval_result) && count($this->array_diff_recursive($eval_result, $expected)) == 0); /* * Nested reply wihin a multi/pipeline block */ $num_scripts = 10; $arr_modes = [Redis::MULTI]; if ($this->havePipeline()) $arr_modes[] = Redis::PIPELINE; foreach($arr_modes as $mode) { $this->redis->multi($mode); for($i=0;$i<$num_scripts;$i++) { $this->redis->eval($nested_script, ['{eval-key}-dummy'], 1); } $replies = $this->redis->exec(); foreach($replies as $reply) { $this->assertTrue(is_array($reply) && count($this->array_diff_recursive($reply, $expected)) == 0); } } /* * KEYS/ARGV */ $args_script = "return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}"; $args_args = ['{k}1','{k}2','{k}3','v1','v2','v3']; $args_result = $this->redis->eval($args_script, $args_args, 3); $this->assertTrue($args_result === $args_args); // turn on key prefixing $this->redis->setOption(Redis::OPT_PREFIX, 'prefix:'); $args_result = $this->redis->eval($args_script, $args_args, 3); // Make sure our first three are prefixed for($i=0;$iassertTrue($args_result[$i] == 'prefix:' . $args_args[$i]); } else { // Should not be prefixed $this->assertTrue($args_result[$i] == $args_args[$i]); } } } public function testEvalSHA() { if (version_compare($this->version, "2.5.0") < 0) { $this->markTestSkipped(); } // Flush any loaded scripts $this->redis->script('flush'); // Non existant script (but proper sha1), and a random (not) sha1 string $this->assertFalse($this->redis->evalsha(sha1(uniqid()))); $this->assertFalse($this->redis->evalsha('some-random-data')); // Load a script $cb = uniqid(); // To ensure the script is new $scr = "local cb='$cb' return 1"; $sha = sha1($scr); // Run it when it doesn't exist, run it with eval, and then run it with sha1 $this->assertTrue(false === $this->redis->evalsha($scr)); $this->assertTrue(1 === $this->redis->eval($scr)); $this->assertTrue(1 === $this->redis->evalsha($sha)); /* Our evalsha_ro handler is the same as evalsha so just make sure we can invoke the command */ if ($this->minVersionCheck('7.0.0')) $this->assertEquals(1, $this->redis->evalsha_ro($sha)); } public function testSerialize() { $vals = [1, 1.5, 'one', ['here','is','an','array']]; // Test with no serialization at all $this->assertTrue($this->redis->_serialize('test') === 'test'); $this->assertTrue($this->redis->_serialize(1) === '1'); $this->assertTrue($this->redis->_serialize([]) === 'Array'); $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object'); $arr_serializers = [Redis::SERIALIZER_PHP]; if(defined('Redis::SERIALIZER_IGBINARY')) { $arr_serializers[] = Redis::SERIALIZER_IGBINARY; } if(defined('Redis::SERIALIZER_MSGPACK')) { $arr_serializers[] = Redis::SERIALIZER_MSGPACK; } foreach($arr_serializers as $mode) { $arr_enc = []; $arr_dec = []; foreach($vals as $k => $v) { $enc = $this->redis->_serialize($v); $dec = $this->redis->_unserialize($enc); // They should be the same $this->assertTrue($enc == $dec); } } } public function testUnserialize() { $vals = [ 1,1.5,'one',['this','is','an','array'] ]; $serializers = Array(Redis::SERIALIZER_PHP); if(defined('Redis::SERIALIZER_IGBINARY')) { $serializers[] = Redis::SERIALIZER_IGBINARY; } if(defined('Redis::SERIALIZER_MSGPACK')) { $serializers[] = Redis::SERIALIZER_MSGPACK; } foreach($serializers as $mode) { $vals_enc = []; // Pass them through redis so they're serialized foreach($vals as $key => $val) { $this->redis->setOption(Redis::OPT_SERIALIZER, $mode); $key = "key" . ++$key; $this->redis->del($key); $this->redis->set($key, $val); // Clear serializer, get serialized value $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); $vals_enc[] = $this->redis->get($key); } // Run through our array comparing values for($i=0;$iredis->setOption(Redis::OPT_SERIALIZER, $mode); $this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i])); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); } } } public function testCompressHelpers() { $compressors = self::getAvailableCompression(); $vals = ['foo', 12345, random_bytes(128), '']; $oldcmp = $this->redis->getOption(Redis::OPT_COMPRESSION); foreach ($compressors as $cmp) { foreach ($vals as $val) { $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp); $this->redis->set('cmpkey', $val); /* Get the value raw */ $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); $raw = $this->redis->get('cmpkey'); $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp); $this->assertEquals($raw, $this->redis->_compress($val)); $uncompressed = $this->redis->get('cmpkey'); $this->assertEquals($uncompressed, $this->redis->_uncompress($raw)); } } $this->redis->setOption(Redis::OPT_COMPRESSION, $oldcmp); } public function testPackHelpers() { list ($oldser, $oldcmp) = [ $this->redis->getOption(Redis::OPT_SERIALIZER), $this->redis->getOption(Redis::OPT_COMPRESSION) ]; foreach ($this->serializers as $ser) { $compressors = self::getAvailableCompression(); foreach ($compressors as $cmp) { $this->redis->setOption(Redis::OPT_SERIALIZER, $ser); $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp); foreach (['foo', 12345, random_bytes(128), '', ['an', 'array']] as $v) { /* Can only attempt the array if we're serializing */ if (is_array($v) && $ser == Redis::SERIALIZER_NONE) continue; $this->redis->set('packkey', $v); /* Get the value raw */ $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); $raw = $this->redis->get('packkey'); $this->redis->setOption(Redis::OPT_SERIALIZER, $ser); $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp); $this->assertEquals($raw, $this->redis->_pack($v)); $unpacked = $this->redis->get('packkey'); $this->assertEquals($unpacked, $this->redis->_unpack($raw)); } } } $this->redis->setOption(Redis::OPT_SERIALIZER, $oldser); $this->redis->setOption(Redis::OPT_COMPRESSION, $oldcmp); } public function testPrefix() { // no prefix $this->redis->setOption(Redis::OPT_PREFIX, ''); $this->assertTrue('key' == $this->redis->_prefix('key')); // with a prefix $this->redis->setOption(Redis::OPT_PREFIX, 'some-prefix:'); $this->assertTrue('some-prefix:key' == $this->redis->_prefix('key')); // Clear prefix $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testReplyLiteral() { $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false); $this->assertTrue($this->redis->rawCommand('set', 'foo', 'bar')); $this->assertTrue($this->redis->eval("return redis.call('set', 'foo', 'bar')", [], 0)); $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1); $this->assertEquals([true, true], $rv); $this->redis->setOption(Redis::OPT_REPLY_LITERAL, true); $this->assertEquals('OK', $this->redis->rawCommand('set', 'foo', 'bar')); $this->assertEquals('OK', $this->redis->eval("return redis.call('set', 'foo', 'bar')", [], 0)); // Nested $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1); $this->assertEquals(['OK', 'PONG'], $rv); // Reset $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false); } public function testNullArray() { $key = "key:arr"; $this->redis->del($key); foreach ([false => [], true => NULL] as $opt => $test) { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt); $r = $this->redis->rawCommand("BLPOP", $key, .05); $this->assertEquals($test, $r); $this->redis->multi(); $this->redis->rawCommand("BLPOP", $key, .05); $r = $this->redis->exec(); $this->assertEquals([$test], $r); } $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } /* Test that we can configure PhpRedis to return NULL for *-1 even nestedwithin replies */ public function testNestedNullArray() { $this->redis->del('{notaset}'); foreach ([false => [], true => NULL] as $opt => $test) { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt); $this->assertEquals([$test, $test], $this->redis->geoPos('{notaset}', 'm1', 'm2')); $this->redis->multi(); $this->redis->geoPos('{notaset}', 'm1', 'm2'); $this->assertEquals([[$test, $test]], $this->redis->exec()); } $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } public function testConfig() { /* GET */ $cfg = $this->redis->config('GET', 'timeout'); $this->assertTrue(is_array($cfg) && isset($cfg['timeout'])); $sec = $cfg['timeout']; /* SET */ foreach ([$sec + 30, $sec] as $val) { $this->assertTrue($this->redis->config('SET', 'timeout', $val)); $cfg = $this->redis->config('GET', 'timeout'); $this->assertTrue(isset($cfg['timeout']) && $cfg['timeout'] == $val); } /* RESETSTAT */ $c1 = count($this->redis->info('commandstats')); $this->assertTrue($this->redis->config('resetstat')); $this->assertTrue(count($this->redis->info('commandstats')) < $c1); /* Ensure invalid calls are handled by PhpRedis */ foreach (['notacommand', 'get', 'set'] as $cmd) { $this->assertFalse(@$this->redis->config($cmd)); } $this->assertFalse(@$this->redis->config('set', 'foo')); /* REWRITE. We don't care if it actually works, just that the command be attempted */ $res = $this->redis->config('rewrite'); $this->assertTrue(is_bool($res)); if ($res == false) { $this->assertPatternMatch($this->redis->getLastError(), '/.*config.*/'); $this->redis->clearLastError(); } if (!$this->minVersionCheck("7.0.0")) return; /* Test getting multiple values */ $settings = $this->redis->config('get', ['timeout', 'databases', 'set-max-intset-entries']); $this->assertTrue(is_array($settings) && isset($settings['timeout']) && isset($settings['databases']) && isset($settings['set-max-intset-entries'])); /* Short circuit if the above assertion would have failed */ if ( ! is_array($settings) || ! isset($settings['timeout']) || ! isset($settings['set-max-intset-entries'])) return; list($timeout, $max_intset) = [$settings['timeout'], $settings['set-max-intset-entries']]; $updates = [ ['timeout' => (string)($timeout + 30), 'set-max-intset-entries' => (string)($max_intset + 128)], ['timeout' => (string)($timeout), 'set-max-intset-entries' => (string)$max_intset], ]; foreach ($updates as $update) { $this->assertTrue($this->redis->config('set', $update)); $vals = $this->redis->config('get', array_keys($update)); ksort($vals); ksort($update); $this->assertEquals($vals, $update); } /* Make sure PhpRedis catches malformed multiple get/set calls */ $this->assertFalse(@$this->redis->config('get', [])); $this->assertFalse(@$this->redis->config('set', [])); $this->assertFalse(@$this->redis->config('set', [0, 1, 2])); } public function testReconnectSelect() { $key = 'reconnect-select'; $value = 'Has been set!'; $original_cfg = $this->redis->config('GET', 'timeout'); // Make sure the default DB doesn't have the key. $this->redis->select(0); $this->redis->del($key); // Set the key on a different DB. $this->redis->select(5); $this->redis->set($key, $value); // Time out after 1 second. $this->redis->config('SET', 'timeout', '1'); // Wait for the connection to time out. On very old versions // of Redis we need to wait much longer (TODO: Investigate // which version exactly) sleep($this->minVersionCheck('3.0.0') ? 2 : 11); // Make sure we're still using the same DB. $this->assertEquals($value, $this->redis->get($key)); // Revert the setting. $this->redis->config('SET', 'timeout', $original_cfg['timeout']); } public function testTime() { if (version_compare($this->version, "2.5.0") < 0) { $this->markTestSkipped(); } $time_arr = $this->redis->time(); $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && strval(intval($time_arr[0])) === strval($time_arr[0]) && strval(intval($time_arr[1])) === strval($time_arr[1])); } public function testReadTimeoutOption() { $this->assertTrue(defined('Redis::OPT_READ_TIMEOUT')); $this->redis->setOption(Redis::OPT_READ_TIMEOUT, "12.3"); $this->assertEquals(12.3, $this->redis->getOption(Redis::OPT_READ_TIMEOUT)); } public function testIntrospection() { // Simple introspection tests $this->assertTrue($this->redis->getHost() === $this->getHost()); $this->assertTrue($this->redis->getPort() === $this->getPort()); $this->assertTrue($this->redis->getAuth() === $this->getAuth()); } public function testTransferredBytes() { $this->redis->set('key', 'val'); $this->redis->clearTransferredBytes(); $get_tx_resp = "*3\r\n$3\r\nGET\r\n$3\r\nkey\r\n"; $get_rx_resp = "$3\r\nval\r\n"; $this->assertEquals('val', $this->redis->get('key')); list ($tx, $rx) = $this->redis->getTransferredBytes(); $this->assertEquals(strlen($get_tx_resp), $tx); $this->assertEquals(strlen($get_rx_resp), $rx); $this->redis->clearTransferredBytes(); $this->redis->multi()->get('key')->get('key')->exec(); list($tx, $rx) = $this->redis->getTransferredBytes(); $this->assertEquals($tx, strlen("*1\r\n$5\r\nMULTI\r\n*1\r\n$4\r\nEXEC\r\n") + 2 * strlen($get_tx_resp)); $this->assertEquals($rx, strlen("+OK\r\n") + strlen("+QUEUED\r\n+QUEUED\r\n") + strlen("*2\r\n") + 2 * strlen($get_rx_resp)); } /** * Scan and variants */ protected function get_keyspace_count($str_db) { $arr_info = $this->redis->info(); if (isset($arr_info[$str_db])) { $arr_info = $arr_info[$str_db]; $arr_info = explode(',', $arr_info); $arr_info = explode('=', $arr_info[0]); return $arr_info[1]; } else { return 0; } } public function testScan() { if(version_compare($this->version, "2.8.0") < 0) { $this->markTestSkipped(); return; } // Key count $i_key_count = $this->get_keyspace_count('db0'); // Have scan retry $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); // Scan them all $it = NULL; while($arr_keys = $this->redis->scan($it)) { $i_key_count -= count($arr_keys); } // Should have iterated all keys $this->assertEquals(0, $i_key_count); // Unique keys, for pattern matching $str_uniq = uniqid() . '-' . uniqid(); for($i=0;$i<10;$i++) { $this->redis->set($str_uniq . "::$i", "bar::$i"); } // Scan just these keys using a pattern match $it = NULL; while($arr_keys = $this->redis->scan($it, "*$str_uniq*")) { $i -= count($arr_keys); } $this->assertEquals(0, $i); // SCAN with type is scheduled for release in Redis 6. if (version_compare($this->version, "6.0.0") >= 0) { // Use a unique ID so we can find our type keys $id = uniqid(); // Create some simple keys and lists for ($i = 0; $i < 3; $i++) { $str_simple = "simple:{$id}:$i"; $str_list = "list:{$id}:$i"; $this->redis->set($str_simple, $i); $this->redis->del($str_list); $this->redis->rpush($str_list, ['foo']); $arr_keys["STRING"][] = $str_simple; $arr_keys["LIST"][] = $str_list; } // Make sure we can scan for specific types foreach ($arr_keys as $str_type => $arr_vals) { foreach ([0, 10] as $i_count) { $arr_resp = []; $it = NULL; while ($arr_scan = $this->redis->scan($it, "*$id*", $i_count, $str_type)) { $arr_resp = array_merge($arr_resp, $arr_scan); } sort($arr_vals); sort($arr_resp); $this->assertEquals($arr_vals, $arr_resp); } } } } public function testScanPrefix() { $keyid = uniqid(); /* Set some keys with different prefixes */ $arr_prefixes = ['prefix-a:', 'prefix-b:']; foreach ($arr_prefixes as $str_prefix) { $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix); $this->redis->set("$keyid", "LOLWUT"); $arr_all_keys["{$str_prefix}{$keyid}"] = true; } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX); foreach ($arr_prefixes as $str_prefix) { $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix); $it = NULL; $arr_keys = $this->redis->scan($it, "*$keyid*"); $this->assertEquals($arr_keys, ["{$str_prefix}{$keyid}"]); } /* Unset the prefix option */ $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX); $it = NULL; while ($arr_keys = $this->redis->scan($it, "*$keyid*")) { foreach ($arr_keys as $str_key) { unset($arr_all_keys[$str_key]); } } /* Should have touched every key */ $this->assertTrue(count($arr_all_keys) == 0); } public function testMaxRetriesOption() { $maxRetriesExpected = 5; $this->redis->setOption(Redis::OPT_MAX_RETRIES, $maxRetriesExpected); $maxRetriesActual=$this->redis->getOption(Redis::OPT_MAX_RETRIES); $this->assertEquals($maxRetriesActual, $maxRetriesExpected); } public function testBackoffOptions() { $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DEFAULT); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DEFAULT); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_CONSTANT); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_CONSTANT); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_UNIFORM); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_UNIFORM); $this->redis -> setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EXPONENTIAL); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EXPONENTIAL); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EQUAL_JITTER); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EQUAL_JITTER); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_FULL_JITTER); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_FULL_JITTER); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER); $this->assertFalse($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, 55555)); $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 500); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 500); $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 750); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 750); $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 500); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 500); $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 750); $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 750); } public function testHScan() { if (version_compare($this->version, "2.8.0") < 0) { $this->markTestSkipped(); return; } // Never get empty sets $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->del('hash'); $i_foo_mems = 0; for($i=0;$i<100;$i++) { if($i>3) { $this->redis->hset('hash', "member:$i", "value:$i"); } else { $this->redis->hset('hash', "foomember:$i", "value:$i"); $i_foo_mems++; } } // Scan all of them $it = NULL; while($arr_keys = $this->redis->hscan('hash', $it)) { $i -= count($arr_keys); } $this->assertEquals(0, $i); // Scan just *foomem* (should be 4) $it = NULL; while($arr_keys = $this->redis->hscan('hash', $it, '*foomember*')) { $i_foo_mems -= count($arr_keys); foreach($arr_keys as $str_mem => $str_val) { $this->assertTrue(strpos($str_mem, 'member')!==FALSE); $this->assertTrue(strpos($str_val, 'value')!==FALSE); } } $this->assertEquals(0, $i_foo_mems); } public function testSScan() { if (version_compare($this->version, "2.8.0") < 0) { $this->markTestSkipped(); return; } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->del('set'); for($i=0;$i<100;$i++) { $this->redis->sadd('set', "member:$i"); } // Scan all of them $it = NULL; while($arr_keys = $this->redis->sscan('set', $it)) { $i -= count($arr_keys); foreach($arr_keys as $str_mem) { $this->assertTrue(strpos($str_mem,'member')!==FALSE); } } $this->assertEquals(0, $i); // Scan just ones with zero in them (0, 10, 20, 30, 40, 50, 60, 70, 80, 90) $it = NULL; $i_w_zero = 0; while($arr_keys = $this->redis->sscan('set', $it, '*0*')) { $i_w_zero += count($arr_keys); } $this->assertEquals(10, $i_w_zero); } public function testZScan() { if (version_compare($this->version, "2.8.0") < 0) { $this->markTestSkipped(); return; } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->del('zset'); $i_tot_score = 0; $i_p_score = 0; $i_p_count = 0; for($i=0;$i<2000;$i++) { if($i<10) { $this->redis->zadd('zset', $i, "pmem:$i"); $i_p_score += $i; $i_p_count += 1; } else { $this->redis->zadd('zset', $i, "mem:$i"); } $i_tot_score += $i; } // Scan them all $it = NULL; while($arr_keys = $this->redis->zscan('zset', $it)) { foreach($arr_keys as $str_mem => $f_score) { $i_tot_score -= $f_score; $i--; } } $this->assertEquals(0, $i); $this->assertEquals((float)0, $i_tot_score); // Just scan "pmem" members $it = NULL; $i_p_score_old = $i_p_score; $i_p_count_old = $i_p_count; while($arr_keys = $this->redis->zscan('zset', $it, "*pmem*")) { foreach($arr_keys as $str_mem => $f_score) { $i_p_score -= $f_score; $i_p_count -= 1; } } $this->assertEquals((float)0, $i_p_score); $this->assertEquals(0, $i_p_count); // Turn off retrying and we should get some empty results $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); $i_skips = 0; $i_p_score = $i_p_score_old; $i_p_count = $i_p_count_old; $it = NULL; while(($arr_keys = $this->redis->zscan('zset', $it, "*pmem*")) !== FALSE) { if(count($arr_keys) == 0) $i_skips++; foreach($arr_keys as $str_mem => $f_score) { $i_p_score -= $f_score; $i_p_count -= 1; } } // We should still get all the keys, just with several empty results $this->assertTrue($i_skips > 0); $this->assertEquals((float)0, $i_p_score); $this->assertEquals(0, $i_p_count); } /* Make sure we capture errors when scanning */ public function testScanErrors() { $this->redis->set('scankey', 'simplekey'); foreach (['sScan', 'hScan', 'zScan'] as $str_method) { $it = NULL; $this->redis->$str_method('scankey', $it); $this->assertTrue(strpos($this->redis->getLastError(), 'WRONGTYPE') === 0); } } // // HyperLogLog (PF) commands // protected function createPFKey($str_key, $i_count) { $arr_mems = []; for($i=0;$i<$i_count;$i++) { $arr_mems[] = uniqid() . '-' . $i; } // Estimation by Redis $this->redis->pfadd($str_key, $i_count); } public function testPFCommands() { // Isn't available until 2.8.9 if (version_compare($this->version, "2.8.9") < 0) { $this->markTestSkipped(); return; } $str_uniq = uniqid(); $arr_mems = []; for($i=0;$i<1000;$i++) { if($i%2 == 0) { $arr_mems[] = $str_uniq . '-' . $i; } else { $arr_mems[] = $i; } } // How many keys to create $i_keys = 10; // Iterate prefixing/serialization options foreach([Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP] as $str_ser) { foreach(['', 'hl-key-prefix:'] as $str_prefix) { $arr_keys = []; // Now add for each key for($i=0;$i<$i_keys;$i++) { $str_key = "{key}:$i"; $arr_keys[] = $str_key; // Clean up this key $this->redis->del($str_key); // Add to our cardinality set, and confirm we got a valid response $this->assertTrue($this->redis->pfadd($str_key, $arr_mems)); // Grab estimated cardinality $i_card = $this->redis->pfcount($str_key); $this->assertTrue(is_int($i_card)); // Count should be close $this->assertLess(abs($i_card-count($arr_mems)), count($arr_mems) * .1); // The PFCOUNT on this key should be the same as the above returned response $this->assertEquals($this->redis->pfcount($str_key), $i_card); } // Clean up merge key $this->redis->del('pf-merge-{key}'); // Merge the counters $this->assertTrue($this->redis->pfmerge('pf-merge-{key}', $arr_keys)); // Validate our merged count $i_redis_card = $this->redis->pfcount('pf-merge-{key}'); // Merged cardinality should still be roughly 1000 $this->assertLess(abs($i_redis_card-count($arr_mems)), count($arr_mems) * .1); // Clean up merge key $this->redis->del('pf-merge-{key}'); } } } // // GEO* command tests // protected function rawCommandArray($key, $args) { return call_user_func_array([$this->redis, 'rawCommand'], $args); } protected function addCities($key) { $this->redis->del($key); foreach ($this->cities as $city => $longlat) { $this->redis->geoadd($key, $longlat[0], $longlat[1], $city); } } /* GEOADD */ public function testGeoAdd() { if (!$this->minVersionCheck("3.2")) { return $this->markTestSkipped(); } $this->redis->del('geokey'); /* Add them one at a time */ foreach ($this->cities as $city => $longlat) { $this->assertEquals($this->redis->geoadd('geokey', $longlat[0], $longlat[1], $city), 1); } /* Add them again, all at once */ $args = ['geokey']; foreach ($this->cities as $city => $longlat) { $args = array_merge($args, [$longlat[0], $longlat[1], $city]); } /* They all exist, should be nothing added */ $this->assertEquals(call_user_func_array([$this->redis, 'geoadd'], $args), 0); } /* GEORADIUS */ public function genericGeoRadiusTest($cmd) { if (!$this->minVersionCheck("3.2.0")) { return $this->markTestSkipped(); } /* Chico */ $city = 'Chico'; $lng = -121.837478; $lat = 39.728494; $this->addCities('{gk}'); /* Pre tested with redis-cli. We're just verifying proper delivery of distance and unit */ if ($cmd == 'georadius' || $cmd == 'georadius_ro') { $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi'), Array('Chico')); $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); $args = Array($cmd, '{gk}', $lng, $lat, 500, 'mi'); /* Test a bad COUNT argument */ foreach (Array(-1, 0, 'notanumber') as $count) { $this->assertFalse(@$this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi', Array('count' => $count))); } } else { $this->assertEquals($this->redis->$cmd('{gk}', $city, 10, 'mi'), Array('Chico')); $this->assertEquals($this->redis->$cmd('{gk}', $city, 30, 'mi'), Array('Gridley','Chico')); $this->assertEquals($this->redis->$cmd('{gk}', $city, 50, 'km'), Array('Gridley','Chico')); $this->assertEquals($this->redis->$cmd('{gk}', $city, 50000, 'm'), Array('Gridley','Chico')); $this->assertEquals($this->redis->$cmd('{gk}', $city, 150000, 'ft'), Array('Gridley', 'Chico')); $args = Array($cmd, '{gk}', $city, 500, 'mi'); /* Test a bad COUNT argument */ foreach (Array(-1, 0, 'notanumber') as $count) { $this->assertFalse(@$this->redis->$cmd('{gk}', $city, 10, 'mi', Array('count' => $count))); } } /* Options */ $opts = ['WITHCOORD', 'WITHDIST', 'WITHHASH']; $sortopts = ['', 'ASC', 'DESC']; $storeopts = ['', 'STORE', 'STOREDIST']; for ($i = 0; $i < count($opts); $i++) { $subopts = array_slice($opts, 0, $i); shuffle($subopts); $subargs = $args; foreach ($subopts as $opt) { $subargs[] = $opt; } /* Cannot mix STORE[DIST] with the WITH* arguments */ $realstoreopts = count($subopts) == 0 ? $storeopts : []; $base_subargs = $subargs; $base_subopts = $subopts; foreach ($realstoreopts as $store_type) { for ($c = 0; $c < 3; $c++) { $subargs = $base_subargs; $subopts = $base_subopts; /* Add a count if we're past first iteration */ if ($c > 0) { $subopts['count'] = $c; $subargs[] = 'count'; $subargs[] = $c; } /* Adding optional sort */ foreach ($sortopts as $sortopt) { $realargs = $subargs; $realopts = $subopts; if ($sortopt) { $realargs[] = $sortopt; $realopts[] = $sortopt; } if ($store_type) { $realopts[$store_type] = "{gk}-$store_type"; $realargs[] = $store_type; $realargs[] = "{gk}-$store_type"; } $ret1 = $this->rawCommandArray('{gk}', $realargs); if ($cmd == 'georadius' || $cmd == 'georadius_ro') { $ret2 = $this->redis->$cmd('{gk}', $lng, $lat, 500, 'mi', $realopts); } else { $ret2 = $this->redis->$cmd('{gk}', $city, 500, 'mi', $realopts); } $this->assertEquals($ret1, $ret2); } } } } } public function testGeoRadius() { if (!$this->minVersionCheck("3.2.0")) { return $this->markTestSkipped(); } $this->genericGeoRadiusTest('georadius'); $this->genericGeoRadiusTest('georadius_ro'); } public function testGeoRadiusByMember() { if (!$this->minVersionCheck("3.2.0")) { return $this->markTestSkipped(); } $this->genericGeoRadiusTest('georadiusbymember'); $this->genericGeoRadiusTest('georadiusbymember_ro'); } public function testGeoPos() { if (!$this->minVersionCheck("3.2.0")) { return $this->markTestSkipped(); } $this->addCities('gk'); $this->assertEquals($this->redis->geopos('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', ['geopos', 'gk', 'Chico', 'Sacramento'])); $this->assertEquals($this->redis->geopos('gk', 'Cupertino'), $this->rawCommandArray('gk', ['geopos', 'gk', 'Cupertino'])); } public function testGeoHash() { if (!$this->minVersionCheck("3.2.0")) { return $this->markTestSkipped(); } $this->addCities('gk'); $this->assertEquals($this->redis->geohash('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', ['geohash', 'gk', 'Chico', 'Sacramento'])); $this->assertEquals($this->redis->geohash('gk', 'Chico'), $this->rawCommandArray('gk', ['geohash', 'gk', 'Chico'])); } public function testGeoDist() { if (!$this->minVersionCheck("3.2.0")) { return $this->markTestSkipped(); } $this->addCities('gk'); $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino'); $r2 = $this->rawCommandArray('gk', ['geodist', 'gk', 'Chico', 'Cupertino']); $this->assertEquals(round($r1, 8), round($r2, 8)); $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino', 'km'); $r2 = $this->rawCommandArray('gk', ['geodist', 'gk', 'Chico', 'Cupertino', 'km']); $this->assertEquals(round($r1, 8), round($r2, 8)); } public function testGeoSearch() { if (!$this->minVersionCheck("6.2.0")) { return $this->markTestSkipped(); } $this->addCities('gk'); $this->assertEquals($this->redis->geosearch('gk', 'Chico', 1, 'm'), ['Chico']); $this->assertValidate($this->redis->geosearch('gk', 'Chico', 1, 'm', ['withcoord', 'withdist', 'withhash']), function ($v) { $this->assertArrayKey($v, 'Chico', 'is_array'); $this->assertEquals(count($v['Chico']), 3); $this->assertArrayKey($v['Chico'], 0, 'is_float'); $this->assertArrayKey($v['Chico'], 1, 'is_int'); $this->assertArrayKey($v['Chico'], 2, 'is_array'); return true; }); } public function testGeoSearchStore() { if (!$this->minVersionCheck("6.2.0")) { return $this->markTestSkipped(); } $this->addCities('{gk}src'); $this->assertEquals($this->redis->geosearchstore('{gk}dst', '{gk}src', 'Chico', 100, 'km'), 3); $this->assertEquals($this->redis->geosearch('{gk}dst', 'Chico', 1, 'm'), ['Chico']); } /* Test a 'raw' command */ public function testRawCommand() { $this->redis->set('mykey','some-value'); $str_result = $this->redis->rawCommand('get', 'mykey'); $this->assertEquals($str_result, 'some-value'); $this->redis->del('mylist'); $this->redis->rpush('mylist', 'A', 'B', 'C', 'D'); $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']); } /* STREAMS */ protected function addStreamEntries($key, $count) { $ids = []; $this->redis->del($key); for ($i = 0; $i < $count; $i++) { $ids[] = $this->redis->xAdd($key, '*', ['field' => "value:$i"]); } return $ids; } protected function addStreamsAndGroups($arr_streams, $count, $arr_groups) { $ids = []; foreach ($arr_streams as $str_stream) { $ids[$str_stream] = $this->addStreamEntries($str_stream, $count); foreach ($arr_groups as $str_group => $str_id) { $this->redis->xGroup('CREATE', $str_stream, $str_group, $str_id); } } return $ids; } public function testXAdd() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); $this->redis->del('stream'); for ($i = 0; $i < 5; $i++) { $id = $this->redis->xAdd("stream", '*', ['k1' => 'v1', 'k2' => 'v2']); $this->assertEquals($this->redis->xLen('stream'), $i+1); /* Redis should return - */ $bits = explode('-', $id); $this->assertEquals(count($bits), 2); $this->assertTrue(is_numeric($bits[0])); $this->assertTrue(is_numeric($bits[1])); } /* Test an absolute maximum length */ for ($i = 0; $i < 100; $i++) { $this->redis->xAdd('stream', '*', ['k' => 'v'], 10); } $this->assertEquals($this->redis->xLen('stream'), 10); /* Not the greatest test but I'm unsure if approximate trimming is * totally deterministic, so just make sure we are able to add with * an approximate maxlen argument structure */ $id = $this->redis->xAdd('stream', '*', ['k' => 'v'], 10, true); $this->assertEquals(count(explode('-', $id)), 2); /* Empty message should fail */ $this->redis->xAdd('stream', '*', []); } protected function doXRangeTest($reverse) { $key = '{stream}'; if ($reverse) { list($cmd,$a1,$a2) = ['xRevRange', '+', 0]; } else { list($cmd,$a1,$a2) = ['xRange', 0, '+']; } $this->redis->del($key); for ($i = 0; $i < 3; $i++) { $msg = ['field' => "value:$i"]; $id = $this->redis->xAdd($key, '*', $msg); $rows[$id] = $msg; } $messages = $this->redis->$cmd($key, $a1, $a2); $this->assertEquals(count($messages), 3); $i = $reverse ? 2 : 0; foreach ($messages as $seq => $v) { $this->assertEquals(count(explode('-', $seq)), 2); $this->assertEquals($v, ['field' => "value:$i"]); $i += $reverse ? -1 : 1; } /* Test COUNT option */ for ($count = 1; $count <= 3; $count++) { $messages = $this->redis->$cmd($key, $a1, $a2, $count); $this->assertEquals(count($messages), $count); } } public function testXRange() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); foreach ([false, true] as $reverse) { foreach ($this->serializers as $serializer) { foreach ([NULL, 'prefix:'] as $prefix) { $this->redis->setOption(Redis::OPT_PREFIX, $prefix); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $this->doXRangeTest($reverse); } } } } protected function testXLen() { if (!$this->minVersionCheck("5.0")) $this->markTestSkipped(); $this->redis->del('{stream}'); for ($i = 0; $i < 5; $i++) { $this->redis->xadd('{stream}', '*', ['foo' => 'bar']); $this->assertEquals($this->redis->xLen('{stream}'), $i+1); } } public function testXGroup() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); /* CREATE MKSTREAM */ $str_key = 's:' . uniqid(); $this->assertFalse($this->redis->xGroup('CREATE', $str_key, 'g0', 0)); $this->assertTrue($this->redis->xGroup('CREATE', $str_key, 'g1', 0, true)); /* XGROUP DESTROY */ $this->assertEquals($this->redis->xGroup('DESTROY', $str_key, 'g1'), 1); /* Populate some entries in stream 's' */ $this->addStreamEntries('s', 2); /* CREATE */ $this->assertTrue($this->redis->xGroup('CREATE', 's', 'mygroup', '$')); $this->assertFalse($this->redis->xGroup('CREATE', 's', 'mygroup', 'BAD_ID')); /* BUSYGROUP */ $this->redis->xGroup('CREATE', 's', 'mygroup', '$'); $this->assertTrue(strpos($this->redis->getLastError(), 'BUSYGROUP') === 0); /* SETID */ $this->assertTrue($this->redis->xGroup('SETID', 's', 'mygroup', '$')); $this->assertFalse($this->redis->xGroup('SETID', 's', 'mygroup', 'BAD_ID')); $this->assertEquals($this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer'),0); if (!$this->minVersionCheck('6.2.0')) return; /* CREATECONSUMER */ $this->assertTrue($this->redis->del('s')); $this->assertTrue($this->redis->xgroup('create', 's', 'mygroup', '$', true)); for ($i = 0; $i < 3; $i++) { $this->assertTrue($this->redis->xgroup('createconsumer', 's', 'mygroup', "c:$i")); $info = $this->redis->xinfo('consumers', 's', 'mygroup'); $this->assertTrue(is_array($info) && count($info) == $i + 1); for ($j = 0; $j <= $i; $j++) { $this->assertTrue(isset($info[$j]) && isset($info[$j]['name']) && $info[$j]['name'] == "c:$j"); } } /* Make sure we don't erroneously send options that don't belong to the operation */ $this->assertTrue($this->redis->xGroup('CREATECONSUMER', 's', 'mygroup', 'fake-consumer', true, 1337)); /* Make sure we handle the case where the user doesn't send enough arguments */ $this->redis->clearLastError(); $this->assertFalse(@$this->redis->xGroup('CREATECONSUMER')); $this->assertEquals(NULL, $this->redis->getLastError()); $this->assertFalse(@$this->redis->xGroup('create')); $this->assertEquals(NULL, $this->redis->getLastError()); if (!$this->minVersionCheck('7.0.0')) return; /* ENTRIESREAD */ $this->assertTrue($this->redis->del('s')); $this->assertTrue($this->redis->xGroup('create', 's', 'mygroup', '$', true, 1337)); $info = $this->redis->xinfo('groups', 's'); $this->assertTrue(isset($info[0]['entries-read']) && 1337 == (int)$info[0]['entries-read']); } public function testXAck() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); for ($n = 1; $n <= 3; $n++) { $this->addStreamsAndGroups(['{s}'], 3, ['g1' => 0]); $msg = $this->redis->xReadGroup('g1', 'c1', ['{s}' => '>']); /* Extract IDs */ $smsg = array_shift($msg); $ids = array_keys($smsg); /* Now ACK $n messages */ $ids = array_slice($ids, 0, $n); $this->assertEquals($this->redis->xAck('{s}', 'g1', $ids), $n); } /* Verify sending no IDs is a failure */ $this->assertFalse($this->redis->xAck('{s}', 'g1', [])); } protected function doXReadTest() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); $row = ['f1' => 'v1', 'f2' => 'v2']; $msgdata = [ '{stream}-1' => $row, '{stream}-2' => $row, ]; /* Append a bit of data and populate STREAM queries */ $this->redis->del(array_keys($msgdata)); foreach ($msgdata as $key => $message) { for ($r = 0; $r < 2; $r++) { $id = $this->redis->xAdd($key, '*', $message); $qresult[$key][$id] = $message; } $qzero[$key] = 0; $qnew[$key] = '$'; $keys[] = $key; } /* Everything from both streams */ $rmsg = $this->redis->xRead($qzero); $this->assertEquals($rmsg, $qresult); /* Test COUNT option */ for ($count = 1; $count <= 2; $count++) { $rmsg = $this->redis->xRead($qzero, $count); foreach ($keys as $key) { $this->assertEquals(count($rmsg[$key]), $count); } } /* Should be empty (no new entries) */ $this->assertEquals(count($this->redis->xRead($qnew)),0); /* Test against a specific ID */ $id = $this->redis->xAdd('{stream}-1', '*', $row); $new_id = $this->redis->xAdd('{stream}-1', '*', ['final' => 'row']); $rmsg = $this->redis->xRead(['{stream}-1' => $id]); $this->assertEquals( $this->redis->xRead(['{stream}-1' => $id]), ['{stream}-1' => [$new_id => ['final' => 'row']]] ); /* Emtpy query should fail */ $this->assertFalse($this->redis->xRead([])); } public function testXRead() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); foreach ($this->serializers as $serializer) { $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $this->doXReadTest(); } /* Don't need to test BLOCK multiple times */ $m1 = round(microtime(true)*1000); $this->redis->xRead(['somestream' => '$'], -1, 100); $m2 = round(microtime(true)*1000); $this->assertTrue($m2 - $m1 >= 100); } protected function compareStreamIds($redis, $control) { foreach ($control as $stream => $ids) { $rcount = count($redis[$stream]); $lcount = count($control[$stream]); /* We should have the same number of messages */ $this->assertEquals($rcount, $lcount); /* We should have the exact same IDs */ foreach ($ids as $k => $id) { $this->assertTrue(isset($redis[$stream][$id])); } } } public function testXReadGroup() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); /* Create some streams and groups */ $streams = ['{s}-1', '{s}-2']; $groups = ['g1' => 0, 'g2' => 0]; /* I'm not totally sure why Redis behaves this way, but we have to * send '>' first and then send ID '0' for subsequent xReadGroup calls * or Redis will not return any messages. This behavior changed from * redis 5.0.1 and 5.0.2 but doing it this way works for both versions. */ $qcount = 0; $query1 = ['{s}-1' => '>', '{s}-2' => '>']; $query2 = ['{s}-1' => '0', '{s}-2' => '0']; $ids = $this->addStreamsAndGroups($streams, 1, $groups); /* Test that we get get the IDs we should */ foreach (['g1', 'g2'] as $group) { foreach ($ids as $stream => $messages) { while ($ids[$stream]) { /* Read more messages */ $query = !$qcount++ ? $query1 : $query2; $resp = $this->redis->xReadGroup($group, 'consumer', $query); /* They should match with our local control array */ $this->compareStreamIds($resp, $ids); /* Remove a message from our control *and* XACK it in Redis */ $id = array_shift($ids[$stream]); $this->redis->xAck($stream, $group, [$id]); } } } /* Test COUNT option */ for ($c = 1; $c <= 3; $c++) { $this->addStreamsAndGroups($streams, 3, $groups); $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, $c); foreach ($resp as $stream => $smsg) { $this->assertEquals(count($smsg), $c); } } /* Test COUNT option with NULL (should be ignored) */ $this->addStreamsAndGroups($streams, 3, $groups, NULL); $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, NULL); foreach ($resp as $stream => $smsg) { $this->assertEquals(count($smsg), 3); } /* Finally test BLOCK with a sloppy timing test */ $t1 = $this->mstime(); $qnew = ['{s}-1' => '>', '{s}-2' => '>']; $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, 100); $t2 = $this->mstime(); $this->assertTrue($t2 - $t1 >= 100); /* Make sure passing NULL to block doesn't block */ $t1 = $this->mstime(); $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, NULL); $t2 = $this->mstime(); $this->assertTrue($t2 - $t1 < 100); /* Make sure passing bad values to BLOCK or COUNT immediately fails */ $this->assertFalse(@$this->redis->xReadGroup('g1', 'c1', $qnew, -1)); $this->assertFalse(@$this->redis->xReadGroup('g1', 'c1', $qnew, NULL, -1)); } public function testXPending() { if (!$this->minVersionCheck("5.0")) { return $this->markTestSkipped(); } $rows = 5; $this->addStreamsAndGroups(['s'], $rows, ['group' => 0]); $msg = $this->redis->xReadGroup('group', 'consumer', ['s' => 0]); $ids = array_keys($msg['s']); for ($n = count($ids); $n >= 0; $n--) { $xp = $this->redis->xPending('s', 'group'); $this->assertEquals($xp[0], count($ids)); /* Verify we're seeing the IDs themselves */ for ($idx = 1; $idx <= 2; $idx++) { if ($xp[$idx]) { $this->assertPatternMatch($xp[$idx], "/^[0-9].*-[0-9].*/"); } } if ($ids) { $id = array_shift($ids); $this->redis->xAck('s', 'group', [$id]); } } /* Ensure we can have NULL trailing arguments */ $this->assertTrue(is_array($this->redis->xpending('s', 'group', '-', '+', 1, null))); $this->assertTrue(is_array($this->redis->xpending('s', 'group', NULL, NULL, -1, NULL))); } public function testXDel() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); for ($n = 5; $n > 0; $n--) { $ids = $this->addStreamEntries('s', 5); $todel = array_slice($ids, 0, $n); $this->assertEquals($this->redis->xDel('s', $todel), count($todel)); } /* Empty array should fail */ $this->assertFalse($this->redis->xDel('s', [])); } public function testXTrim() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); for ($maxlen = 0; $maxlen <= 50; $maxlen += 10) { $this->addStreamEntries('stream', 100); $trimmed = $this->redis->xTrim('stream', $maxlen); $this->assertEquals($trimmed, 100 - $maxlen); } /* APPROX trimming isn't easily deterministic, so just make sure we can call it with the flag */ $this->addStreamEntries('stream', 100); $this->assertFalse($this->redis->xTrim('stream', 1, true) === false); /* We need Redis >= 6.2.0 for MINID and LIMIT options */ if (!$this->minVersionCheck("6.2.0")) return; $this->assertEquals(1, $this->redis->del('stream')); /* Test minid by generating a stream with more than one */ for ($i = 1; $i < 3; $i++) { for ($j = 0; $j < 3; $j++) { $this->redis->xadd('stream', "$i-$j", ['foo' => 'bar']); } } /* MINID of 2-0 */ $this->assertEquals(3, $this->redis->xtrim('stream', 2, false, true)); $this->assertEquals(['2-0', '2-1', '2-2'], array_keys($this->redis->xrange('stream', '0', '+'))); /* TODO: Figure oiut how to test LIMIT deterministically. For now just send a LIMIT and verify we don't get a failure from Redis. */ $this->assertTrue(is_int($this->redis->xtrim('stream', 2, true, false, 3))); } /* XCLAIM is one of the most complicated commands, with a great deal of different options * The following test attempts to verify every combination of every possible option. */ public function testXClaim() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); foreach ([0, 100] as $min_idle_time) { foreach ([false, true] as $justid) { foreach ([0, 10] as $retrycount) { /* We need to test not passing TIME/IDLE as well as passing either */ if ($min_idle_time == 0) { $topts = [[], ['IDLE', 1000000], ['TIME', time() * 1000]]; } else { $topts = [NULL]; } foreach ($topts as $tinfo) { if ($tinfo) { list($ttype, $tvalue) = $tinfo; } else { $ttype = NULL; $tvalue = NULL; } /* Add some messages and create a group */ $this->addStreamsAndGroups(['s'], 10, ['group1' => 0]); /* Create a second stream we can FORCE ownership on */ $fids = $this->addStreamsAndGroups(['f'], 10, ['group1' => 0]); $fids = $fids['f']; /* Have consumer 'Mike' read the messages */ $oids = $this->redis->xReadGroup('group1', 'Mike', ['s' => '>']); $oids = array_keys($oids['s']); /* We're only dealing with stream 's' */ /* Construct our options array */ $opts = []; if ($justid) $opts[] = 'JUSTID'; if ($retrycount) $opts['RETRYCOUNT'] = $retrycount; if ($tvalue !== NULL) $opts[$ttype] = $tvalue; /* Now have pavlo XCLAIM them */ $cids = $this->redis->xClaim('s', 'group1', 'Pavlo', $min_idle_time, $oids, $opts); if (!$justid) $cids = array_keys($cids); if ($min_idle_time == 0) { $this->assertEquals($cids, $oids); /* Append the FORCE option to our second stream where we have not already * assigned to a PEL group */ $opts[] = 'FORCE'; $freturn = $this->redis->xClaim('f', 'group1', 'Test', 0, $fids, $opts); if (!$justid) $freturn = array_keys($freturn); $this->assertEquals($freturn, $fids); if ($retrycount || $tvalue !== NULL) { $pending = $this->redis->xPending('s', 'group1', 0, '+', 1, 'Pavlo'); if ($retrycount) { $this->assertEquals($pending[0][3], $retrycount); } if ($tvalue !== NULL) { if ($ttype == 'IDLE') { /* If testing IDLE the value must be >= what we set */ $this->assertTrue($pending[0][2] >= $tvalue); } else { /* Timing tests are notoriously irritating but I don't see * how we'll get >= 20,000 ms between XCLAIM and XPENDING no * matter how slow the machine/VM running the tests is */ $this->assertTrue($pending[0][2] <= 20000); } } } } else { /* We're verifying that we get no messages when we've set 100 seconds * as our idle time, which should match nothing */ $this->assertEquals($cids, []); } } } } } } /* Make sure our XAUTOCLAIM handler works */ public function testXAutoClaim() { $this->redis->del('ships'); $this->redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); // Test an empty xautoclaim reply $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); $this->assertEquals(['0-0', [], []], $res); $this->redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); // Consume the ['name' => 'Defiant'] message $this->redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1); // The "Jem'Hadar" consumer has the message presently $pending = $this->redis->xPending('ships', 'combatants'); $this->assertTrue($pending && isset($pending[3][0][0]) && $pending[3][0][0] == "Jem'Hadar"); // Asssume control of the pending message with a different consumer. $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); $this->assertTrue($res && count($res) == 3 && $res[0] == '0-0' && isset($res[1]['1424-74205']['name']) && $res[1]['1424-74205']['name'] == 'Defiant'); // Now the 'Sisko' consumer should own the message $pending = $this->redis->xPending('ships', 'combatants'); $this->assertTrue(isset($pending[3][0][0]) && $pending[3][0][0] == 'Sisko'); } public function testXInfo() { if (!$this->minVersionCheck("5.0")) $this->markTestSkipped(); /* Create some streams and groups */ $stream = 's'; $groups = ['g1' => 0, 'g2' => 0]; $this->addStreamsAndGroups([$stream], 1, $groups); $info = $this->redis->xInfo('GROUPS', $stream); $this->assertTrue(is_array($info)); $this->assertEquals(count($info), count($groups)); foreach ($info as $group) { $this->assertTrue(array_key_exists('name', $group)); $this->assertTrue(array_key_exists($group['name'], $groups)); } $info = $this->redis->xInfo('STREAM', $stream); $this->assertTrue(is_array($info)); $this->assertTrue(array_key_exists('groups', $info)); $this->assertEquals($info['groups'], count($groups)); foreach (['first-entry', 'last-entry'] as $key) { $this->assertTrue(array_key_exists($key, $info)); $this->assertTrue(is_array($info[$key])); } /* Ensure that default/NULL arguments are ignored */ $info = $this->redis->xInfo('STREAM', $stream, NULL); $this->assertTrue(is_array($info)); $info = $this->redis->xInfo('STREAM', $stream, NULL, -1); $this->assertTrue(is_array($info)); /* XINFO STREAM FULL [COUNT N] Requires >= 6.0.0 */ if (!$this->minVersionCheck("6.0")) return; /* Add some items to the stream so we can test COUNT */ for ($i = 0; $i < 5; $i++) { $this->redis->xAdd($stream, '*', ['foo' => 'bar']); } $info = $this->redis->xInfo('STREAM', $stream, 'full'); $this->assertTrue(isset($info['groups'])); for ($count = 1; $count < 5; $count++) { $info = $this->redis->xInfo('STREAM', $stream, 'full', $count); $n = isset($info['entries']) ? count($info['entries']) : 0; $this->assertEquals($n, $count); } /* Count <= 0 should be ignored */ foreach ([-1, 0] as $count) { $info = $this->redis->xInfo('STREAM', $stream, 'full', 0); $n = isset($info['entries']) ? count($info['entries']) : 0; $this->assertEquals($n, $this->redis->xLen($stream)); } /* Make sure we can't erroneously send non-null args after null ones */ $this->redis->clearLastError(); $this->assertFalse(@$this->redis->xInfo('FOO', NULL, 'fail', 25)); $this->assertEquals(NULL, $this->redis->getLastError()); $this->assertFalse(@$this->redis->xInfo('FOO', NULL, NULL, -2)); $this->assertEquals(NULL, $this->redis->getLastError()); } /* Regression test for issue-1831 (XINFO STREAM on an empty stream) */ public function testXInfoEmptyStream() { /* Configure an empty stream */ $this->redis->del('s'); $this->redis->xAdd('s', '*', ['foo' => 'bar']); $this->redis->xTrim('s', 0); $arr_info = $this->redis->xInfo('STREAM', 's'); $this->assertTrue(is_array($arr_info)); $this->assertEquals(0, $arr_info['length']); $this->assertEquals(NULL, $arr_info['first-entry']); $this->assertEquals(NULL, $arr_info['last-entry']); } public function testInvalidAuthArgs() { $obj_new = $this->newInstance(); $arr_args = [ [], [NULL, NULL], ['foo', 'bar', 'baz'], ['a','b','c','d'], ['a','b','c'], [['a','b'], 'a'], [['a','b','c']], [[NULL, 'pass']], [[NULL, NULL]], ]; foreach ($arr_args as $arr_arg) { try { if (is_array($arr_arg)) { @call_user_func_array([$obj_new, 'auth'], $arr_arg); } } catch (Exception $ex) { unset($ex); /* Suppress intellisense warning */ } catch (ArgumentCountError $ex) { unset($ex); /* Suppress intellisense warning */ } } } public function testAcl() { if ( ! $this->minVersionCheck("6.0")) return $this->markTestSkipped(); /* ACL USERS/SETUSER */ $this->assertTrue(in_array('default', $this->redis->acl('USERS'))); $this->assertTrue($this->redis->acl('SETUSER', 'admin', 'on', '>admin', '+@all')); $this->assertTrue($this->redis->acl('SETUSER', 'noperm', 'on', '>noperm', '-@all')); $this->assertInArray('default', $this->redis->acl('USERS')); /* Verify ACL GETUSER has the correct hash and is in 'nice' format */ $arr_admin = $this->redis->acl('GETUSER', 'admin'); $this->assertInArray(hash('sha256', 'admin'), $arr_admin['passwords']); /* Now nuke our 'admin' user and make sure it went away */ $this->assertTrue($this->redis->acl('DELUSER', 'admin')); $this->assertTrue(!in_array('admin', $this->redis->acl('USERS'))); /* Try to log in with a bad username/password */ $this->assertThrowsMatch($this->redis, function($o) { $o->auth(['1337haxx00r', 'lolwut']); }, '/^WRONGPASS.*$/'); /* We attempted a bad login. We should have an ACL log entry */ $arr_log = $this->redis->acl('log'); if (! $arr_log || !is_array($arr_log)) { $this->assertTrue(false); return; } /* Make sure our ACL LOG entries are nice for the user */ $arr_entry = array_shift($arr_log); $this->assertArrayKey($arr_entry, 'age-seconds', 'is_numeric'); $this->assertArrayKey($arr_entry, 'count', 'is_int'); /* ACL CAT */ $cats = $this->redis->acl('CAT'); foreach (['read', 'write', 'slow'] as $cat) { $this->assertInArray($cat, $cats); } /* ACL CAT */ $cats = $this->redis->acl('CAT', 'string'); foreach (['get', 'set', 'setnx'] as $cat) { $this->assertInArray($cat, $cats); } /* ctype_xdigit even if PHP doesn't have it */ $ctype_xdigit = function($v) { if (function_exists('ctype_xdigit')) { return ctype_xdigit($v); } else { return strspn(strtoupper($v), '0123456789ABCDEF') == strlen($v); } }; /* ACL GENPASS/ACL GENPASS */ $this->assertValidate($this->redis->acl('GENPASS'), $ctype_xdigit); $this->assertValidate($this->redis->acl('GENPASS', 1024), $ctype_xdigit); /* ACL WHOAMI */ $this->assertValidate($this->redis->acl('WHOAMI'), 'strlen'); /* Finally make sure AUTH errors throw an exception */ $r2 = $this->newInstance(true); /* Test NOPERM exception */ $this->assertTrue($r2->auth(['noperm', 'noperm'])); $this->assertThrowsMatch($r2, function($r) { $r->set('foo', 'bar'); }, '/^NOPERM.*$/'); } /* If we detect a unix socket make sure we can connect to it in a variety of ways */ public function testUnixSocket() { if ( ! file_exists("/tmp/redis.sock")) { return $this->markTestSkipped(); } $arr_sock_tests = [ ["/tmp/redis.sock"], ["/tmp/redis.sock", null], ["/tmp/redis.sock", 0], ["/tmp/redis.sock", -1], ]; try { foreach ($arr_sock_tests as $arr_args) { $obj_r = new Redis(); if (count($arr_args) == 2) { @$obj_r->connect($arr_args[0], $arr_args[1]); } else { @$obj_r->connect($arr_args[0]); } if ($this->getAuth()) { $this->assertTrue($obj_r->auth($this->getAuth())); } $this->assertTrue($obj_r->ping()); } } catch (Exception $ex) { $this->assertTrue(false); } } /* Test high ports if we detect Redis running there */ public function testHighPorts() { $arr_ports = [32767, 32768, 32769]; $arr_test_ports = []; foreach ($arr_ports as $port) { if (is_resource(@fsockopen('localhost', $port))) { $arr_test_ports[] = $port; } } if ( ! $arr_test_ports) { return $this->markTestSkipped(); } foreach ($arr_test_ports as $port) { $obj_r = new Redis(); try { @$obj_r->connect('localhost', $port); if ($this->getAuth()) { $this->assertTrue($obj_r->auth($this->getAuth())); } $this->assertTrue($obj_r->ping()); } catch(Exception $ex) { $this->assertTrue(false); } } } public function testSession_savedToRedis() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId)); $this->assertTrue($sessionSuccessful); } public function testSession_lockKeyCorrect() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 5, true); $maxwait = (ini_get('redis.session.lock_wait_time') * ini_get('redis.session.lock_retries') / 1000000.00); $exist = $this->waitForSessionLockKey($sessionId, $maxwait); $this->assertTrue($exist); } public function testSession_lockingDisabledByDefault() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 5, true, 300, false); usleep(100000); $start = microtime(true); $sessionSuccessful = $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, false); $end = microtime(true); $elapsedTime = $end - $start; $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); $this->assertTrue($elapsedTime < 1); $this->assertTrue($sessionSuccessful); } public function testSession_lockReleasedOnClose() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 1, true); $sleep = ini_get('redis.session.lock_wait_time') * ini_get('redis.session.lock_retries'); usleep($sleep + 10000); $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); } public function testSession_lock_ttlMaxExecutionTime() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 10, true, 2); usleep(100000); $start = microtime(true); $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); $end = microtime(true); $elapsedTime = $end - $start; $this->assertTrue($elapsedTime < 3); $this->assertTrue($sessionSuccessful); } public function testSession_lock_ttlLockExpire() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 10, true, 300, true, null, -1, 2); usleep(100000); $start = microtime(true); $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); $end = microtime(true); $elapsedTime = $end - $start; $this->assertTrue($elapsedTime < 3); $this->assertTrue($sessionSuccessful); } public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 2, true, 300, true, null, -1, 1, 'firstProcess'); usleep(1500000); // 1.5 sec $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 10, 'secondProcess'); sleep(1); $this->assertTrue($writeSuccessful); $this->assertEquals('secondProcess', $this->getSessionData($sessionId)); } public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $writeSuccessful = $this->startSessionProcess($sessionId, 2, false, 300, true, null, -1, 1, 'firstProcess'); $this->assertFalse($writeSuccessful); $this->assertTrue('firstProcess' !== $this->getSessionData($sessionId)); } public function testSession_correctLockRetryCount() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); /* Start another process and wait until it has the lock */ $this->startSessionProcess($sessionId, 10, true); if ( ! $this->waitForSessionLockKey($sessionId, 2)) { $this->assertTrue(false); return; } $t1 = microtime(true); $ok = $this->startSessionProcess($sessionId, 0, false, 10, true, 100000, 10); if ( ! $this->assertFalse($ok)) return; $t2 = microtime(true); $this->assertTrue($t2 - $t1 >= 1 && $t2 - $t1 <= 3); } public function testSession_defaultLockRetryCount() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 10, true); $keyname = $this->sessionPrefix . $sessionId . '_LOCK'; $begin = microtime(true); if ( ! $this->waitForSessionLockKey($sessionId, 3)) { $this->assertTrue(false); return; } $start = microtime(true); $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 20000, 0); $end = microtime(true); $elapsedTime = $end - $start; $this->assertTrue($elapsedTime > 2 && $elapsedTime < 3); $this->assertFalse($sessionSuccessful); } public function testSession_noUnlockOfOtherProcess() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $t1 = microtime(true); /* 1. Start a background process, and wait until we are certain * the lock was attained. */ $nsec = 3; $this->startSessionProcess($sessionId, $nsec, true, $nsec); if ( ! $this->waitForSessionLockKey($sessionId, 1)) { $this->assertFalse(true); return; } /* 2. Attempt to lock the same session. This should force us to * wait until the first lock is released. */ $t2 = microtime(true); $ok = $this->startSessionProcess($sessionId, 0, false); $t3 = microtime(true); /* 3. Verify that we did in fact have to wait for this lock */ $this->assertTrue($ok); $this->assertTrue($t3 - $t2 >= $nsec - ($t2 - $t1)); } public function testSession_lockWaitTime() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 1, true, 300); usleep(100000); $start = microtime(true); $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, 3000000); $end = microtime(true); $elapsedTime = $end - $start; $this->assertTrue($elapsedTime > 2.5); $this->assertTrue($elapsedTime < 3.5); $this->assertTrue($sessionSuccessful); } public function testMultipleConnect() { $host = $this->redis->GetHost(); $port = $this->redis->GetPort(); for($i = 0; $i < 5; $i++) { $this->redis->connect($host, $port); if ($this->getAuth()) { $this->assertTrue($this->redis->auth($this->getAuth())); } $this->assertTrue($this->redis->ping()); } } public function testConnectException() { $host = 'github.com'; if (gethostbyname($host) === $host) { return $this->markTestSkipped('online test'); } $redis = new Redis(); try { $redis->connect($host, 6379, 0.01); } catch (Exception $e) { $this->assertTrue(strpos($e, "timed out") !== false); } } public function testTlsConnect() { if (($fp = @fsockopen($this->getHost(), 6378)) == NULL) return $this->markTestSkipped(); fclose($fp); foreach (['localhost' => true, '127.0.0.1' => false] as $host => $verify) { $redis = new Redis(); $this->assertTrue($redis->connect('tls://' . $host, 6378, 0, null, 0, 0, [ 'stream' => ['verify_peer_name' => $verify, 'verify_peer' => false] ])); } } public function testReset() { // Only available since 6.2.0 if (version_compare($this->version, '6.2.0') < 0) { $this->markTestSkipped(); return; } $this->assertTrue($this->redis->multi()->select(2)->set('foo', 'bar')->reset()); $this->assertEquals(Redis::ATOMIC, $this->redis->getMode()); $this->assertEquals(0, $this->redis->getDBNum()); } public function testCopy() { // Only available since 6.2.0 if (version_compare($this->version, '6.2.0') < 0) { $this->markTestSkipped(); return; } $this->redis->del('{key}dst'); $this->redis->set('{key}src', 'foo'); $this->assertTrue($this->redis->copy('{key}src', '{key}dst')); $this->assertEquals('foo', $this->redis->get('{key}dst')); $this->redis->set('{key}src', 'bar'); $this->assertFalse($this->redis->copy('{key}src', '{key}dst')); $this->assertEquals('foo', $this->redis->get('{key}dst')); $this->assertTrue($this->redis->copy('{key}src', '{key}dst', ['replace' => true])); $this->assertEquals('bar', $this->redis->get('{key}dst')); } public function testCommand() { $commands = $this->redis->command(); $this->assertTrue(is_array($commands)); $this->assertEquals(count($commands), $this->redis->command('count')); $infos = $this->redis->command('info'); $this->assertTrue(is_array($infos)); $this->assertEquals(count($infos), count($commands)); if (version_compare($this->version, '7.0') >= 0) { $docs = $this->redis->command('docs'); $this->assertTrue(is_array($docs)); $this->assertEquals(count($docs), 2 * count($commands)); $list = $this->redis->command('list', 'filterby', 'pattern', 'lol*'); $this->assertTrue(is_array($list)); $this->assertEquals($list, ['lolwut']); } } public function testFunction() { if (version_compare($this->version, '7.0') < 0) { $this->markTestSkipped(); return; } $this->assertTrue($this->redis->function('flush', 'sync')); $this->assertEquals('mylib', $this->redis->function('load', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)")); $this->assertEquals('foo', $this->redis->fcall('myfunc', [], ['foo'])); $payload = $this->redis->function('dump'); $this->assertEquals('mylib', $this->redis->function('load', 'replace', "#!lua name=mylib\nredis.register_function{function_name='myfunc', callback=function(keys, args) return args[1] end, flags={'no-writes'}}")); $this->assertEquals('foo', $this->redis->fcall_ro('myfunc', [], ['foo'])); $this->assertEquals($this->redis->function('stats'), ['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]]); $this->assertTrue($this->redis->function('delete', 'mylib')); $this->assertTrue($this->redis->function('restore', $payload)); $this->assertEquals($this->redis->function('list'), [['library_name' => 'mylib', 'engine' => 'LUA', 'functions' => [['name' => 'myfunc', 'description' => false,'flags' => []]]]]); $this->assertTrue($this->redis->function('delete', 'mylib')); } /* Make sure we handle a bad option value gracefully */ public function testBadOptionValue() { $this->assertFalse(@$this->redis->setOption(pow(2, 32), false)); } public function testSession_regenerateSessionId_noLock_noDestroy() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId); $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_noLock_withDestroy() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, false, true); $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_withLock_noDestroy() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, true); $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_withLock_withDestroy() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, true, true); $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_noLock_noDestroy_withProxy() { if (!interface_exists('SessionHandlerInterface')) { $this->markTestSkipped('session handler interface not available in PHP < 5.4'); } $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, false, false, true); $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_noLock_withDestroy_withProxy() { if (!interface_exists('SessionHandlerInterface')) { $this->markTestSkipped('session handler interface not available in PHP < 5.4'); } $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, false, true, true); $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_withLock_noDestroy_withProxy() { if (!interface_exists('SessionHandlerInterface')) { $this->markTestSkipped('session handler interface not available in PHP < 5.4'); } $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, true, false, true); $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_withLock_withDestroy_withProxy() { if (!interface_exists('SessionHandlerInterface')) { $this->markTestSkipped('session handler interface not available in PHP < 5.4'); } $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, true, true, true); $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_ttl_equalsToSessionLifetime() { $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); $this->assertEquals(600, $ttl); } public function testSession_ttl_resetOnWrite() { $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); $this->redis->expire($this->sessionPrefix . $sessionId, 9999); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); $this->assertEquals(600, $ttl); } public function testSession_ttl_resetOnRead() { $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); $this->redis->expire($this->sessionPrefix . $sessionId, 9999); $this->getSessionData($sessionId, 600); $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); $this->assertEquals(600, $ttl); } private function setSessionHandler() { $host = $this->getHost() ?: 'localhost'; @ini_set('session.save_handler', 'redis'); @ini_set('session.save_path', 'tcp://' . $host . ':6379'); } /** * @return string */ private function generateSessionId() { if (function_exists('session_create_id')) { return session_create_id(); } else if (function_exists('random_bytes')) { return bin2hex(random_bytes(8)); } else if (function_exists('openssl_random_pseudo_bytes')) { return bin2hex(openssl_random_pseudo_bytes(8)); } else { return uniqid(); } } /** * @param string $sessionId * @param int $sleepTime * @param bool $background * @param int $maxExecutionTime * @param bool $locking_enabled * @param int $lock_wait_time * @param int $lock_retries * @param int $lock_expires * @param string $sessionData * * @param int $sessionLifetime * * @return bool * @throws Exception */ private function startSessionProcess($sessionId, $sleepTime, $background, $maxExecutionTime = 300, $locking_enabled = true, $lock_wait_time = null, $lock_retries = -1, $lock_expires = 0, $sessionData = '', $sessionLifetime = 1440) { if (substr(php_uname(), 0, 7) == "Windows"){ $this->markTestSkipped(); return true; } else { $commandParameters = [$this->getFullHostPath(), $this->sessionSaveHandler, $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData, $sessionLifetime]; if ($locking_enabled) { $commandParameters[] = '1'; if ($lock_wait_time != null) { $commandParameters[] = $lock_wait_time; } } $commandParameters = array_map('escapeshellarg', $commandParameters); $command = self::getPhpCommand('startSession.php') . implode(' ', $commandParameters); $command .= $background ? ' 2>/dev/null > /dev/null &' : ' 2>&1'; exec($command, $output); return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')) ? true : false; } } /** * @param string $session_id * @param string $max_wait_sec * * Sometimes we want to block until a session lock has been detected * This is better and faster than arbitrarily sleeping. If we don't * detect the session key within the specified maximum number of * seconds, the function returns failure. * * @return bool */ private function waitForSessionLockKey($session_id, $max_wait_sec) { $now = microtime(true); $key = $this->sessionPrefix . $session_id . '_LOCK'; do { usleep(10000); $exists = $this->redis->exists($key); } while (!$exists && microtime(true) <= $now + $max_wait_sec); return $exists || $this->redis->exists($key); } /** * @param string $sessionId * @param int $sessionLifetime * * @return string */ private function getSessionData($sessionId, $sessionLifetime = 1440) { $command = self::getPhpCommand('getSessionData.php') . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . escapeshellarg($sessionId) . ' ' . escapeshellarg($sessionLifetime); exec($command, $output); return $output[0]; } /** * @param string $sessionId * @param bool $locking * @param bool $destroyPrevious * @param bool $sessionProxy * * @return string */ private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false, $sessionProxy = false) { $args = array_map('escapeshellarg', [$sessionId, $locking, $destroyPrevious, $sessionProxy]); $command = self::getPhpCommand('regenerateSessionId.php') . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . implode(' ', $args); exec($command, $output); return $output[0]; } /** * Return command to launch PHP with built extension enabled * taking care of environment (TEST_PHP_EXECUTABLE and TEST_PHP_ARGS) * * @param string $script * * @return string */ private function getPhpCommand($script) { static $cmd = NULL; if (!$cmd) { $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY); if ($test_args = getenv('TEST_PHP_ARGS')) { $cmd .= ' '; $cmd .= $test_args; } else { /* Only append specific extension directives if PHP hasn't been compiled with what we need statically */ $modules = shell_exec("$cmd --no-php-ini -m"); /* Determine if we need to specifically add extensions */ $arr_extensions = array_filter( ['redis', 'igbinary', 'msgpack', 'json'], function ($module) use ($modules) { return strpos($modules, $module) === false; } ); /* If any are needed add them to the command */ if ($arr_extensions) { $cmd .= ' --no-php-ini'; foreach ($arr_extensions as $str_extension) { /* We want to use the locally built redis extension */ if ($str_extension == 'redis') { $str_extension = dirname(__DIR__) . '/modules/redis'; } $cmd .= " --define extension=$str_extension.so"; } } } } return $cmd . ' ' . __DIR__ . '/' . $script . ' '; } } ?> redis-6.0.2/tests/TestRedis.php0000644000175000000120000000646614515245367017215 0ustar pyatsukhnenkowheel redis-6.0.2/tests/TestSuite.php0000644000175000000120000002304114515245367017224 0ustar pyatsukhnenkowheelstr_host = $str_host; $this->i_port = $i_port; $this->auth = $auth; } public function getHost() { return $this->str_host; } public function getPort() { return $this->i_port; } public function getAuth() { return $this->auth; } public static function getAvailableCompression() { $result[] = Redis::COMPRESSION_NONE; if (defined('Redis::COMPRESSION_LZF')) $result[] = Redis::COMPRESSION_LZF; if (defined('Redis::COMPRESSION_LZ4')) $result[] = Redis::COMPRESSION_LZ4; if (defined('Redis::COMPRESSION_ZSTD')) $result[] = Redis::COMPRESSION_ZSTD; return $result; } /** * Returns the fully qualified host path, * which may be used directly for php.ini parameters like session.save_path * * @return null|string */ protected function getFullHostPath() { return $this->str_host ? 'tcp://' . $this->str_host . ':' . $this->i_port : null; } public static function make_bold($str_msg) { return self::$_boo_colorize ? self::$BOLD_ON . $str_msg . self::$BOLD_OFF : $str_msg; } public static function make_success($str_msg) { return self::$_boo_colorize ? self::$GREEN . $str_msg . self::$BOLD_OFF : $str_msg; } public static function make_fail($str_msg) { return self::$_boo_colorize ? self::$RED . $str_msg . self::$BOLD_OFF : $str_msg; } public static function make_warning($str_msg) { return self::$_boo_colorize ? self::$YELLOW . $str_msg . self::$BOLD_OFF : $str_msg; } protected function assertFalse($bool) { if(!$bool) return true; $bt = debug_backtrace(false); self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); return false; } protected function assertTrue($bool, $msg='') { if($bool) return true; $bt = debug_backtrace(false); self::$errors []= sprintf("Assertion failed: %s:%d (%s) %s\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); return false; } protected function assertInArray($ele, $arr, $cb = NULL) { if ($cb && !is_callable($cb)) die("Fatal: assertInArray callback must be callable!\n"); if (($in = in_array($ele, $arr)) && (!$cb || $cb($arr[array_search($ele, $arr)]))) return true; $bt = debug_backtrace(false); $ex = $in ? 'validation' : 'missing'; self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $ele); return false; } protected function assertArrayKey($arr, $key, $cb = NULL) { if ($cb && !is_callable($cb)) die("Fatal: assertArrayKey callback must be callable\n"); if (($exists = isset($arr[$key])) && (!$cb || $cb($arr[$key]))) return true; $bt = debug_backtrace(false); $ex = $exists ? 'validation' : 'missing'; self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $key); return false; } protected function assertValidate($val, $cb) { if ( ! is_callable($cb)) die("Fatal: Callable assertValidate callback required\n"); if ($cb($val)) return true; $bt = debug_backtrace(false); self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); return false; } protected function assertThrowsMatch($arg, $cb, $regex = NULL) { $threw = $match = false; if ( ! is_callable($cb)) die("Fatal: Callable assertThrows callback required\n"); try { $cb($arg); } catch (Exception $ex) { $threw = true; $match = !$regex || preg_match($regex, $ex->getMessage()); } if ($threw && $match) return true; $bt = debug_backtrace(false); $ex = !$threw ? 'no exception' : "no match '$regex'"; self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s]\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex); return false; } protected function assertLess($a, $b) { if($a < $b) return; $bt = debug_backtrace(false); self::$errors[] = sprintf("Assertion failed (%s >= %s): %s: %d (%s\n", print_r($a, true), print_r($b, true), $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); } protected function assertEquals($a, $b) { if($a === $b) return; $bt = debug_backtrace(false); self::$errors []= sprintf("Assertion failed (%s !== %s): %s:%d (%s)\n", print_r($a, true), print_r($b, true), $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); } protected function assertPatternMatch($str_test, $str_regex) { if (preg_match($str_regex, $str_test)) return; $bt = debug_backtrace(false); self::$errors []= sprintf("Assertion failed ('%s' doesnt match '%s'): %s:%d (%s)\n", $str_test, $str_regex, $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); } protected function markTestSkipped($msg='') { $bt = debug_backtrace(false); self::$warnings []= sprintf("Skipped test: %s:%d (%s) %s\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); throw new TestSkippedException($msg); } private static function getMaxTestLen($arr_methods, $str_limit) { $i_result = 0; foreach ($arr_methods as $obj_method) { $str_name = strtolower($obj_method->name); if (substr($str_name, 0, 4) != 'test') continue; if ($str_limit && !strstr($str_name, $str_limit)) continue; if (strlen($str_name) > $i_result) { $i_result = strlen($str_name); } } return $i_result; } /* Flag colorization */ public static function flagColorization($boo_override) { self::$_boo_colorize = $boo_override && function_exists('posix_isatty') && posix_isatty(STDOUT); } public static function run($className, $str_limit = NULL, $str_host = NULL, $i_port = NULL, $auth = NULL) { /* Lowercase our limit arg if we're passed one */ $str_limit = $str_limit ? strtolower($str_limit) : $str_limit; $rc = new ReflectionClass($className); $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC); $i_max_len = self::getMaxTestLen($methods, $str_limit); foreach($methods as $m) { $name = $m->name; if(substr($name, 0, 4) !== 'test') continue; /* If we're trying to limit to a specific test and can't match the * substring, skip */ if ($str_limit && strstr(strtolower($name), $str_limit)===FALSE) { continue; } $str_out_name = str_pad($name, $i_max_len + 1); echo self::make_bold($str_out_name); $count = count($className::$errors); $rt = new $className($str_host, $i_port, $auth); try { $rt->setUp(); $rt->$name(); if ($count === count($className::$errors)) { $str_msg = self::make_success('PASSED'); } else { $str_msg = self::make_fail('FAILED'); } //echo ($count === count($className::$errors)) ? "." : "F"; } catch (Exception $e) { /* We may have simply skipped the test */ if ($e instanceof TestSkippedException) { $str_msg = self::make_warning('SKIPPED'); } else { $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n"; $str_msg = self::make_fail('FAILED'); } } echo "[" . $str_msg . "]\n"; } echo "\n"; echo implode('', $className::$warnings) . "\n"; if(empty($className::$errors)) { echo "All tests passed. \o/\n"; return 0; } echo implode('', $className::$errors); return 1; } } ?> redis-6.0.2/tests/getSessionData.php0000644000175000000120000000106014515245367020205 0ustar pyatsukhnenkowheelhandler = new SessionHandler(); } public function close() { return $this->handler->close(); } public function destroy($session_id) { return $this->handler->destroy($session_id); } public function gc($maxlifetime) { return $this->handler->gc($maxlifetime); } public function open($save_path, $name) { return $this->handler->open($save_path, $name); } public function read($session_id) { return $this->handler->read($session_id); } public function write($session_id, $session_data) { return $this->handler->write($session_id, $session_data); } } } if ($sessionProxy) { $handler = new TestHandler(); session_set_save_handler($handler); } session_id($sessionId); if (!session_start()) { $result = "FAILED: session_start()"; } elseif (!session_regenerate_id($destroyPrevious)) { $result = "FAILED: session_regenerate_id()"; } else { $result = session_id(); } session_write_close(); echo $result; redis-6.0.2/tests/startSession.php0000644000175000000120000000203014515245367017767 0ustar pyatsukhnenkowheel /dev/null 2>&1; then echo "Error: Must have $1 on the path!" exit 1 fi } # Run a command and output what we're running verboseRun() { echo "Running: $@" $@ } # Spawn a specific redis instance, cluster enabled spawnNode() { # ACL file if we have one if [ ! -z "$ACLFILE" ]; then ACLARG="--aclfile $ACLFILE" fi # Attempt to spawn the node verboseRun redis-server --cluster-enabled yes --dir $NODEDIR --port $PORT \ --cluster-config-file node-$PORT.conf --daemonize yes --save \'\' \ --bind $HOST --dbfilename node-$PORT.rdb $ACLARG # Abort if we can't spin this instance if [ $? -ne 0 ]; then echo "Error: Can't spawn node at port $PORT." exit 1 fi } # Spawn nodes from start to end port spawnNodes() { for PORT in `seq $START_PORT $END_PORT`; do # Attempt to spawn the node spawnNode $PORT # Add this host:port to our nodemap so the tests can get seeds echo "$HOST:$PORT" >> $MAPFILE done } # Check to see if any nodes are running checkNodes() { echo -n "Checking port availability " for PORT in `seq $START_PORT $END_PORT`; do redis-cli -p $PORT ping > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "FAIL" echo "Error: There appears to be an instance running at port $PORT" exit 1 fi done echo "OK" } # Create our 'node' directory if it doesn't exist and clean out any previous # configuration files from a previous run. cleanConfigInfo() { verboseRun mkdir -p $NODEDIR verboseRun rm -f $NODEDIR/* if [ -f "$ACLFILE" ]; then cp $ACLFILE $NODEDIR/$ACLFILE fi } # Initialize our cluster with redis-trib.rb initCluster() { TRIBARGS="" for PORT in `seq $START_PORT $END_PORT`; do TRIBARGS="$TRIBARGS $HOST:$PORT" done if [[ ! -z "$USER" ]]; then USERARG="--user $USER" fi if [[ ! -z "$PASS" ]]; then PASSARG="-a $PASS" fi if [[ "$1" -eq "1" ]]; then echo yes | redis-cli $USERARG $PASSARG -p $START_PORT --cluster create $TRIBARGS --cluster-replicas $REPLICAS else verboseRun redis-cli $USERARG $PASSARG -p $START_PORT --cluster create $TRIBARGS --cluster-replicas $REPLICAS fi if [ $? -ne 0 ]; then echo "Error: Couldn't create cluster!" exit 1 fi } # Attempt to spin up our cluster startCluster() { # Make sure none of these nodes are already running checkNodes # Clean out node configuration, etc cleanConfigInfo # Attempt to spawn the nodes spawnNodes # Attempt to initialize the cluster initCluster $1 } # Shut down nodes in our cluster stopCluster() { for PORT in `seq $START_PORT $END_PORT`; do verboseRun redis-cli -p $PORT SHUTDOWN NOSAVE > /dev/null 2>&1 done } # Shut down nodes by killing them killCluster() { for PORT in `seq $START_PORT $END_PORT`; do PID=$(ps aux|grep [r]edis-server|grep $PORT|awk '{print $2}') echo -n "Killing $PID: " if kill $PID; then echo "OK" else echo "ERROR" fi done } printUsage() { echo "Usage: make-cluster [OPTIONS] " echo echo " Options" echo echo " -u Redis username to use when spawning cluster" echo " -p Redis password to use when spawning cluster" echo " -a Redis acl filename to use when spawning cluster" echo " -y Automatically send 'yes' when starting cluster" echo " -h This message" echo exit 0 } # We need redis-server checkExe redis-server while getopts "u:p:a:hy" OPT; do case $OPT in h) printUsage ;; a) if [ ! -f "$OPTARG" ]; then echo "Error: '$OPTARG' is not a filename!" exit -1 fi ACLFILE=$OPTARG ;; u) USER=$OPTARG ;; p) PASS=$OPTARG ;; h) HOST=$OPTARG ;; y) NOASK=1 ;; *) echo "Unknown option: $OPT" exit 1 ;; esac done shift "$((OPTIND - 1))" if [[ $# -lt 1 ]]; then echo "Error: Must pass an operation (start or stop)" exit -1 fi case "$1" in start) startCluster $NOASK ;; stop) stopCluster ;; kill) killCluster ;; *) echo "Usage: make-cluster.sh [options] " exit 1 ;; esac redis-6.0.2/tests/mkring.sh0000755000175000000120000000133314515245367016410 0ustar pyatsukhnenkowheel#!/bin/bash PORTS="6379 6380 6381 6382" REDIS=redis-server function start_node() { P=$1 echo "starting node on port $P"; CONFIG_FILE=`tempfile` cat > $CONFIG_FILE << CONFIG port $P CONFIG $REDIS $CONFIG_FILE > /dev/null 2>/dev/null & sleep 1 rm -f $CONFIG_FILE } function stop_node() { P=$1 PID=$2 redis-cli -h localhost -p $P shutdown kill -9 $PID 2>/dev/null } function stop() { for P in $PORTS; do PID=`lsof -i :$P | tail -1 | cut -f 2 -d " "` if [ "$PID" != "" ]; then stop_node $P $PID fi done } function start() { for P in $PORTS; do start_node $P done } case "$1" in start) start ;; stop) stop ;; restart) stop start ;; *) echo "Usage: $0 [start|stop|restart]" ;; esac redis-6.0.2/LICENSE0000644000175000000120000000622214515245367014427 0ustar pyatsukhnenkowheel-------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from ". THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- This software consists of voluntary contributions made by many individuals on behalf of the PHP Group. The PHP Group can be contacted via Email at group@php.net. For more information on the PHP Group and the PHP project, please see . PHP includes the Zend Engine, freely available at . redis-6.0.2/CREDITS0000644000175000000120000000031414515245367014436 0ustar pyatsukhnenkowheelRedis client extension for PHP Alfonso Jimenez (yo@alfonsojimenez.com) Nasreddine Bouafif (n.bouafif@owlient.eu) Nicolas Favre-Felix (n.favre-felix@owlient.eu) Michael Grunder (michael.grunder@gmail.com) redis-6.0.2/README.md0000644000175000000120000037464614515245367014723 0ustar pyatsukhnenkowheel# PhpRedis [![Build Status](https://github.com/phpredis/phpredis/actions/workflows/ci.yml/badge.svg)](https://github.com/phpredis/phpredis/actions/workflows/ci.yml) [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis) [![PHP version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://github.com/phpredis/phpredis) The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). This code has been developed and maintained by Owlient from November 2009 to March 2011. You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to michael.grunder@gmail.com ([Twitter](https://twitter.com/grumi78), Mastodon), p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)), or n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)). ## [API Documentation](https://phpredis.github.io/phpredis) These are a work in progress, but will eventually replace our **ONE README TO RULE THEM ALL** docs. ## Supporting the project PhpRedis will always be free and open source software, but if you or your company has found it useful please consider supporting the project. Developing a large, complex, and performant library like PhpRedis takes a great deal of time and effort, and support would be appreciated! :heart: The best way to support the project is through [GitHub sponsors](https://github.com/sponsors/michael-grunder). Many of the reward tiers grant access to our [slack channel](https://phpredis.slack.com) where [myself](https://github.com/michael-grunder) and [Pavlo](https://github.com/yatsukhnenko) are regularly available to answer questions. Additionally this will allow you to provide feedback on which fixes and new features to prioritize. You can also make a one-time contribution with [![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5) ## Sponsors Audiomack.com Bluehost.com Object Cache Pro OpenLMS.net # Table of contents ----- 1. [Installing/Configuring](#installingconfiguring) * [Installation](#installation) * [PHP Session handler](#php-session-handler) * [Distributed Redis Array](./array.md#readme) * [Redis Cluster support](./cluster.md#readme) * [Redis Sentinel support](./sentinel.md#readme) * [Running the unit tests](#running-the-unit-tests) 1. [Classes and methods](#classes-and-methods) * [Usage](#usage) * [Connection](#connection) * [Retry and backoff](#retry-and-backoff) * [Server](#server) * [Keys and strings](#keys-and-strings) * [Hashes](#hashes) * [Lists](#lists) * [Sets](#sets) * [Sorted sets](#sorted-sets) * [HyperLogLogs](#hyperloglogs) * [Geocoding](#geocoding) * [Streams](#streams) * [Pub/sub](#pubsub) * [Transactions](#transactions) * [Scripting](#scripting) * [Introspection](#introspection) ----- # Installing/Configuring ----- ## Installation For everything you should need to install PhpRedis on your system, see the [INSTALL.md](./INSTALL.md) page. ## PHP Session handler phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions: ~~~ session.save_handler = redis session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2&read_timeout=2.5" ~~~ `session.save_path` can have a simple `host:port` format too, but you need to provide the `tcp://` scheme if you want to use the parameters. The following parameters are available: * weight (integer): the weight of a host is used in comparison with the others in order to customize the session distribution on several hosts. If host A has twice the weight of host B, it will get twice the amount of sessions. In the example, *host1* stores 20% of all the sessions (1/(1+2+2)) while *host2* and *host3* each store 40% (2/(1+2+2)). The target host is determined once and for all at the start of the session, and doesn't change. The default weight is 1. * timeout (float): the connection timeout to a redis host, expressed in seconds. If the host is unreachable in that amount of time, the session storage will be unavailable for the client. The default timeout is very high (86400 seconds). * persistent (integer, should be 1 or 0): defines if a persistent connection should be used. * prefix (string, defaults to "PHPREDIS_SESSION:"): used as a prefix to the Redis key in which the session is stored. The key is composed of the prefix followed by the session ID. * auth (string, or an array with one or two elements): used to authenticate with the server prior to sending commands. * database (integer): selects a different database. Sessions have a lifetime expressed in seconds and stored in the INI variable "session.gc_maxlifetime". You can change it with [`ini_set()`](http://php.net/ini_set). The session handler requires a version of Redis supporting `EX` and `NX` options of `SET` command (at least 2.6.12). phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0"`. ### Session locking **Support**: Locking feature is currently only supported for Redis setup with single master instance (e.g. classic master/slave Sentinel environment). So locking may not work properly in RedisArray or RedisCluster environments. Following INI variables can be used to configure session locking: ~~~ ; Should the locking be enabled? Defaults to: 0. redis.session.locking_enabled = 1 ; How long should the lock live (in seconds)? Defaults to: value of max_execution_time. redis.session.lock_expire = 60 ; How long to wait between attempts to acquire lock, in microseconds (µs)?. Defaults to: 20000 redis.session.lock_wait_time = 50000 ; Maximum number of times to retry (-1 means infinite). Defaults to: 100 redis.session.lock_retries = 2000 ~~~ ## Running the unit tests phpredis uses a small custom unit test suite for testing functionality of the various classes. To run tests, simply do the following: ~~~ # Run tests for Redis class (note this is the default) php tests/TestRedis.php --class Redis # Run tests for RedisArray class tests/mkring.sh start php tests/TestRedis.php --class RedisArray tests/mkring.sh stop # Run tests for the RedisCluster class tests/make-cluster.sh start php tests/TestRedis.php --class RedisCluster tests/make-cluster.sh stop # Run tests for RedisSentinel class php tests/TestRedis.php --class RedisSentinel ~~~ Note that it is possible to run only tests which match a substring of the test itself by passing the additional argument '--test ' when invoking. ~~~ # Just run the 'echo' test php tests/TestRedis.php --class Redis --test echo ~~~ # Classes and methods ----- ## Usage 1. [Class Redis](#class-redis) 1. [Class RedisException](#class-redisexception) 1. [Predefined constants](#predefined-constants) ### Class Redis ----- _**Description**_: Creates a Redis client ##### *Example* ~~~php $redis = new Redis(); ~~~ Starting from version 6.0.0 it's possible to specify configuration options. This allows to connect lazily to the server without explicitly invoking `connect` command. ##### *Example* ~~~php $redis = new Redis([ 'host' => '127.0.0.1', 'port' => 6379, 'connectTimeout' => 2.5, 'auth' => ['phpredis', 'phpredis'], 'ssl' => ['verify_peer' => false], 'backoff' => [ 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, 'base' => 500, 'cap' => 750, ], ]); ~~~ ##### *Parameters* *host*: string. can be a host, or the path to a unix domain socket. *port*: int (default is 6379, should be -1 for unix domain socket) *connectTimeout*: float, value in seconds (default is 0 meaning unlimited) *retryInterval*: int, value in milliseconds (optional) *readTimeout*: float, value in seconds (default is 0 meaning unlimited) *persistent*: mixed, if value is string then it used as persistend id, else value casts to boolean *auth*: mixed, authentication information *ssl*: array, SSL context options ### Class RedisException ----- phpredis throws a [RedisException](#class-redisexception) object if it can't reach the Redis server. That can happen in case of connectivity issues, if the Redis service is down, or if the redis host is overloaded. In any other problematic case that does not involve an unreachable server (such as a key not existing, an invalid command, etc), phpredis will return `FALSE`. ### Predefined constants ----- _**Description**_: Available Redis Constants Redis data types, as returned by [type](#type) ~~~ Redis::REDIS_STRING - String Redis::REDIS_SET - Set Redis::REDIS_LIST - List Redis::REDIS_ZSET - Sorted set Redis::REDIS_HASH - Hash Redis::REDIS_NOT_FOUND - Not found / other ~~~ @TODO: OPT_SERIALIZER, AFTER, BEFORE,... ## Connection 1. [connect, open](#connect-open) - Connect to a server 1. [pconnect, popen](#pconnect-popen) - Connect to a server (persistent) 1. [auth](#auth) - Authenticate to the server 1. [select](#select) - Change the selected database for the current connection 1. [swapdb](#swapdb) - Swaps two Redis databases 1. [close](#close) - Close the connection 1. [setOption](#setoption) - Set client option 1. [getOption](#getoption) - Get client option 1. [ping](#ping) - Ping the server 1. [echo](#echo) - Echo the given string ### connect, open ----- _**Description**_: Connects to a Redis instance. ##### *Parameters* *host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) *reserved*: should be '' if retry_interval is specified *retry_interval*: int, value in milliseconds (optional) *read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) *others*: array, with PhpRedis >= 5.3.0, it allows setting _auth_ and [_stream_](https://www.php.net/manual/en/context.ssl.php) configuration. ##### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. ##### *Example* ~~~php $redis->connect('127.0.0.1', 6379); $redis->connect('127.0.0.1'); // port 6379 by default $redis->connect('tls://127.0.0.1', 6379); // enable transport level security. $redis->connect('tls://127.0.0.1'); // enable transport level security, port 6379 by default. $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout. $redis->connect('/tmp/redis.sock'); // unix domain socket. $redis->connect('127.0.0.1', 6379, 1, '', 100); // 1 sec timeout, 100ms delay between reconnection attempts. $redis->connect('/tmp/redis.sock', 0, 1.5, NULL, 0, 1.5); // Unix socket with 1.5s timeouts (connect and read) /* With PhpRedis >= 5.3.0 you can specify authentication and stream information on connect */ $redis->connect('127.0.0.1', 6379, 1, '', 0, 0, ['auth' => ['phpredis', 'phpredis']]); ~~~ **Note:** `open` is an alias for `connect` and will be removed in future versions of phpredis. ### pconnect, popen ----- _**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. The connection will not be closed on end of request until the php process ends. So be prepared for too many open FD's errors (specially on redis server side) when using persistent connections on many servers connecting to one redis server. Also more than one persistent connection can be made identified by either host + port + timeout or host + persistent_id or unix socket + timeout. Starting from version 4.2.1, it became possible to use connection pooling by setting INI variable `redis.pconnect.pooling_enabled` to 1. This feature is not available in threaded versions. `pconnect` and `popen` then working like their non persistent equivalents. ##### *Parameters* *host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) *persistent_id*: string. identity for the requested persistent connection *retry_interval*: int, value in milliseconds (optional) *read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) ##### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. ##### *Example* ~~~php $redis->pconnect('127.0.0.1', 6379); $redis->pconnect('127.0.0.1'); // port 6379 by default - same connection like before. $redis->pconnect('tls://127.0.0.1', 6379); // enable transport level security. $redis->pconnect('tls://127.0.0.1'); // enable transport level security, port 6379 by default. $redis->pconnect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout and would be another connection than the two before. $redis->pconnect('127.0.0.1', 6379, 2.5, 'x'); // x is sent as persistent_id and would be another connection than the three before. $redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another connection than the four before. ~~~ **Note:** `popen` is an alias for `pconnect` and will be removed in future versions of phpredis. ### auth ----- _**Description**_: Authenticate the connection using a password or a username and password. *Warning*: The password is sent in plain-text over the network. ##### *Parameters* *MIXED*: password ##### *Return value* *BOOL*: `TRUE` if the connection is authenticated, `FALSE` otherwise. *Note*: In order to authenticate with a username and password you need Redis >= 6.0. ##### *Example* ~~~php /* Authenticate with the password 'foobared' */ $redis->auth('foobared'); /* Authenticate with the username 'phpredis', and password 'haxx00r' */ $redis->auth(['phpredis', 'haxx00r']); /* Authenticate with the password 'foobared' */ $redis->auth(['foobared']); /* You can also use an associative array specifying user and pass */ $redis->auth(['user' => 'phpredis', 'pass' => 'phpredis']); $redis->auth(['pass' => 'phpredis']); ~~~ ### select ----- _**Description**_: Change the selected database for the current connection. ##### *Parameters* *INTEGER*: dbindex, the database number to switch to. ##### *Return value* `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* See method for example: [move](#move) ### swapdb ----- _**Description**_: Swap one Redis database with another atomically ##### *Parameters* *INTEGER*: db1 *INTEGER*: db2 ##### *Return value* `TRUE` on success and `FALSE` on failure. *Note*: Requires Redis >= 4.0.0 ##### *Example* ~~~php $redis->swapdb(0, 1); /* Swaps DB 0 with DB 1 atomically */ ~~~ ### close ----- _**Description**_: Disconnects from the Redis instance. *Note*: Closing a persistent connection requires PhpRedis >= 4.2.0. ##### *Parameters* None. ##### *Return value* *BOOL*: `TRUE` on success, `FALSE` on failure. ### setOption ----- _**Description**_: Set client option. ##### *Parameters* *parameter name* *parameter value* ##### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. ##### *Example* ~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // Don't serialize data $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // Use built-in serialize/unserialize $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // Use igBinary serialize/unserialize $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_MSGPACK); // Use msgpack serialize/unserialize $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON); // Use JSON to serialize/unserialize $redis->setOption(Redis::OPT_PREFIX, 'myAppName:'); // use custom prefix on all keys /* Options for the SCAN family of commands, indicating whether to abstract empty results from the user. If set to SCAN_NORETRY (the default), phpredis will just issue one SCAN command at a time, sometimes returning an empty array of results. If set to SCAN_RETRY, phpredis will retry the scan command until keys come back OR Redis returns an iterator of zero */ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* Scan can also be configured to automatically prepend the currently set PhpRedis prefix to any MATCH pattern. */ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX); $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX); ~~~ ### getOption ----- _**Description**_: Get client option. ##### *Parameters* *parameter name* ##### *Return value* Parameter value. ##### *Example* ~~~php // return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, // Redis::SERIALIZER_IGBINARY, Redis::SERIALIZER_MSGPACK or Redis::SERIALIZER_JSON $redis->getOption(Redis::OPT_SERIALIZER); ~~~ ### ping ----- _**Description**_: Check the current connection status. ##### *Prototype* ~~~php $redis->ping([string $message]); ~~~ ##### *Return value* *Mixed*: This method returns `TRUE` on success, or the passed string if called with an argument. ##### *Example* ~~~php /* When called without an argument, PING returns `TRUE` */ $redis->ping(); /* If passed an argument, that argument is returned. Here 'hello' will be returned */ $redis->ping('hello'); ~~~ *Note*: Prior to PhpRedis 5.0.0 this command simply returned the string `+PONG`. ### echo ----- _**Description**_: Sends a string to Redis, which replies with the same string ##### *Parameters* *STRING*: The message to send. ##### *Return value* *STRING*: the same message. ## Retry and backoff 1. [Maximum retries](#maximum-retries) 1. [Backoff algorithms](#backoff-algorithms) ### Maximum retries You can set and get the maximum retries upon connection issues using the `OPT_MAX_RETRIES` option. Note that this is the number of _retries_, meaning if you set this option to _n_, there will be a maximum _n+1_ attemps overall. Defaults to 10. ##### *Example* ~~~php $redis->setOption(Redis::OPT_MAX_RETRIES, 5); $redis->getOption(Redis::OPT_MAX_RETRIES); ~~~ ### Backoff algorithms You can set the backoff algorithm using the `Redis::OPT_BACKOFF_ALGORITHM` option and choose among the following algorithms described in this blog post by Marc Brooker from AWS: [Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter): * Default: `Redis::BACKOFF_ALGORITHM_DEFAULT` * Decorrelated jitter: `Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER` * Full jitter: `Redis::BACKOFF_ALGORITHM_FULL_JITTER` * Equal jitter: `Redis::BACKOFF_ALGORITHM_EQUAL_JITTER` * Exponential: `Redis::BACKOFF_ALGORITHM_EXPONENTIAL` * Uniform: `Redis::BACKOFF_ALGORITHM_UNIFORM` * Constant: `Redis::BACKOFF_ALGORITHM_CONSTANT` These algorithms depend on the _base_ and _cap_ parameters, both in milliseconds, which you can set using the `Redis::OPT_BACKOFF_BASE` and `Redis::OPT_BACKOFF_CAP` options, respectively. ##### *Example* ~~~php $redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER); $redis->setOption(Redis::OPT_BACKOFF_BASE, 500); // base for backoff computation: 500ms $redis->setOption(Redis::OPT_BACKOFF_CAP, 750); // backoff time capped at 750ms ~~~ ## Server 1. [acl](#acl) - Manage Redis ACLs 1. [bgRewriteAOF](#bgrewriteaof) - Asynchronously rewrite the append-only file 1. [bgSave](#bgsave) - Asynchronously save the dataset to disk (in background) 1. [config](#config) - Get or Set the Redis server configuration parameters 1. [dbSize](#dbsize) - Return the number of keys in selected database 1. [flushAll](#flushall) - Remove all keys from all databases 1. [flushDb](#flushdb) - Remove all keys from the current database 1. [info](#info) - Get information and statistics about the server 1. [lastSave](#lastsave) - Get the timestamp of the last disk save 1. [save](#save) - Synchronously save the dataset to disk (wait to complete) 1. [slaveOf](#slaveof) - Make the server a slave of another instance, or promote it to master 1. [time](#time) - Return the current server time 1. [slowLog](#slowlog) - Access the Redis slowLog entries ### acl ----- _**Description**_: Execute the Redis ACL command. ##### *Parameters* _variable_: Minumum of one argument for `Redis` and two for `RedisCluster`. ##### *Example* ~~~php $redis->acl('USERS'); /* Get a list of users */ $redis->acl('LOG'); /* See log of Redis' ACL subsystem */ ~~~ *Note*: In order to user the `ACL` command you must be communicating with Redis >= 6.0 and be logged into an account that has access to administration commands such as ACL. Please reference [this tutorial](https://redis.io/topics/acl) for an overview of Redis 6 ACLs and [the redis command reference](https://redis.io/commands) for every ACL subcommand. *Note*: If you are connecting to Redis server >= 4.0.0 you can remove a key with the `unlink` method in the exact same way you would use `del`. The Redis [unlink](https://redis.io/commands/unlink) command is non-blocking and will perform the actual deletion asynchronously. ### bgRewriteAOF ----- _**Description**_: Start the background rewrite of AOF (Append-Only File) ##### *Parameters* None. ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $redis->bgRewriteAOF(); ~~~ ### bgSave ----- _**Description**_: Asynchronously save the dataset to disk (in background) ##### *Parameters* None. ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. ##### *Example* ~~~php $redis->bgSave(); ~~~ ### config ----- _**Description**_: Get or Set the Redis server configuration parameters. ##### *Prototype* ~~~php $redis->config(string $operation, string|array|null $key = NULL, ?string $value = NULL): mixed; ~~~ ##### *Return value* *Associative array* for `GET`, key(s) -> value(s) *bool* for `SET`, `RESETSTAT`, and `REWRITE` ##### *Examples* ~~~php $redis->config("GET", "*max-*-entries*"); $redis->config("SET", ['timeout', 'loglevel']); $redis->config("SET", "dir", "/var/run/redis/dumps/"); $redis->config("SET", ['timeout' => 128, 'loglevel' => 'warning']); $redis->config('RESETSTAT'); ~~~ ### dbSize ----- _**Description**_: Return the number of keys in selected database. ##### *Parameters* None. ##### *Return value* *INTEGER*: DB size, in number of keys. ##### *Example* ~~~php $count = $redis->dbSize(); echo "Redis has $count keys\n"; ~~~ ### flushAll ----- _**Description**_: Remove all keys from all databases. ##### *Parameters* *async* (bool) requires server version 4.0.0 or greater ##### *Return value* *BOOL*: Always `TRUE`. ##### *Example* ~~~php $redis->flushAll(); ~~~ ### flushDb ----- _**Description**_: Remove all keys from the current database. ##### *Prototype* ~~~php $redis->flushdb(?bool $sync = NULL): Redis|bool; ~~~ ##### *Return value* *BOOL*: This command returns true on success and false on failure. ##### *Example* ~~~php $redis->flushDb(); ~~~ ### info ----- _**Description**_: Get information and statistics about the server Returns an associative array that provides information about the server. Passing no arguments to INFO will call the standard REDIS INFO command, which returns information such as the following: * redis_version * arch_bits * uptime_in_seconds * uptime_in_days * connected_clients * connected_slaves * used_memory * changes_since_last_save * bgsave_in_progress * last_save_time * total_connections_received * total_commands_processed * role You can pass a variety of options to INFO ([per the Redis documentation](http://redis.io/commands/info)), which will modify what is returned. ##### *Parameters* *option*: The option to provide redis (e.g. "COMMANDSTATS", "CPU") ##### *Example* ~~~php $redis->info(); /* standard redis INFO command */ $redis->info("COMMANDSTATS"); /* Information on the commands that have been run (>=2.6 only) $redis->info("CPU"); /* just CPU information from Redis INFO */ ~~~ ### lastSave ----- _**Description**_: Returns the timestamp of the last disk save. ##### *Parameters* None. ##### *Return value* *INT*: timestamp. ##### *Example* ~~~php $redis->lastSave(); ~~~ ### save ----- _**Description**_: Synchronously save the dataset to disk (wait to complete) ##### *Parameters* None. ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. ##### *Example* ~~~php $redis->save(); ~~~ ### slaveOf ----- _**Description**_: Changes the slave status ##### *Parameters* Either host (string) and port (int), or no parameter to stop being a slave. ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $redis->slaveOf('10.0.1.7', 6379); /* ... */ $redis->slaveOf(); ~~~ ### time ----- _**Description**_: Return the current server time. ##### *Parameters* (none) ##### *Return value* If successful, the time will come back as an associative array with element zero being the unix timestamp, and element one being microseconds. ##### *Examples* ~~~php $redis->time(); ~~~ ### slowLog ----- _**Description**_: Access the Redis slowLog ##### *Parameters* *Operation* (string): This can be either `GET`, `LEN`, or `RESET` *Length* (integer), optional: If executing a `SLOWLOG GET` command, you can pass an optional length. ##### ##### *Return value* The return value of SLOWLOG will depend on which operation was performed. SLOWLOG GET: Array of slowLog entries, as provided by Redis SLOGLOG LEN: Integer, the length of the slowLog SLOWLOG RESET: Boolean, depending on success ##### ##### *Examples* ~~~php // Get ten slowLog entries $redis->slowLog('get', 10); // Get the default number of slowLog entries $redis->slowLog('get'); // Reset our slowLog $redis->slowLog('reset'); // Retrieve slowLog length $redis->slowLog('len'); ~~~ ## Keys and Strings ### Strings ----- * [append](#append) - Append a value to a key * [bitCount](#bitcount) - Count set bits in a string * [bitOp](#bitop) - Perform bitwise operations between strings * [decr, decrBy](#decr-decrby) - Decrement the value of a key * [get](#get) - Get the value of a key * [getBit](#getbit) - Returns the bit value at offset in the string value stored at key * [getRange](#getrange) - Get a substring of the string stored at a key * [getSet](#getset) - Set the string value of a key and return its old value * [incr, incrBy](#incr-incrby) - Increment the value of a key * [incrByFloat](#incrbyfloat) - Increment the float value of a key by the given amount * [mGet, getMultiple](#mget-getmultiple) - Get the values of all the given keys * [mSet, mSetNX](#mset-msetnx) - Set multiple keys to multiple values * [set](#set) - Set the string value of a key * [setBit](#setbit) - Sets or clears the bit at offset in the string value stored at key * [setEx, pSetEx](#setex-psetex) - Set the value and expiration of a key * [setNx](#setnx) - Set the value of a key, only if the key does not exist * [setRange](#setrange) - Overwrite part of a string at key starting at the specified offset * [strLen](#strlen) - Get the length of the value stored in a key ### Keys ----- * [del, delete, unlink](#del-delete-unlink) - Delete a key * [dump](#dump) - Return a serialized version of the value stored at the specified key. * [exists](#exists) - Determine if a key exists * [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds * [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp * [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern * [scan](#scan) - Scan for keys in the keyspace (Redis >= 2.8.0) * [migrate](#migrate) - Atomically transfer a key from a Redis instance to another one * [move](#move) - Move a key to another database * [object](#object) - Inspect the internals of Redis objects * [persist](#persist) - Remove the expiration from a key * [randomKey](#randomkey) - Return a random key from the keyspace * [rename, renameKey](#rename-renamekey) - Rename a key * [renameNx](#renamenx) - Rename a key, only if the new key does not exist * [type](#type) - Determine the type stored at key * [sort](#sort) - Sort the elements in a list, set or sorted set * [ttl, pttl](#ttl-pttl) - Get the time to live for a key * [restore](#restore) - Create a key using the provided serialized value, previously obtained with [dump](#dump). ----- ### get ----- _**Description**_: Get the value related to the specified key ##### *Parameters* *key* ##### *Return value* *String* or *Bool*: If key didn't exist, `FALSE` is returned. Otherwise, the value related to this key is returned. ##### *Examples* ~~~php $redis->get('key'); ~~~ ### set ----- _**Description**_: Set the string value in argument as value of the key. If you're using Redis >= 2.6.12, you can pass extended options as explained below ##### *Parameters* *Key* *Value* *Timeout or Options Array* (optional). If you pass an integer, phpredis will redirect to SETEX, and will try to use Redis >= 2.6.12 extended options if you pass an array with valid values ##### *Return value* *Bool* `TRUE` if the command is successful. ##### *Examples* ~~~php // Simple key -> value set $redis->set('key', 'value'); // Will redirect, and actually make an SETEX call $redis->set('key','value', 10); // Will set the key, if it doesn't exist, with a ttl of 10 seconds $redis->set('key', 'value', ['nx', 'ex'=>10]); // Will set a key, if it does exist, with a ttl of 1000 milliseconds $redis->set('key', 'value', ['xx', 'px'=>1000]); ~~~ ### setEx, pSetEx ----- _**Description**_: Set the string value in argument as value of the key, with a time to live. PSETEX uses a TTL in milliseconds. ##### *Parameters* *Key* *TTL* *Value* ##### *Return value* *Bool* `TRUE` if the command is successful. ##### *Examples* ~~~php $redis->setEx('key', 3600, 'value'); // sets key → value, with 1h TTL. $redis->pSetEx('key', 100, 'value'); // sets key → value, with 0.1 sec TTL. ~~~ ### setNx ----- _**Description**_: Set the string value in argument as value of the key if the key doesn't already exist in the database. ##### *Parameters* *key* *value* ##### *Return value* *Bool* `TRUE` in case of success, `FALSE` in case of failure. ##### *Examples* ~~~php $redis->setNx('key', 'value'); /* return TRUE */ $redis->setNx('key', 'value'); /* return FALSE */ ~~~ ### del, delete, unlink ----- _**Description**_: Remove specified keys. ##### *Parameters* An array of keys, or an undefined number of parameters, each a key: *key1* *key2* *key3* ... *keyN* *Note*: If you are connecting to Redis server >= 4.0.0 you can remove a key with the `unlink` method in the exact same way you would use `del`. The Redis [unlink](https://redis.io/commands/unlink) command is non-blocking and will perform the actual deletion asynchronously. ##### *Return value* *Long* Number of keys deleted. ##### *Examples* ~~~php $redis->set('key1', 'val1'); $redis->set('key2', 'val2'); $redis->set('key3', 'val3'); $redis->set('key4', 'val4'); $redis->del('key1', 'key2'); /* return 2 */ $redis->del(['key3', 'key4']); /* return 2 */ /* If using Redis >= 4.0.0 you can call unlink */ $redis->unlink('key1', 'key2'); $redis->unlink(['key1', 'key2']); ~~~ **Note:** `delete` is an alias for `del` and will be removed in future versions of phpredis. ### exists ----- _**Description**_: Verify if the specified key exists. ##### *Parameters* *key* ##### *Return value* *long*: The number of keys tested that do exist. ##### *Examples* ~~~php $redis->set('key', 'value'); $redis->exists('key'); /* 1 */ $redis->exists('NonExistingKey'); /* 0 */ $redis->mset(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz']); $redis->exists(['foo', 'bar', 'baz']); /* 3 */ $redis->exists('foo', 'bar', 'baz'); /* 3 */ ~~~ **Note**: This function took a single argument and returned TRUE or FALSE in phpredis versions < 4.0.0. ### incr, incrBy ----- _**Description**_: Increment the number stored at key by one. If the second argument is filled, it will be used as the integer value of the increment. ##### *Parameters* *key* *value*: value that will be added to key (only for incrBy) ##### *Return value* *INT* the new value ##### *Examples* ~~~php $redis->incr('key1'); /* key1 didn't exists, set to 0 before the increment */ /* and now has the value 1 */ $redis->incr('key1'); /* 2 */ $redis->incr('key1'); /* 3 */ $redis->incr('key1'); /* 4 */ // Will redirect, and actually make an INCRBY call $redis->incr('key1', 10); /* 14 */ $redis->incrBy('key1', 10); /* 24 */ ~~~ ### incrByFloat ----- _**Description**_: Increment the key with floating point precision. ##### *Parameters* *key* *value*: (float) value that will be added to the key ##### *Return value* *FLOAT* the new value ##### *Examples* ~~~php $redis->incrByFloat('key1', 1.5); /* key1 didn't exist, so it will now be 1.5 */ $redis->incrByFloat('key1', 1.5); /* 3 */ $redis->incrByFloat('key1', -1.5); /* 1.5 */ $redis->incrByFloat('key1', 2.5); /* 4 */ ~~~ ### decr, decrBy ----- _**Description**_: Decrement the number stored at key by one. If the second argument is filled, it will be used as the integer value of the decrement. ##### *Parameters* *key* *value*: value that will be subtracted to key (only for decrBy) ##### *Return value* *INT* the new value ##### *Examples* ~~~php $redis->decr('key1'); /* key1 didn't exists, set to 0 before the increment */ /* and now has the value -1 */ $redis->decr('key1'); /* -2 */ $redis->decr('key1'); /* -3 */ // Will redirect, and actually make an DECRBY call $redis->decr('key1', 10); /* -13 */ $redis->decrBy('key1', 10); /* -23 */ ~~~ ### mGet, getMultiple ----- _**Description**_: Get the values of all the specified keys. If one or more keys don't exist, the array will contain `FALSE` at the position of the key. ##### *Parameters* *Array*: Array containing the list of the keys ##### *Return value* *Array*: Array containing the values related to keys in argument ##### *Examples* ~~~php $redis->set('key1', 'value1'); $redis->set('key2', 'value2'); $redis->set('key3', 'value3'); $redis->mGet(['key1', 'key2', 'key3']); /* ['value1', 'value2', 'value3']; $redis->mGet(['key0', 'key1', 'key5']); /* [`FALSE`, 'value1', `FALSE`]; ~~~ **Note:** `getMultiple` is an alias for `mGet` and will be removed in future versions of phpredis. ### getSet ----- _**Description**_: Sets a value and returns the previous entry at that key. ##### *Parameters* *Key*: key *STRING*: value ##### *Return value* A string, the previous value located at this key. ##### *Example* ~~~php $redis->set('x', '42'); $exValue = $redis->getSet('x', 'lol'); // return '42', replaces x by 'lol' $newValue = $redis->get('x')' // return 'lol' ~~~ ### randomKey ----- _**Description**_: Returns a random key. ##### *Parameters* None. ##### *Return value* *STRING*: an existing key in redis. ##### *Example* ~~~php $key = $redis->randomKey(); $surprise = $redis->get($key); // who knows what's in there. ~~~ ### move ----- _**Description**_: Moves a key to a different database. ##### *Parameters* *Key*: key, the key to move. *INTEGER*: dbindex, the database number to move the key to. ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $redis->select(0); // switch to DB 0 $redis->set('x', '42'); // write 42 to x $redis->move('x', 1); // move to DB 1 $redis->select(1); // switch to DB 1 $redis->get('x'); // will return 42 ~~~ ### rename, renameKey ----- _**Description**_: Renames a key. ##### *Parameters* *STRING*: srckey, the key to rename. *STRING*: dstkey, the new name for the key. ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $redis->set('x', '42'); $redis->rename('x', 'y'); $redis->get('y'); // → 42 $redis->get('x'); // → `FALSE` ~~~ **Note:** `renameKey` is an alias for `rename` and will be removed in future versions of phpredis. ### renameNx ----- _**Description**_: Same as rename, but will not replace a key if the destination already exists. This is the same behaviour as setNx. ### expire, pexpire ----- _**Description**_: Sets an expiration on a key in either seconds or milliseconds. ##### *Prototype* ~~~php public function expire(string $key, int $seconds, ?string $mode = NULL): Redis|bool; public function pexpire(string $key, int $milliseconds, ?string $mode = NULL): Redis|bool; ~~~ ##### *Return value* *BOOL*: `TRUE` if an expiration was set, and `FALSE` on failure or if one was not set. You can distinguish between an error and an expiration not being set by checking `getLastError()`. ##### *Example* ~~~php $redis->set('x', '42'); $redis->expire('x', 3); // x will disappear in 3 seconds. sleep(5); // wait 5 seconds $redis->get('x'); // will return `FALSE`, as 'x' has expired. ~~~ **Note:** `setTimeout` is an alias for `expire` and will be removed in future versions of phpredis. ### expireAt, pexpireAt ----- _**Description**_: Seta specific timestamp for a key to expire in seconds or milliseconds. ##### *Prototype* ~~~php public function expireat(string $key, int $unix_timestamp, ?string $mode = NULL): Redis|bool; public function pexpireat(string $key, int $unix_timestamp_millis, ?string $mode = NULL): Redis|bool; ~~~ ##### *Return value* *BOOL*: `TRUE` if an expiration was set and `FALSE` if one was not set or in the event on an error. You can detect an actual error by checking `getLastError()`. ##### *Example* ~~~php $redis->set('x', '42'); $redis->expireAt('x', time(NULL) + 3); // x will disappear in 3 seconds. sleep(5); // wait 5 seconds $redis->get('x'); // will return `FALSE`, as 'x' has expired. ~~~ ### keys, getKeys ----- _**Description**_: Returns the keys that match a certain pattern. ##### *Parameters* *STRING*: pattern, using '*' as a wildcard. ##### *Return value* *Array of STRING*: The keys that match a certain pattern. ##### *Example* ~~~php $allKeys = $redis->keys('*'); // all keys will match this. $keyWithUserPrefix = $redis->keys('user*'); ~~~ **Note:** `getKeys` is an alias for `keys` and will be removed in future versions of phpredis. ### scan ----- _**Description**_: Scan the keyspace for keys ##### *Parameters* *LONG (reference)*: Iterator, initialized to NULL *STRING, Optional*: Pattern to match *LONG, Optional*: Count of keys per iteration (only a suggestion to Redis) ##### *Return value* *Array, boolean*: This function will return an array of keys or FALSE if Redis returned zero keys *Note*: SCAN is a "directed node" command in [RedisCluster](cluster.md#directed-node-commands) ##### *Example* ~~~php /* Without enabling Redis::SCAN_RETRY (default condition) */ $it = NULL; do { // Scan for some keys $arr_keys = $redis->scan($it); // Redis may return empty results, so protect against that if ($arr_keys !== FALSE) { foreach($arr_keys as $str_key) { echo "Here is a key: $str_key\n"; } } } while ($it > 0); echo "No more keys to scan!\n"; /* With Redis::SCAN_RETRY enabled */ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $it = NULL; /* phpredis will retry the SCAN command if empty results are returned from the server, so no empty results check is required. */ while ($arr_keys = $redis->scan($it)) { foreach ($arr_keys as $str_key) { echo "Here is a key: $str_key\n"; } } echo "No more keys to scan!\n"; ~~~ ### object ----- _**Description**_: Describes the object pointed to by a key. ##### *Parameters* The information to retrieve (string) and the key (string). Info can be one of the following: * "encoding" * "refcount" * "idletime" ##### *Return value* *STRING* for "encoding", *LONG* for "refcount" and "idletime", `FALSE` if the key doesn't exist. ##### *Example* ~~~php $redis->object("encoding", "l"); // → ziplist $redis->object("refcount", "l"); // → 1 $redis->object("idletime", "l"); // → 400 (in seconds, with a precision of 10 seconds). ~~~ ### type ----- _**Description**_: Returns the type of data pointed by a given key. ##### *Parameters* *Key*: key ##### *Return value* Depending on the type of the data pointed by the key, this method will return the following value: string: Redis::REDIS_STRING set: Redis::REDIS_SET list: Redis::REDIS_LIST zset: Redis::REDIS_ZSET hash: Redis::REDIS_HASH other: Redis::REDIS_NOT_FOUND ##### *Example* ~~~php $redis->type('key'); ~~~ ### append ----- _**Description**_: Append specified string to the string stored in specified key. ##### *Parameters* *Key* *Value* ##### *Return value* *INTEGER*: Size of the value after the append ##### *Example* ~~~php $redis->set('key', 'value1'); $redis->append('key', 'value2'); /* 12 */ $redis->get('key'); /* 'value1value2' */ ~~~ ### getRange ----- _**Description**_: Return a substring of a larger string ##### *Parameters* *key* *start* *end* ##### *Return value* *STRING*: the substring ##### *Example* ~~~php $redis->set('key', 'string value'); $redis->getRange('key', 0, 5); /* 'string' */ $redis->getRange('key', -5, -1); /* 'value' */ ~~~ **Note**: `substr` is an alias for `getRange` and will be removed in future versions of phpredis. ### setRange ----- _**Description**_: Changes a substring of a larger string. ##### *Parameters* *key* *offset* *value* ##### *Return value* *STRING*: the length of the string after it was modified. ##### *Example* ~~~php $redis->set('key', 'Hello world'); $redis->setRange('key', 6, "redis"); /* returns 11 */ $redis->get('key'); /* "Hello redis" */ ~~~ ### strLen ----- _**Description**_: Get the length of a string value. ##### *Parameters* *key* ##### *Return value* *INTEGER* ##### *Example* ~~~php $redis->set('key', 'value'); $redis->strlen('key'); /* 5 */ ~~~ ### getBit ----- _**Description**_: Return a single bit out of a larger string ##### *Parameters* *key* *offset* ##### *Return value* *LONG*: the bit value (0 or 1) ##### *Example* ~~~php $redis->set('key', "\x7f"); // this is 0111 1111 $redis->getBit('key', 0); /* 0 */ $redis->getBit('key', 1); /* 1 */ ~~~ ### setBit ----- _**Description**_: Changes a single bit of a string. ##### *Parameters* *key* *offset* *value*: bool or int (1 or 0) ##### *Return value* *LONG*: 0 or 1, the value of the bit before it was set. ##### *Example* ~~~php $redis->set('key', "*"); // ord("*") = 42 = 0x2f = "0010 1010" $redis->setBit('key', 5, 1); /* returns 0 */ $redis->setBit('key', 7, 1); /* returns 0 */ $redis->get('key'); /* chr(0x2f) = "/" = b("0010 1111") */ ~~~ ### bitOp ----- _**Description**_: Bitwise operation on multiple keys. ##### *Parameters* *operation*: either "AND", "OR", "NOT", "XOR" *ret_key*: return key *key1* *key2...* ##### *Return value* *LONG*: The size of the string stored in the destination key. ### bitCount ----- _**Description**_: Count bits in a string. ##### *Parameters* *key* ##### *Return value* *LONG*: The number of bits set to 1 in the value behind the input key. ### sort ----- _**Description**_: Sort the elements in a list, set or sorted set. ##### *Parameters* *Key*: key *Options*: [key => value, ...] - optional, with the following keys and values: ~~~ 'by' => 'some_pattern_*', 'limit' => [0, 1], 'get' => 'some_other_pattern_*' or an array of patterns, 'sort' => 'asc' or 'desc', 'alpha' => TRUE, 'store' => 'external-key' ~~~ ##### *Return value* An array of values, or a number corresponding to the number of elements stored if that was used. ##### *Example* ~~~php $redis->del('s'); $redis->sAdd('s', 5); $redis->sAdd('s', 4); $redis->sAdd('s', 2); $redis->sAdd('s', 1); $redis->sAdd('s', 3); var_dump($redis->sort('s')); // 1,2,3,4,5 var_dump($redis->sort('s', ['sort' => 'desc'])); // 5,4,3,2,1 var_dump($redis->sort('s', ['sort' => 'desc', 'store' => 'out'])); // (int)5 ~~~ ### ttl, pttl ----- _**Description**_: Returns the time to live left for a given key in seconds (ttl), or milliseconds (pttl). ##### *Parameters* *Key*: key ##### *Return value* *LONG*: The time to live in seconds. If the key has no ttl, `-1` will be returned, and `-2` if the key doesn't exist. ##### *Example* ~~~php $redis->ttl('key'); ~~~ ### persist ----- _**Description**_: Remove the expiration timer from a key. ##### *Parameters* *Key*: key ##### *Return value* *BOOL*: `TRUE` if a timeout was removed, `FALSE` if the key didn’t exist or didn’t have an expiration timer. ##### *Example* ~~~php $redis->persist('key'); ~~~ ### mSet, mSetNx ----- _**Description**_: Sets multiple key-value pairs in one atomic command. MSETNX only returns TRUE if all the keys were set (see SETNX). ##### *Parameters* *Pairs*: [key => value, ...] ##### *Return value* *Bool* `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $redis->mSet(['key0' => 'value0', 'key1' => 'value1']); var_dump($redis->get('key0')); var_dump($redis->get('key1')); ~~~ Output: ~~~ string(6) "value0" string(6) "value1" ~~~ ### dump ----- _**Description**_: Dump a key out of a redis database, the value of which can later be passed into redis using the RESTORE command. The data that comes out of DUMP is a binary representation of the key as Redis stores it. ##### *Parameters* *key* string ##### *Return value* The Redis encoded value of the key, or FALSE if the key doesn't exist ##### *Examples* ~~~php $redis->set('foo', 'bar'); $val = $redis->dump('foo'); // $val will be the Redis encoded key value ~~~ ### restore ----- _**Description**_: Restore a key from the result of a DUMP operation. ##### *Parameters* *key* string. The key name *ttl* integer. How long the key should live (if zero, no expire will be set on the key) *value* string (binary). The Redis encoded key value (from DUMP) ##### *Examples* ~~~php $redis->set('foo', 'bar'); $val = $redis->dump('foo'); $redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key 'foo' ~~~ ### migrate ----- _**Description**_: Migrates a key to a different Redis instance. **Note:**: Redis introduced migrating multiple keys in 3.0.6, so you must have at least that version in order to call `migrate` with an array of keys. ##### *Parameters* *host* string. The destination host *port* integer. The TCP port to connect to. *key(s)* string or array. *destination-db* integer. The target DB. *timeout* integer. The maximum amount of time given to this transfer. *copy* boolean, optional. Should we send the COPY flag to redis. *replace* boolean, optional. Should we send the REPLACE flag to redis ##### *Examples* ~~~php $redis->migrate('backup', 6379, 'foo', 0, 3600); $redis->migrate('backup', 6379, 'foo', 0, 3600, true, true); /* copy and replace */ $redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE flag */ /* Migrate multiple keys (requires Redis >= 3.0.6) $redis->migrate('backup', 6379, ['key1', 'key2', 'key3'], 0, 3600); ~~~ ## Hashes * [hDel](#hdel) - Delete one or more hash fields * [hExists](#hexists) - Determine if a hash field exists * [hGet](#hget) - Get the value of a hash field * [hGetAll](#hgetall) - Get all the fields and values in a hash * [hIncrBy](#hincrby) - Increment the integer value of a hash field by the given number * [hIncrByFloat](#hincrbyfloat) - Increment the float value of a hash field by the given amount * [hKeys](#hkeys) - Get all the fields in a hash * [hLen](#hlen) - Get the number of fields in a hash * [hMGet](#hmget) - Get the values of all the given hash fields * [hMSet](#hmset) - Set multiple hash fields to multiple values * [hSet](#hset) - Set the string value of a hash field * [hSetNx](#hsetnx) - Set the value of a hash field, only if the field does not exist * [hVals](#hvals) - Get all the values in a hash * [hScan](#hscan) - Scan a hash key for members * [hStrLen](#hstrlen) - Get the string length of the value associated with field in the hash ### hSet ----- _**Description**_: Adds a value to the hash stored at key. ##### *Parameters* *key* *hashKey* *value* ##### *Return value* *LONG* `1` if value didn't exist and was added successfully, `0` if the value was already present and was replaced, `FALSE` if there was an error. ##### *Example* ~~~php $redis->del('h') $redis->hSet('h', 'key1', 'hello'); /* 1, 'key1' => 'hello' in the hash at "h" */ $redis->hGet('h', 'key1'); /* returns "hello" */ $redis->hSet('h', 'key1', 'plop'); /* 0, value was replaced. */ $redis->hGet('h', 'key1'); /* returns "plop" */ ~~~ ### hSetNx ----- _**Description**_: Adds a value to the hash stored at key only if this field isn't already in the hash. ##### *Return value* *BOOL* `TRUE` if the field was set, `FALSE` if it was already present. ##### *Example* ~~~php $redis->del('h') $redis->hSetNx('h', 'key1', 'hello'); /* TRUE, 'key1' => 'hello' in the hash at "h" */ $redis->hSetNx('h', 'key1', 'world'); /* FALSE, 'key1' => 'hello' in the hash at "h". No change since the field wasn't replaced. */ ~~~ ### hGet ----- _**Description**_: Gets a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* *key* *hashKey* ##### *Return value* *STRING* The value, if the command executed successfully *BOOL* `FALSE` in case of failure ### hLen ----- _**Description**_: Returns the length of a hash, in number of items ##### *Parameters* *key* ##### *Return value* *LONG* the number of items in a hash, `FALSE` if the key doesn't exist or isn't a hash. ##### *Example* ~~~php $redis->del('h') $redis->hSet('h', 'key1', 'hello'); $redis->hSet('h', 'key2', 'plop'); $redis->hLen('h'); /* returns 2 */ ~~~ ### hDel ----- _**Description**_: Removes a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* *key* *hashKey1* *hashKey2* ... ##### *Return value* *LONG* the number of deleted keys, 0 if the key doesn't exist, `FALSE` if the key isn't a hash. ### hKeys ----- _**Description**_: Returns the keys in a hash, as an array of strings. ##### *Parameters* *Key*: key ##### *Return value* An array of elements, the keys of the hash. This works like PHP's array_keys(). ##### *Example* ~~~php $redis->del('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); $redis->hSet('h', 'c', 'z'); $redis->hSet('h', 'd', 't'); var_dump($redis->hKeys('h')); ~~~ Output: ~~~ array(4) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> string(1) "c" [3]=> string(1) "d" } ~~~ The order is random and corresponds to redis' own internal representation of the set structure. ### hVals ----- _**Description**_: Returns the values in a hash, as an array of strings. ##### *Parameters* *Key*: key ##### *Return value* An array of elements, the values of the hash. This works like PHP's array_values(). ##### *Example* ~~~php $redis->del('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); $redis->hSet('h', 'c', 'z'); $redis->hSet('h', 'd', 't'); var_dump($redis->hVals('h')); ~~~ Output: ~~~ array(4) { [0]=> string(1) "x" [1]=> string(1) "y" [2]=> string(1) "z" [3]=> string(1) "t" } ~~~ The order is random and corresponds to redis' own internal representation of the set structure. ### hGetAll ----- _**Description**_: Returns the whole hash, as an array of strings indexed by strings. ##### *Parameters* *Key*: key ##### *Return value* An array of elements, the contents of the hash. ##### *Example* ~~~php $redis->del('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); $redis->hSet('h', 'c', 'z'); $redis->hSet('h', 'd', 't'); var_dump($redis->hGetAll('h')); ~~~ Output: ~~~ array(4) { ["a"]=> string(1) "x" ["b"]=> string(1) "y" ["c"]=> string(1) "z" ["d"]=> string(1) "t" } ~~~ The order is random and corresponds to redis' own internal representation of the set structure. ### hExists ----- _**Description**_: Verify if the specified member exists in a key. ##### *Parameters* *key* *memberKey* ##### *Return value* *BOOL*: If the member exists in the hash table, return `TRUE`, otherwise return `FALSE`. ##### *Examples* ~~~php $redis->hSet('h', 'a', 'x'); $redis->hExists('h', 'a'); /* TRUE */ $redis->hExists('h', 'NonExistingKey'); /* FALSE */ ~~~ ### hIncrBy ----- _**Description**_: Increments the value of a member from a hash by a given amount. ##### *Parameters* *key* *member* *value*: (integer) value that will be added to the member's value ##### *Return value* *LONG* the new value ##### *Examples* ~~~php $redis->del('h'); $redis->hIncrBy('h', 'x', 2); /* returns 2: h[x] = 2 now. */ $redis->hIncrBy('h', 'x', 1); /* h[x] ← 2 + 1. Returns 3 */ ~~~ ### hIncrByFloat ----- _**Description**_: Increments the value of a hash member by the provided float value ##### *Parameters* *key* *member* *value*: (float) value that will be added to the member's value ##### *Return value* *FLOAT* the new value ##### *Examples* ~~~php $redis->del('h'); $redis->hIncrByFloat('h','x', 1.5); /* returns 1.5: h[x] = 1.5 now */ $redis->hIncrByFloat('h', 'x', 1.5); /* returns 3.0: h[x] = 3.0 now */ $redis->hIncrByFloat('h', 'x', -3.0); /* returns 0.0: h[x] = 0.0 now */ ~~~ ### hMSet ----- _**Description**_: Fills in a whole hash. Non-string values are converted to string, using the standard `(string)` cast. NULL values are stored as empty strings. ##### *Parameters* *key* *members*: key → value array ##### *Return value* *BOOL* ##### *Examples* ~~~php $redis->del('user:1'); $redis->hMSet('user:1', ['name' => 'Joe', 'salary' => 2000]); $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. ~~~ ### hMGet ----- _**Description**_: Retrieve the values associated to the specified fields in the hash. ##### *Parameters* *key* *memberKeys* Array ##### *Return value* *Array* An array of elements, the values of the specified fields in the hash, with the hash keys as array keys. ##### *Examples* ~~~php $redis->del('h'); $redis->hSet('h', 'field1', 'value1'); $redis->hSet('h', 'field2', 'value2'); $redis->hMGet('h', ['field1', 'field2']); /* returns ['field1' => 'value1', 'field2' => 'value2'] */ ~~~ ### hScan ----- _**Description**_: Scan a HASH value for members, with an optional pattern and count ##### *Parameters* *key*: String *iterator*: Long (reference) *pattern*: Optional pattern to match against *count*: How many keys to return in a go (only a suggestion to Redis) ##### *Return value* *Array* An array of members that match our pattern ##### *Examples* ~~~php $it = NULL; /* Don't ever return an empty array until we're done iterating */ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); while($arr_keys = $redis->hScan('hash', $it)) { foreach($arr_keys as $str_field => $str_value) { echo "$str_field => $str_value\n"; /* Print the hash member and value */ } } ~~~ ### hStrLen ----- _**Description**_: Get the string length of the value associated with field in the hash stored at key. ##### *Parameters* *key*: String *field*: String ##### *Return value* *LONG* the string length of the value associated with field, or zero when field is not present in the hash or key does not exist at all. ## Lists * [blPop, brPop](#blpop-brpop) - Remove and get the first/last element in a list * [bRPopLPush](#brpoplpush) - Pop a value from a list, push it to another list and return it * [lIndex, lGet](#lindex-lget) - Get an element from a list by its index * [lInsert](#linsert) - Insert an element before or after another element in a list * [lLen, lSize](#llen-lsize) - Get the length/size of a list * [lPop](#lpop) - Remove and get the first element in a list * [lPush](#lpush) - Prepend one or multiple values to a list * [lPushx](#lpushx) - Prepend a value to a list, only if the list exists * [lRange, lGetRange](#lrange-lgetrange) - Get a range of elements from a list * [lRem, lRemove](#lrem-lremove) - Remove elements from a list * [lSet](#lset) - Set the value of an element in a list by its index * [lTrim, listTrim](#ltrim-listtrim) - Trim a list to the specified range * [rPop](#rpop) - Remove and get the last element in a list * [rPopLPush](#rpoplpush) - Remove the last element in a list, append it to another list and return it (redis >= 1.1) * [rPush](#rpush) - Append one or multiple values to a list * [rPushX](#rpushx) - Append a value to a list, only if the list exists ### blPop, brPop ----- _**Description**_: Is a blocking lPop(rPop) primitive. If at least one of the lists contains at least one element, the element will be popped from the head of the list and returned to the caller. If all the list identified by the keys passed in arguments are empty, blPop will block during the specified timeout until an element is pushed to one of those lists. This element will be popped. ##### *Parameters* *ARRAY* Array containing the keys of the lists *INTEGER* Timeout Or *STRING* Key1 *STRING* Key2 *STRING* Key3 ... *STRING* Keyn *INTEGER* Timeout ##### *Return value* *ARRAY* ['listName', 'element'] ##### *Example* ~~~php /* Non blocking feature */ $redis->lPush('key1', 'A'); $redis->del('key2'); $redis->blPop('key1', 'key2', 10); /* ['key1', 'A'] */ /* OR */ $redis->blPop(['key1', 'key2'], 10); /* ['key1', 'A'] */ $redis->brPop('key1', 'key2', 10); /* ['key1', 'A'] */ /* OR */ $redis->brPop(['key1', 'key2'], 10); /* ['key1', 'A'] */ /* Blocking feature */ /* process 1 */ $redis->del('key1'); $redis->blPop('key1', 10); /* blocking for 10 seconds */ /* process 2 */ $redis->lPush('key1', 'A'); /* process 1 */ /* ['key1', 'A'] is returned*/ ~~~ ### bRPopLPush ----- _**Description**_: A blocking version of `rPopLPush`, with an integral timeout in the third parameter. ##### *Parameters* *Key*: srckey *Key*: dstkey *Long*: timeout ##### *Return value* *STRING* The element that was moved in case of success, `FALSE` in case of timeout. ### lIndex, lGet ----- _**Description**_: Return the specified element of the list stored at the specified key. 0 the first element, 1 the second ... -1 the last element, -2 the penultimate ... Return `FALSE` in case of a bad index or a key that doesn't point to a list. ##### *Parameters* *key* *index* ##### *Return value* *String* the element at this index *Bool* `FALSE` if the key identifies a non-string data type, or no value corresponds to this index in the list `Key`. ##### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->lindex('key1', 0); /* 'A' */ $redis->lindex('key1', -1); /* 'C' */ $redis->lindex('key1', 10); /* `FALSE` */ ~~~ **Note:** `lGet` is an alias for `lIndex` and will be removed in future versions of phpredis. ### lInsert ----- _**Description**_: Insert value in the list before or after the pivot value. The parameter options specify the position of the insert (before or after). If the list didn't exists, or the pivot didn't exists, the value is not inserted. ##### *Parameters* *key* *position* Redis::BEFORE | Redis::AFTER *pivot* *value* ##### *Return value* The number of the elements in the list, -1 if the pivot didn't exists. ##### *Example* ~~~php $redis->del('key1'); $redis->lInsert('key1', Redis::AFTER, 'A', 'X'); /* 0 */ $redis->lPush('key1', 'A'); $redis->lPush('key1', 'B'); $redis->lPush('key1', 'C'); $redis->lInsert('key1', Redis::BEFORE, 'C', 'X'); /* 4 */ $redis->lRange('key1', 0, -1); /* ['A', 'B', 'X', 'C'] */ $redis->lInsert('key1', Redis::AFTER, 'C', 'Y'); /* 5 */ $redis->lRange('key1', 0, -1); /* ['A', 'B', 'X', 'C', 'Y'] */ $redis->lInsert('key1', Redis::AFTER, 'W', 'value'); /* -1 */ ~~~ ### lPop ----- _**Description**_: Return and remove the first element of the list. ##### *Parameters* *key* ##### *Return value* *STRING* if command executed successfully *BOOL* `FALSE` in case of failure (empty list) ##### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->lPop('key1'); /* key1 => [ 'B', 'C' ] */ ~~~ ### lPush ----- _**Description**_: Adds one or more values to the head of a LIST. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. ##### *Prototype* ~~~php $redis->lPush($key, $entry [, $entry, $entry]); ~~~ ##### *Return value* *LONG* The new length of the list in case of success, `FALSE` in case of Failure. ##### *Examples* ~~~php $redis->del('key1'); $redis->lPush('key1', 'F'); // returns 1 $redis->lPush('key1', 'E'); // returns 2 $redis->lPush('key1', 'D'); // returns 3 /* key1 now contains: [ 'D', 'E', 'F' ] */ $redis->lPush('key1', 'C', 'B', 'A'); // Returns 6 /* key1 now contains: [ 'A', 'B', 'C', 'D', 'E', 'F' ] ~~~ ### lPushx ----- _**Description**_: Adds the string value to the head (left) of the list if the list exists. ##### *Parameters* *key* *value* String, value to push in key ##### *Return value* *LONG* The new length of the list in case of success, `FALSE` in case of Failure. ##### *Examples* ~~~php $redis->del('key1'); $redis->lPushx('key1', 'A'); // returns 0 $redis->lPush('key1', 'A'); // returns 1 $redis->lPushx('key1', 'B'); // returns 2 $redis->lPushx('key1', 'C'); // returns 3 /* key1 now points to the following list: [ 'A', 'B', 'C' ] */ ~~~ ### lRange, lGetRange ----- _**Description**_: Returns the specified elements of the list stored at the specified key in the range [start, end]. start and stop are interpreted as indices: 0 the first element, 1 the second ... -1 the last element, -2 the penultimate ... ##### *Parameters* *key* *start* *end* ##### *Return value* *Array* containing the values in specified range. ##### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); $redis->lRange('key1', 0, -1); /* ['A', 'B', 'C'] */ ~~~ **Note:** `lGetRange` is an alias for `lRange` and will be removed in future versions of phpredis. ### lRem, lRemove ----- _**Description**_: Removes the first `count` occurrences of the value element from the list. If count is zero, all the matching elements are removed. If count is negative, elements are removed from tail to head. **Note**: The argument order is not the same as in the Redis documentation. This difference is kept for compatibility reasons. ##### *Parameters* *key* *value* *count* ##### *Return value* *LONG* the number of elements to remove *BOOL* `FALSE` if the value identified by key is not a list. ##### *Example* ~~~php $redis->lPush('key1', 'A'); $redis->lPush('key1', 'B'); $redis->lPush('key1', 'C'); $redis->lPush('key1', 'A'); $redis->lPush('key1', 'A'); $redis->lRange('key1', 0, -1); /* ['A', 'A', 'C', 'B', 'A'] */ $redis->lRem('key1', 'A', 2); /* 2 */ $redis->lRange('key1', 0, -1); /* ['C', 'B', 'A'] */ ~~~ **Note:** `lRemove` is an alias for `lRem` and will be removed in future versions of phpredis. ### lSet ----- _**Description**_: Set the list at index with the new value. ##### *Parameters* *key* *index* *value* ##### *Return value* *BOOL* `TRUE` if the new value was set. `FALSE` if the index is out of range, or data type identified by key is not a list. ##### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->lindex('key1', 0); /* 'A' */ $redis->lSet('key1', 0, 'X'); $redis->lindex('key1', 0); /* 'X' */ ~~~ ### lTrim, listTrim ----- _**Description**_: Trims an existing list so that it will contain only a specified range of elements. ##### *Parameters* *key* *start* *stop* ##### *Return value* *Array* *Bool* return `FALSE` if the key identify a non-list value. ##### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); $redis->lRange('key1', 0, -1); /* ['A', 'B', 'C'] */ $redis->lTrim('key1', 0, 1); $redis->lRange('key1', 0, -1); /* ['A', 'B'] */ ~~~ **Note:** `listTrim` is an alias for `lTrim` and will be removed in future versions of phpredis. ### rPop ----- _**Description**_: Returns and removes the last element of the list. ##### *Parameters* *key* ##### *Return value* *STRING* if command executed successfully *BOOL* `FALSE` in case of failure (empty list) ##### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->rPop('key1'); /* key1 => [ 'A', 'B' ] */ ~~~ ### rPopLPush ----- _**Description**_: Pops a value from the tail of a list, and pushes it to the front of another list. Also return this value. (redis >= 1.1) ##### *Parameters* *Key*: srckey *Key*: dstkey ##### *Return value* *STRING* The element that was moved in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $redis->del('x', 'y'); $redis->lPush('x', 'abc'); $redis->lPush('x', 'def'); $redis->lPush('y', '123'); $redis->lPush('y', '456'); // move the last of x to the front of y. var_dump($redis->rPopLPush('x', 'y')); var_dump($redis->lRange('x', 0, -1)); var_dump($redis->lRange('y', 0, -1)); ~~~ Output: ~~~ string(3) "abc" array(1) { [0]=> string(3) "def" } array(3) { [0]=> string(3) "abc" [1]=> string(3) "456" [2]=> string(3) "123" } ~~~ ### rPush ----- _**Description**_: Adds one or more entries to the tail of a LIST. Redis will create the list if it doesn't exist. ##### *Prototype* ~~~php $redis->rPush($key, $entry [, $entry, $entry]); ~~~ ##### *Return value* *LONG* The new length of the list in case of success, `FALSE` in case of Failure. ##### *Examples* ~~~php $redis->del('key1'); $redis->rPush('key1', 'A'); // returns 1 $redis->rPush('key1', 'B'); // returns 2 $redis->rPush('key1', 'C'); // returns 3 $redis->rPush('key1', 'D', 'E', 'F'); // returns 6 /* key1 now contains: [ 'A', 'B', 'C', 'D', 'E', 'F' ] */ ~~~ ### rPushX ----- _**Description**_: Adds the string value to the tail (right) of the list if the list exists. `FALSE` in case of Failure. ##### *Parameters* *key* *value* String, value to push in key ##### *Return value* *LONG* The new length of the list in case of success, `FALSE` in case of Failure. ##### *Examples* ~~~php $redis->del('key1'); $redis->rPushX('key1', 'A'); // returns 0 $redis->rPush('key1', 'A'); // returns 1 $redis->rPushX('key1', 'B'); // returns 2 $redis->rPushX('key1', 'C'); // returns 3 /* key1 now points to the following list: [ 'A', 'B', 'C' ] */ ~~~ ### lLen, lSize ----- _**Description**_: Returns the size of a list identified by Key. If the list didn't exist or is empty, the command returns 0. If the data type identified by Key is not a list, the command return `FALSE`. ##### *Parameters* *Key* ##### *Return value* *LONG* The size of the list identified by Key exists. *BOOL* `FALSE` if the data type identified by Key is not list ##### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->lLen('key1');/* 3 */ $redis->rPop('key1'); $redis->lLen('key1');/* 2 */ ~~~ **Note:** `lSize` is an alias for `lLen` and will be removed in future versions of phpredis. ## Sets * [sAdd](#sadd) - Add one or more members to a set * [sCard, sSize](#scard-ssize) - Get the number of members in a set * [sDiff](#sdiff) - Subtract multiple sets * [sDiffStore](#sdiffstore) - Subtract multiple sets and store the resulting set in a key * [sInter](#sinter) - Intersect multiple sets * [sInterStore](#sinterstore) - Intersect multiple sets and store the resulting set in a key * [sIsMember, sContains](#sismember-scontains) - Determine if a given value is a member of a set * [sMembers, sGetMembers](#smembers-sgetmembers) - Get all the members in a set * [sMove](#smove) - Move a member from one set to another * [sPop](#spop) - Remove and return one or more members of a set at random * [sRandMember](#srandmember) - Get one or multiple random members from a set * [sRem, sRemove](#srem-sremove) - Remove one or more members from a set * [sUnion](#sunion) - Add multiple sets * [sUnionStore](#sunionstore) - Add multiple sets and store the resulting set in a key * [sScan](#sscan) - Scan a set for members ### sAdd ----- _**Description**_: Adds a value to the set value stored at key. ##### *Parameters* *key* *value* ##### *Return value* *LONG* the number of elements added to the set. ##### *Example* ~~~php $redis->sAdd('key1' , 'member1'); /* 1, 'key1' => {'member1'} */ $redis->sAdd('key1' , 'member2', 'member3'); /* 2, 'key1' => {'member1', 'member2', 'member3'}*/ $redis->sAdd('key1' , 'member2'); /* 0, 'key1' => {'member1', 'member2', 'member3'}*/ ~~~ ### sCard, sSize ----- _**Description**_: Returns the cardinality of the set identified by key. ##### *Parameters* *key* ##### *Return value* *LONG* the cardinality of the set identified by key, 0 if the set doesn't exist. ##### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ $redis->sCard('key1'); /* 3 */ $redis->sCard('keyX'); /* 0 */ ~~~ **Note:** `sSize` is an alias for `sCard` and will be removed in future versions of phpredis. ### sDiff ----- _**Description**_: Performs the difference between N sets and returns it. ##### *Parameters* *Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. ##### *Return value* *Array of strings*: The difference of the first set will all the others. ##### *Example* ~~~php $redis->del('s0', 's1', 's2'); $redis->sAdd('s0', '1'); $redis->sAdd('s0', '2'); $redis->sAdd('s0', '3'); $redis->sAdd('s0', '4'); $redis->sAdd('s1', '1'); $redis->sAdd('s2', '3'); var_dump($redis->sDiff('s0', 's1', 's2')); ~~~ Return value: all elements of s0 that are neither in s1 nor in s2. ~~~ array(2) { [0]=> string(1) "4" [1]=> string(1) "2" } ~~~ ### sDiffStore ----- _**Description**_: Performs the same action as sDiff, but stores the result in the first key ##### *Parameters* *Key*: dstkey, the key to store the diff into. *Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis ##### *Return value* *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* ~~~php $redis->del('s0', 's1', 's2'); $redis->sAdd('s0', '1'); $redis->sAdd('s0', '2'); $redis->sAdd('s0', '3'); $redis->sAdd('s0', '4'); $redis->sAdd('s1', '1'); $redis->sAdd('s2', '3'); var_dump($redis->sDiffStore('dst', 's0', 's1', 's2')); var_dump($redis->sMembers('dst')); ~~~ Return value: the number of elements of s0 that are neither in s1 nor in s2. ~~~ int(2) array(2) { [0]=> string(1) "4" [1]=> string(1) "2" } ~~~ ### sInter ----- _**Description**_: Returns the members of a set resulting from the intersection of all the sets held at the specified keys. If just a single key is specified, then this command produces the members of this set. If one of the keys is missing, `FALSE` is returned. ##### *Parameters* key1, key2, keyN: keys identifying the different sets on which we will apply the intersection. ##### *Return value* Array, contain the result of the intersection between those keys. If the intersection between the different sets is empty, the return value will be empty array. ##### *Examples* ~~~php $redis->sAdd('key1', 'val1'); $redis->sAdd('key1', 'val2'); $redis->sAdd('key1', 'val3'); $redis->sAdd('key1', 'val4'); $redis->sAdd('key2', 'val3'); $redis->sAdd('key2', 'val4'); $redis->sAdd('key3', 'val3'); $redis->sAdd('key3', 'val4'); var_dump($redis->sInter('key1', 'key2', 'key3')); ~~~ Output: ~~~ array(2) { [0]=> string(4) "val4" [1]=> string(4) "val3" } ~~~ ### sInterStore ----- _**Description**_: Performs a sInter command and stores the result in a new set. ##### *Parameters* *Key*: dstkey, the key to store the diff into. *Keys*: key1, key2... keyN. key1..keyN are intersected as in sInter. ##### *Return value* *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* ~~~php $redis->sAdd('key1', 'val1'); $redis->sAdd('key1', 'val2'); $redis->sAdd('key1', 'val3'); $redis->sAdd('key1', 'val4'); $redis->sAdd('key2', 'val3'); $redis->sAdd('key2', 'val4'); $redis->sAdd('key3', 'val3'); $redis->sAdd('key3', 'val4'); var_dump($redis->sInterStore('output', 'key1', 'key2', 'key3')); var_dump($redis->sMembers('output')); ~~~ Output: ~~~ int(2) array(2) { [0]=> string(4) "val4" [1]=> string(4) "val3" } ~~~ ### sIsMember, sContains ----- _**Description**_: Checks if `value` is a member of the set stored at the key `key`. ##### *Parameters* *key* *value* ##### *Return value* *BOOL* `TRUE` if `value` is a member of the set at key `key`, `FALSE` otherwise. ##### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ $redis->sIsMember('key1', 'member1'); /* TRUE */ $redis->sIsMember('key1', 'memberX'); /* FALSE */ ~~~ **Note:** `sContains` is an alias for `sIsMember` and will be removed in future versions of phpredis. ### sMembers, sGetMembers ----- _**Description**_: Returns the contents of a set. ##### *Parameters* *Key*: key ##### *Return value* An array of elements, the contents of the set. ##### *Example* ~~~php $redis->del('s'); $redis->sAdd('s', 'a'); $redis->sAdd('s', 'b'); $redis->sAdd('s', 'a'); $redis->sAdd('s', 'c'); var_dump($redis->sMembers('s')); ~~~ Output: ~~~ array(3) { [0]=> string(1) "c" [1]=> string(1) "a" [2]=> string(1) "b" } ~~~ The order is random and corresponds to redis' own internal representation of the set structure. **Note:** `sGetMembers` is an alias for `sMembers` and will be removed in future versions of phpredis. ### sMove ----- _**Description**_: Moves the specified member from the set at srcKey to the set at dstKey. ##### *Parameters* *srcKey* *dstKey* *member* ##### *Return value* *BOOL* If the operation is successful, return `TRUE`. If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, `FALSE` is returned. ##### *Example* ~~~php $redis->sAdd('key1' , 'member11'); $redis->sAdd('key1' , 'member12'); $redis->sAdd('key1' , 'member13'); /* 'key1' => {'member11', 'member12', 'member13'}*/ $redis->sAdd('key2' , 'member21'); $redis->sAdd('key2' , 'member22'); /* 'key2' => {'member21', 'member22'}*/ $redis->sMove('key1', 'key2', 'member13'); /* 'key1' => {'member11', 'member12'} */ /* 'key2' => {'member21', 'member22', 'member13'} */ ~~~ ### sPop ----- _**Description**_: Removes and returns a random element from the set value at Key. ##### *Parameters* *key* *count*: Integer, optional ##### *Return value (without count argument)* *String* "popped" value *Bool* `FALSE` if set identified by key is empty or doesn't exist. ##### *Return value (with count argument)* *Array*: Member(s) returned or an empty array if the set doesn't exist *Bool*: `FALSE` on error if the key is not a set ##### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/ $redis->sPop('key1'); /* 'member1', 'key1' => {'member3', 'member2'} */ $redis->sPop('key1'); /* 'member3', 'key1' => {'member2'} */ /* With count */ $redis->sAdd('key2', 'member1', 'member2', 'member3'); $redis->sPop('key2', 3); /* Will return all members but in no particular order */ ~~~ ### sRandMember ----- _**Description**_: Returns a random element from the set value at Key, without removing it. ##### *Parameters* *key* *count* (Integer, optional) ##### *Return value* If no count is provided, a random *String* value from the set will be returned. If a count is provided, an array of values from the set will be returned. Read about the different ways to use the count here: [SRANDMEMBER](http://redis.io/commands/srandmember) *Bool* `FALSE` if set identified by key is empty or doesn't exist. ##### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/ // No count $redis->sRandMember('key1'); /* 'member1', 'key1' => {'member3', 'member1', 'member2'} */ $redis->sRandMember('key1'); /* 'member3', 'key1' => {'member3', 'member1', 'member2'} */ // With a count $redis->sRandMember('key1', 3); // Will return an array with all members from the set $redis->sRandMember('key1', 2); // Will an array with 2 members of the set $redis->sRandMember('key1', -100); // Will return an array of 100 elements, picked from our set (with dups) $redis->sRandMember('empty-set', 100); // Will return an empty array $redis->sRandMember('not-a-set', 100); // Will return FALSE ~~~ ### sRem, sRemove ----- _**Description**_: Removes the specified member from the set value stored at key. ##### *Parameters* *key* *member* ##### *Return value* *LONG* The number of elements removed from the set. ##### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ $redis->sRem('key1', 'member2', 'member3'); /*return 2. 'key1' => {'member1'} */ ~~~ **Note:** `sRemove` is an alias for `sRem` and will be removed in future versions of phpredis. ### sUnion ----- _**Description**_: Performs the union between N sets and returns it. ##### *Parameters* *Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. ##### *Return value* *Array of strings*: The union of all these sets. **Note:** `sUnion` can also take a single array with keys (see example below). ##### *Example* ~~~php $redis->del('s0', 's1', 's2'); $redis->sAdd('s0', '1'); $redis->sAdd('s0', '2'); $redis->sAdd('s1', '3'); $redis->sAdd('s1', '1'); $redis->sAdd('s2', '3'); $redis->sAdd('s2', '4'); /* Get the union with variadic arguments */ var_dump($redis->sUnion('s0', 's1', 's2')); /* Pass a single array */ var_dump($redis->sUnion(['s0', 's1', 's2'])); ~~~ Return value: all elements that are either in s0 or in s1 or in s2. ~~~ array(4) { [0]=> string(1) "3" [1]=> string(1) "4" [2]=> string(1) "1" [3]=> string(1) "2" } ~~~ ### sUnionStore ----- _**Description**_: Performs the same action as sUnion, but stores the result in the first key ##### *Parameters* *Key*: dstkey, the key to store the diff into. *Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. ##### *Return value* *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* ~~~php $redis->del('s0', 's1', 's2'); $redis->sAdd('s0', '1'); $redis->sAdd('s0', '2'); $redis->sAdd('s1', '3'); $redis->sAdd('s1', '1'); $redis->sAdd('s2', '3'); $redis->sAdd('s2', '4'); var_dump($redis->sUnionStore('dst', 's0', 's1', 's2')); var_dump($redis->sMembers('dst')); ~~~ Return value: the number of elements that are either in s0 or in s1 or in s2. ~~~ int(4) array(4) { [0]=> string(1) "3" [1]=> string(1) "4" [2]=> string(1) "1" [3]=> string(1) "2" } ~~~ ### sScan ----- _**Description**_: Scan a set for members ##### *Parameters* *Key*: The set to search *iterator*: LONG (reference) to the iterator as we go *pattern*: String, optional pattern to match against *count*: How many members to return at a time (Redis might return a different amount) ##### *Return value* *Array, boolean*: PHPRedis will return an array of keys or FALSE when we're done iterating ##### *Example* ~~~php $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* don't return empty results until we're done */ while($arr_mems = $redis->sScan('set', $it, "*pattern*")) { foreach($arr_mems as $str_mem) { echo "Member: $str_mem\n"; } } $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); /* return after each iteration, even if empty */ while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) { if(count($arr_mems) > 0) { foreach($arr_mems as $str_mem) { echo "Member found: $str_mem\n"; } } else { echo "No members in this iteration, iterator value: $it\n"; } } ~~~ ## Sorted sets * [bzPop](#bzpop) - Block until Redis can pop the highest or lowest scoring member from one or more ZSETs. * [zAdd](#zadd) - Add one or more members to a sorted set or update its score if it already exists * [zCard, zSize](#zcard-zsize) - Get the number of members in a sorted set * [zCount](#zcount) - Count the members in a sorted set with scores within the given values * [zIncrBy](#zincrby) - Increment the score of a member in a sorted set * [zinterstore, zInter](#zinterstore-zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key * [zPop](#zpop) - Redis can pop the highest or lowest scoring member from one a ZSET. * [zRange](#zrange) - Return a range of members in a sorted set, by index * [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score * [zRangeByLex](#zrangebylex) - Return a lexicographical range from members that share the same score * [zRank, zRevRank](#zrank-zrevrank) - Determine the index of a member in a sorted set * [zRem, zDelete, zRemove](#zrem-zdelete-zremove) - Remove one or more members from a sorted set * [zRemRangeByRank, zDeleteRangeByRank](#zremrangebyrank-zdeleterangebyrank) - Remove all members in a sorted set within the given indexes * [zRemRangeByScore, zDeleteRangeByScore, zRemoveRangeByScore](#zremrangebyscore-zdeleterangebyscore-zremoverangebyscore) - Remove all members in a sorted set within the given scores * [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low * [zScore](#zscore) - Get the score associated with the given member in a sorted set * [zunionstore, zUnion](#zunionstore-zunion) - Add multiple sorted sets and store the resulting sorted set in a new key * [zScan](#zscan) - Scan a sorted set for members ### bzPop ----- _**Description**_: Block until Redis can pop the highest or lowest scoring members from one or more ZSETs. There are two commands (`BZPOPMIN` and `BZPOPMAX` for popping the lowest and highest scoring elements respectively.) ##### *Prototype* ~~~php $redis->bzPopMin(array $keys, int $timeout): array $redis->bzPopMax(array $keys, int $timeout): array $redis->bzPopMin(string $key1, string $key2, ... int $timeout): array $redis->bzPopMax(string $key1, string $key2, ... int $timeout): array ~~~ ##### *Return value* *ARRAY:* Either an array with the key member and score of the highest or lowest element or an empty array if the timeout was reached without an element to pop. ##### *Example* ~~~php /* Wait up to 5 seconds to pop the *lowest* scoring member from sets `zs1` and `zs2`. */ $redis->bzPopMin(['zs1', 'zs2'], 5); $redis->bzPopMin('zs1', 'zs2', 5); /* Wait up to 5 seconds to pop the *highest* scoring member from sets `zs1` and `zs2` */ $redis->bzPopMax(['zs1', 'zs2'], 5); $redis->bzPopMax('zs1', 'zs2', 5); ~~~ **Note:** Calling these functions with an array of keys or with a variable number of arguments is functionally identical. ### zAdd ----- _**Description**_: Add one or more members to a sorted set or update its score if it already exists ##### *Prototype* ~~~php $redis->zAdd($key, [ $options ,] $score, $value [, $score1, $value1, ...]); ~~~ ##### *Parameters* *key*: string *options*: array (optional) *score*: double *value*: string *score1*: double *value1*: string ##### *Return value* *Long* 1 if the element is added. 0 otherwise. ##### *Example* ~~~php $redis->zAdd('key', 1, 'val1'); $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 5, 'val5'); $redis->zRange('key', 0, -1); // [val0, val1, val5] // From Redis 3.0.2 it's possible to add options like XX, NX, CH, INCR $redis->zAdd('key', ['CH'], 5, 'val5', 10, 'val10', 15, 'val15'); ~~~ ### zCard, zSize ----- _**Description**_: Returns the cardinality of an ordered set. ##### *Parameters* *key* ##### *Return value* *Long*, the set's cardinality ##### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); $redis->zCard('key'); /* 3 */ ~~~ **Note**: `zSize` is an alias for `zCard` and will be removed in future versions of phpredis. ### zCount ----- _**Description**_: Returns the *number* of elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. ##### *Parameters* *key* *start*: string *end*: string ##### *Return value* *LONG* the size of a corresponding zRangeByScore. ##### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); $redis->zCount('key', 0, 3); /* 2, corresponding to ['val0', 'val2'] */ ~~~ ### zIncrBy ----- _**Description**_: Increments the score of a member from a sorted set by a given amount. ##### *Parameters* *key* *value*: (double) value that will be added to the member's score *member* ##### *Return value* *DOUBLE* the new value ##### *Examples* ~~~php $redis->del('key'); $redis->zIncrBy('key', 2.5, 'member1'); /* key or member1 didn't exist, so member1's score is to 0 before the increment */ /* and now has the value 2.5 */ $redis->zIncrBy('key', 1, 'member1'); /* 3.5 */ ~~~ ### zinterstore, zInter ----- _**Description**_: Creates an intersection of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. The third optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. ##### *Parameters* *keyOutput* *arrayZSetKeys* *arrayWeights* *aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zinterstore. ##### *Return value* *LONG* The number of values in the new sorted set. ##### *Example* ~~~php $redis->del('k1'); $redis->del('k2'); $redis->del('k3'); $redis->del('ko1'); $redis->del('ko2'); $redis->del('ko3'); $redis->del('ko4'); $redis->zAdd('k1', 0, 'val0'); $redis->zAdd('k1', 1, 'val1'); $redis->zAdd('k1', 3, 'val3'); $redis->zAdd('k2', 2, 'val1'); $redis->zAdd('k2', 3, 'val3'); $redis->zinterstore('ko1', ['k1', 'k2']); /* 2, 'ko1' => ['val1', 'val3'] */ $redis->zinterstore('ko2', ['k1', 'k2'], [1, 1]); /* 2, 'ko2' => ['val1', 'val3'] */ /* Weighted zinterstore */ $redis->zinterstore('ko3', ['k1', 'k2'], [1, 5], 'min'); /* 2, 'ko3' => ['val1', 'val3'] */ $redis->zinterstore('ko4', ['k1', 'k2'], [1, 5], 'max'); /* 2, 'ko4' => ['val3', 'val1'] */ ~~~ **Note:** `zInter` is an alias for `zinterstore` and will be removed in future versions of phpredis. ### zPop ----- _**Description**_: Can pop the highest or lowest scoring members from one ZSETs. There are two commands (`ZPOPMIN` and `ZPOPMAX` for popping the lowest and highest scoring elements respectively.) ##### *Prototype* ~~~php $redis->zPopMin(string $key, int $count): array $redis->zPopMax(string $key, int $count): array $redis->zPopMin(string $key, int $count): array $redis->zPopMax(string $key, int $count): array ~~~ ##### *Return value* *ARRAY:* Either an array with the key member and score of the highest or lowest element or an empty array if there is no element available. ##### *Example* ~~~php /* Pop the *lowest* scoring member from set `zs1`. */ $redis->zPopMin('zs1', 5); /* Pop the *highest* scoring member from set `zs1`. */ $redis->zPopMax('zs1', 5); ~~~ ### zRange ----- _**Description**_: Returns a range of elements from the ordered set stored at the specified key, with values in the range [start, end]. Start and stop are interpreted as zero-based indices: `0` the first element, `1` the second ... `-1` the last element, `-2` the penultimate ... ##### *Parameters* *key* *start*: long *end*: long *withscores*: bool = false ##### *Return value* *Array* containing the values in specified range. ##### *Example* ~~~php $redis->zAdd('key1', 0, 'val0'); $redis->zAdd('key1', 2, 'val2'); $redis->zAdd('key1', 10, 'val10'); $redis->zRange('key1', 0, -1); /* ['val0', 'val2', 'val10'] */ // with scores $redis->zRange('key1', 0, -1, true); /* ['val0' => 0, 'val2' => 2, 'val10' => 10] */ ~~~ ### zRangeByScore, zRevRangeByScore ----- _**Description**_: Returns the elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. zRevRangeByScore returns the same items in reverse order, when the `start` and `end` parameters are swapped. ##### *Parameters* *key* *start*: string *end*: string *options*: array Two options are available: `withscores => TRUE`, and `limit => [$offset, $count]` ##### *Return value* *Array* containing the values in specified range. ##### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); $redis->zRangeByScore('key', 0, 3); /* ['val0', 'val2'] */ $redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]); /* ['val0' => 0, 'val2' => 2] */ $redis->zRangeByScore('key', 0, 3, ['limit' => [1, 1]]); /* ['val2'] */ $redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE, 'limit' => [1, 1]]); /* ['val2' => 2] */ $redis->zRangeByScore('key', '-inf', '+inf', ['withscores' => TRUE]); /* ['val0' => 0, 'val2' => 2, 'val10' => 10] */ ~~~ ### zRangeByLex ----- _**Description**_: Returns a lexicographical range of members in a sorted set, assuming the members have the same score. The min and max values are required to start with '(' (exclusive), '[' (inclusive), or be exactly the values '-' (negative inf) or '+' (positive inf). The command must be called with either three *or* five arguments or will return FALSE. ##### *Parameters* *key*: The ZSET you wish to run against *min*: The minimum alphanumeric value you wish to get *max*: The maximum alphanumeric value you wish to get *offset*: Optional argument if you wish to start somewhere other than the first element. *limit*: Optional argument if you wish to limit the number of elements returned. ##### *Return value* *Array* containing the values in the specified range. ##### *Example* ~~~php foreach(['a','b','c','d','e','f','g'] as $c) $redis->zAdd('key',0,$c); $redis->zRangeByLex('key','-','[c') /* ['a','b','c']; */ $redis->zRangeByLex('key','-','(c') /* ['a','b'] */ $redis->zRangeByLex('key','-','[c',1,2) /* ['b','c'] */ ~~~ ### zRank, zRevRank ----- _**Description**_: Returns the rank of a given member in the specified sorted set, starting at 0 for the item with the smallest score. zRevRank starts at 0 for the item with the *largest* score. ##### *Parameters* *key* *member* ##### *Return value* *Long*, the item's rank. ##### *Example* ~~~php $redis->del('z'); $redis->zAdd('key', 1, 'one'); $redis->zAdd('key', 2, 'two'); $redis->zRank('key', 'one'); /* 0 */ $redis->zRank('key', 'two'); /* 1 */ $redis->zRevRank('key', 'one'); /* 1 */ $redis->zRevRank('key', 'two'); /* 0 */ ~~~ ### zRem, zDelete, zRemove ----- _**Description**_: Delete one or more members from a sorted set. ##### *Prototype* ~~~php $redis->zRem($key, $member [, $member ...]); ~~~ ##### *Return value* *LONG:* The number of members deleted. ##### *Example* ~~~php $redis->zAdd('key', 0, 'val0', 1, 'val1', 2, 'val2'); $redis->zRem('key', 'val0', 'val1', 'val2'); // Returns: 3 ~~~ **Note:** `zDelete` and `zRemove` are an alias for `zRem` and will be removed in future versions of phpredis. ### zRemRangeByRank, zDeleteRangeByRank ----- _**Description**_: Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end]. ##### *Parameters* *key* *start*: LONG *end*: LONG ##### *Return value* *LONG* The number of values deleted from the sorted set ##### *Example* ~~~php $redis->zAdd('key', 1, 'one'); $redis->zAdd('key', 2, 'two'); $redis->zAdd('key', 3, 'three'); $redis->zRemRangeByRank('key', 0, 1); /* 2 */ $redis->zRange('key', 0, -1, ['withscores' => TRUE]); /* ['three' => 3] */ ~~~ **Note:** `zDeleteRangeByRank` is an alias for `zRemRangeByRank` and will be removed in future versions of phpredis. ### zRemRangeByScore, zDeleteRangeByScore, zRemoveRangeByScore ----- _**Description**_: Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end]. ##### *Parameters* *key* *start*: double or "+inf" or "-inf" string *end*: double or "+inf" or "-inf" string ##### *Return value* *LONG* The number of values deleted from the sorted set ##### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); $redis->zRemRangeByScore('key', 0, 3); /* 2 */ ~~~ **Note:** `zDeleteRangeByScore` and `zRemoveRangeByScore` are an alias for `zRemRangeByScore` and will be removed in future versions of phpredis. ### zRevRange ----- _**Description**_: Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpreted as zero-based indices: `0` the first element, `1` the second ... `-1` the last element, `-2` the penultimate ... ##### *Parameters* *key* *start*: long *end*: long *withscores*: bool = false ##### *Return value* *Array* containing the values in specified range. ##### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); $redis->zRevRange('key', 0, -1); /* ['val10', 'val2', 'val0'] */ // with scores $redis->zRevRange('key', 0, -1, true); /* ['val10' => 10, 'val2' => 2, 'val0' => 0] */ ~~~ ### zScore ----- _**Description**_: Returns the score of a given member in the specified sorted set. ##### *Parameters* *key* *member* ##### *Return value* *Double* or *FALSE* when the value is not found ##### *Example* ~~~php $redis->zAdd('key', 2.5, 'val2'); $redis->zScore('key', 'val2'); /* 2.5 */ ~~~ ### zunionstore, zUnion ----- _**Description**_: Creates an union of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. The third optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. ##### *Parameters* *keyOutput* *arrayZSetKeys* *arrayWeights* *aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zunionstore. ##### *Return value* *LONG* The number of values in the new sorted set. ##### *Example* ~~~php $redis->del('k1'); $redis->del('k2'); $redis->del('k3'); $redis->del('ko1'); $redis->del('ko2'); $redis->del('ko3'); $redis->zAdd('k1', 0, 'val0'); $redis->zAdd('k1', 1, 'val1'); $redis->zAdd('k2', 2, 'val2'); $redis->zAdd('k2', 3, 'val3'); $redis->zunionstore('ko1', ['k1', 'k2']); /* 4, 'ko1' => ['val0', 'val1', 'val2', 'val3'] */ /* Weighted zunionstore */ $redis->zunionstore('ko2', ['k1', 'k2'], [1, 1]); /* 4, 'ko2' => ['val0', 'val1', 'val2', 'val3'] */ $redis->zunionstore('ko3', ['k1', 'k2'], [5, 1]); /* 4, 'ko3' => ['val0', 'val2', 'val3', 'val1'] */ ~~~ **Note:** `zUnion` is an alias for `zunionstore` and will be removed in future versions of phpredis. ### zScan ----- _**Description**_: Scan a sorted set for members, with optional pattern and count ##### *Parameters* *key*: String, the set to scan *iterator*: Long (reference), initialized to NULL *pattern*: String (optional), the pattern to match *count*: How many keys to return per iteration (Redis might return a different number) ##### *Return value* *Array, boolean* PHPRedis will return matching keys from Redis, or FALSE when iteration is complete ##### *Example* ~~~php $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { foreach($arr_matches as $str_mem => $f_score) { echo "Key: $str_mem, Score: $f_score\n"; } } ~~~ ## HyperLogLogs ### pfAdd ----- _**Description**_: Adds the specified elements to the specified HyperLogLog. ##### *Prototype* ~~~php $redis->pfAdd($key, Array $elements); ~~~ ##### *Parameters* _Key_ _Array of values_ ##### *Return value* *Integer*: 1 if at least 1 HyperLogLog internal register was altered. 0 otherwise. ##### *Example* ~~~php $redis->pfAdd('hll', ['a', 'b', 'c']); // (int) 1 $redis->pfAdd('hll', ['a', 'b']); // (int) 0 ~~~ ### pfCount ----- _**Description**_: Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s). ##### *Prototype* ~~~php $redis->pfCount($key); $redis->pfCount(Array $keys); ~~~ ##### *Parameters* _Key_ or _Array of keys_ ##### *Return value* *Integer*: The approximated number of unique elements observed via [pfAdd](#pfAdd). ##### *Example* ~~~php $redis->pfAdd('hll1', ['a', 'b', 'c']); // (int) 1 $redis->pfCount('hll1'); // (int) 3 $redis->pfAdd('hll2', ['d', 'e', 'a']); // (int) 1 $redis->pfCount('hll2'); // (int) 3 $redis->pfCount(['hll1', 'hll2']); // (int) 5 ~~~ ### pfMerge ----- _**Description**_: Merge N different HyperLogLogs into a single one. ##### *Prototype* ~~~php $redis->pfMerge($destkey, Array $sourceKeys); ~~~ ##### *Parameters* _Destination Key_ _Array of Source Keys_ ##### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. ##### *Example* ~~~php $redis->pfAdd('hll1', ['a', 'b', 'c']); // (int) 1 $redis->pfAdd('hll2', ['d', 'e', 'a']); // (int) 1 $redis->pfMerge('hll3', ['hll1', 'hll2']); // true $redis->pfCount('hll3'); // (int) 5 ~~~ ## Geocoding ### geoAdd ----- ##### *Prototype* ~~~php $redis->geoAdd($key, $longitude, $latitude, $member [, $longitude, $latitude, $member, ...]); ~~~ _**Description**_: Add one or more geospatial items to the specified key. This function must be called with at least one _longitude, latitude, member_ triplet. ##### *Return value* *Integer*: The number of elements added to the geospatial key. ##### *Example* ~~~php $redis->del("myplaces"); /* Since the key will be new, $result will be 2 */ $result = $redis->geoAdd( "myplaces", -122.431, 37.773, "San Francisco", -157.858, 21.315, "Honolulu" ); ~~~ ### geoHash ----- ##### *Prototype* ~~~php $redis->geoHash($key, $member [, $member, $member, ...]); ~~~ _**Description**_: Retrieve Geohash strings for one or more elements of a geospatial index. ##### *Return value* *Array*: One or more Redis Geohash encoded strings. ##### *Example* ~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); $hashes = $redis->geoHash("hawaii", "Honolulu", "Maui"); var_dump($hashes); ~~~ ##### *Output* ~~~ array(2) { [0]=> string(11) "87z9pyek3y0" [1]=> string(11) "8e8y6d5jps0" } ~~~ ### geoPos ----- ##### *Prototype* ~~~php $redis->geoPos($key, $member [, $member, $member, ...]); ~~~ _**Description**_: Return longitude, latitude positions for each requested member. ##### *Return value* *Array*: One or more longitude/latitude positions ##### *Example* ~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); $positions = $redis->geoPos("hawaii", "Honolulu", "Maui"); var_dump($positions); ~~~ ##### *Output* ~~~ array(2) { [0]=> array(2) { [0]=> string(22) "-157.85800248384475708" [1]=> string(19) "21.3060004581273077" } [1]=> array(2) { [0]=> string(22) "-156.33099943399429321" [1]=> string(20) "20.79799924753607598" } } ~~~ ### GeoDist ----- ##### *Prototype* ~~~php $redis->geoDist($key, $member1, $member2 [, $unit]); ~~~ _**Description**_: Return the distance between two members in a geospatial set. If units are passed it must be one of the following values: * 'm' => Meters * 'km' => Kilometers * 'mi' => Miles * 'ft' => Feet ##### *Return value* *Double*: The distance between the two passed members in the units requested (meters by default). ##### *Example* ~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); $meters = $redis->geoDist("hawaii", "Honolulu", "Maui"); $kilometers = $redis->geoDist("hawaii", "Honolulu", "Maui", 'km'); $miles = $redis->geoDist("hawaii", "Honolulu", "Maui", 'mi'); $feet = $redis->geoDist("hawaii", "Honolulu", "Maui", 'ft'); echo "Distance between Honolulu and Maui:\n"; echo " meters : $meters\n"; echo " kilometers: $kilometers\n"; echo " miles : $miles\n"; echo " feet : $feet\n"; /* Bad unit */ $inches = $redis->geoDist("hawaii", "Honolulu", "Maui", 'in'); echo "Invalid unit returned:\n"; var_dump($inches); ~~~ ##### *Output* ~~~ Distance between Honolulu and Maui: meters : 168275.204 kilometers: 168.2752 miles : 104.5616 feet : 552084.0028 Invalid unit returned: bool(false) ~~~ ### geoRadius ----- ##### *Prototype* ~~~php $redis->geoRadius($key, $longitude, $latitude, $radius, $unit [, Array $options]); ~~~ _**Description**_: Return members of a set with geospatial information that are within the radius specified by the caller. ##### *Options Array* The georadius command can be called with various options that control how Redis returns results. The following table describes the options phpredis supports. All options are case insensitive. | Key | Value | Description | :--- | :--- | :---- | | COUNT | integer > 0 | Limit how many results are returned | | WITHCOORD | Return longitude and latitude of matching members | | WITHDIST | Return the distance from the center | | WITHHASH | Return the raw geohash-encoded score | | ASC | Sort results in ascending order | | DESC | Sort results in descending order | STORE | _key_ | Store results in _key_ | STOREDIST | _key_ | Store the results as distances in _key_ *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will be used. *Note*: When using `STORE[DIST]` in Redis Cluster, the store key must has to the same slot as the query key or you will get a `CROSSLOT` error. ##### *Return value* *Mixed*: When no `STORE` option is passed, this function returns an array of results. If it is passed this function returns the number of stored entries. ##### *Example* ~~~php /* Add some cities */ $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); echo "Within 300 miles of Honolulu:\n"; var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi')); echo "\nWithin 300 miles of Honolulu with distances:\n"; $options = ['WITHDIST']; var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options)); echo "\nFirst result within 300 miles of Honolulu with distances:\n"; $options['count'] = 1; var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options)); echo "\nFirst result within 300 miles of Honolulu with distances in descending sort order:\n"; $options[] = 'DESC'; var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options)); ~~~ ##### *Output* ~~~ Within 300 miles of Honolulu: array(2) { [0]=> string(8) "Honolulu" [1]=> string(4) "Maui" } Within 300 miles of Honolulu with distances: array(2) { [0]=> array(2) { [0]=> string(8) "Honolulu" [1]=> string(6) "0.0002" } [1]=> array(2) { [0]=> string(4) "Maui" [1]=> string(8) "104.5615" } } First result within 300 miles of Honolulu with distances: array(1) { [0]=> array(2) { [0]=> string(8) "Honolulu" [1]=> string(6) "0.0002" } } First result within 300 miles of Honolulu with distances in descending sort order: array(1) { [0]=> array(2) { [0]=> string(4) "Maui" [1]=> string(8) "104.5615" } } ~~~ ### geoRadiusByMember ##### *Prototype* ~~~php $redis->geoRadiusByMember($key, $member, $radius, $units [, Array $options]); ~~~ _**Description**_: This method is identical to [geoRadius](#georadius) except that instead of passing a longitude and latitude as the "source" you pass an existing member in the geospatial set. ##### *Options Array* See [geoRadius](#georadius) command for options array. ##### *Return value* *Array*: The zero or more entries that are close enough to the member given the distance and radius specified. ##### *Example* ~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); echo "Within 300 miles of Honolulu:\n"; var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi')); echo "\nFirst match within 300 miles of Honolulu:\n"; var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi', ['count' => 1])); ~~~ ##### *Output* ~~~ Within 300 miles of Honolulu: array(2) { [0]=> string(8) "Honolulu" [1]=> string(4) "Maui" } First match within 300 miles of Honolulu: array(1) { [0]=> string(8) "Honolulu" } ~~~ ## Streams * [xAck](#xack) - Acknowledge one or more pending messages * [xAdd](#xadd) - Add a message to a stream * [xClaim](#xclaim) - Acquire ownership of a pending message * [xDel](#xdel) - Remove a message from a stream * [xGroup](#xgroup) - Manage consumer groups * [xInfo](#xinfo) - Get information about a stream * [xLen](#xlen) - Get the length of a stream * [xPending](#xpending) - Inspect pending messages in a stream * [xRange](#xrange) - Query a range of messages from a stream * [xRead](#xread) - Read message(s) from a stream * [xReadGroup](#xreadgroup) - Read stream messages with a group and consumer * [xRevRange](#xrevrange) - Query one or more messages from end to start * [xTrim](#xtrim) - Trim a stream's size ### xAck ----- ##### *Prototype* ~~~php $obj_redis->xAck($stream, $group, $arr_messages); ~~~ _**Description**_: Acknowledge one or more messages on behalf of a consumer group. ##### *Return value* *long*: The number of messages Redis reports as acknowledged. ##### *Example* ~~~php $obj_redis->xAck('stream', 'group1', ['1530063064286-0', '1530063064286-1']); ~~~ ### xAdd ----- ##### *Prototype* ~~~php $obj_redis->xAdd($str_key, $str_id, $arr_message[, $i_maxlen, $boo_approximate]); ~~~ _**Description**_: Add a message to a stream ##### *Return value* *String*: The added message ID ##### *Example* ~~~php $obj_redis->xAdd('mystream', "*", ['field' => 'value']); $obj_redis->xAdd('mystream', "*", ['field' => 'value'], 1000); // set max length of stream to 1000 $obj_redis->xAdd('mystream', "*", ['field' => 'value'], 1000, true); // set max length of stream to ~1000 ~~~ ### xClaim ----- ##### *Prototype* ~~~php $obj_redis->xClaim($str_key, $str_group, $str_consumer, $min_idle_time, $arr_ids, [$arr_options]); ~~~ _**Description**_: Claim ownership of one or more pending messages. #### *Options Array* ~~~php $options = [ /* Note: 'TIME', and 'IDLE' are mutually exclusive */ 'IDLE' => $value, /* Set the idle time to $value ms */, 'TIME' => $value, /* Set the idle time to now - $value */ 'RETRYCOUNT' => $value, /* Update message retrycount to $value */ 'FORCE', /* Claim the message(s) even if they're not pending anywhere */ 'JUSTID', /* Instruct Redis to only return IDs */ ]; ~~~ ##### *Return value* *Array*: Either an array of message IDs along with corresponding data, or just an array of IDs (if the 'JUSTID' option was passed). ##### *Example* ~~~php $ids = ['1530113681011-0', '1530113681011-1', '1530113681011-2']; /* Without any options */ $obj_redis->xClaim( 'mystream', 'group1', 'myconsumer1', 0, $ids ); /* With options */ $obj_redis->xClaim( 'mystream', 'group1', 'myconsumer2', 0, $ids, [ 'IDLE' => time() * 1000, 'RETRYCOUNT' => 5, 'FORCE', 'JUSTID' ] ); ~~~ ### xDel ----- ##### *Prototype* ~~~php $obj_redis->xDel($str_key, $arr_ids); ~~~ _**Description**_: Delete one or more messages from a stream. ##### *Return value* *long*: The number of messages removed ##### *Example* ~~~php $obj_redis->xDel('mystream', ['1530115304877-0', '1530115305731-0']); ~~~ ### xGroup ----- ##### *Prototype* ~~~php $obj_redis->xGroup('HELP'); $obj_redis->xGroup('CREATE', $str_key, $str_group, $str_msg_id, [$boo_mkstream]); $obj_redis->xGroup('SETID', $str_key, $str_group, $str_msg_id); $obj_redis->xGroup('DESTROY', $str_key, $str_group); $obj_redis->xGroup('DELCONSUMER', $str_key, $str_group, $str_consumer_name); ~~~ _**Description**_: This command is used in order to create, destroy, or manage consumer groups. ##### *Return value* *Mixed*: This command returns different types depending on the specific XGROUP command executed. ##### *Example* ~~~php $obj_redis->xGroup('CREATE', 'mystream', 'mygroup', '0'); $obj_redis->xGroup('CREATE', 'mystream', 'mygroup2', '0', true); /* Create stream if non-existent. */ $obj_redis->xGroup('DESTROY', 'mystream', 'mygroup'); ~~~ ### xInfo ----- ##### *Prototype* ~~~php $obj_redis->xInfo('CONSUMERS', $str_stream, $str_group); $obj_redis->xInfo('GROUPS', $str_stream); $obj_redis->xInfo('STREAM', $str_stream [, 'FULL' [, $i_count]]); $obj_redis->xInfo('HELP'); ~~~ _**Description**_: Get information about a stream or consumer groups. ##### *Return value* *Mixed*: This command returns different types depending on which subcommand is used. ##### *Example* ~~~php $obj_redis->xInfo('STREAM', 'mystream'); $obj_redis->xInfo('STREAM', 'mystream', 'FULL', 10); ~~~ ### xLen ----- ##### *Prototype* ~~~php $obj_redis->xLen($str_stream); ~~~ _**Description**_: Get the length of a given stream ##### *Return value* *Long*: The number of messages in the stream. ##### *Example* ~~~php $obj_redis->xLen('mystream'); ~~~ ### xPending ----- ##### *Prototype* ~~~php $obj_redis->xPending($str_stream, $str_group [, $str_start, $str_end, $i_count, $str_consumer]); ~~~ _**Description**_: Get information about pending messages in a given stream. ##### *Return value* *Array*: Information about the pending messages, in various forms depending on the specific invocation of XPENDING. ##### *Examples* ~~~php $obj_redis->xPending('mystream', 'mygroup'); $obj_redis->xPending('mystream', 'mygroup', '-', '+', 1, 'consumer-1'); ~~~ ### xRange ----- ##### *Prototype* ~~~php $obj_redis->xRange($str_stream, $str_start, $str_end [, $i_count]); ~~~ _**Description**_: Get a range of messages from a given stream. ##### *Return value* *Array*: The messages in the stream within the requested range. ##### *Example* ~~~php /* Get everything in this stream */ $obj_redis->xRange('mystream', '-', '+'); /* Only the first two messages */ $obj_redis->xRange('mystream', '-', '+', 2); ~~~ ### xRead ----- ##### *Prototype* ~~~php $obj_redis->xRead($arr_streams [, $i_count, $i_block]); ~~~ _**Description**_: Read data from one or more streams and only return IDs greater than sent in the command. ##### *Return value* *Array*: The messages in the stream newer than the IDs passed to Redis (if any). ##### *Example* ~~~php $obj_redis->xRead(['stream1' => '1535222584555-0', 'stream2' => '1535222584555-0']); /* --- Possible output --- Array ( [stream1] => Array ( [1535222584555-1] => Array ( [key:1] => val:1 ) ) [stream2] => Array ( [1535222584555-1] => Array ( [key:1] => val:1 ) ) ) */ // Receive only new message ($ = last id) and wait for one new message unlimited time $obj_redis->xRead(['stream1' => '$'], 1, 0); ~~~ ### xReadGroup ----- ##### *Prototype* ~~~php $obj_redis->xReadGroup($str_group, $str_consumer, $arr_streams [, $i_count, $i_block]); ~~~ _**Description**_: This method is similar to xRead except that it supports reading messages for a specific consumer group. ##### *Return value* *Array*: The messages delivered to this consumer group (if any). ##### *Examples* ~~~php /* Consume messages for 'mygroup', 'consumer1' */ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => 0, 's2' => 0]); /* Consume messages for 'mygroup', 'consumer1' which were not consumed yet by the group */ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => '>', 's2' => '>']); /* Read a single message as 'consumer2' wait for up to a second until a message arrives. */ $obj_redis->xReadGroup('mygroup', 'consumer2', ['s1' => 0, 's2' => 0], 1, 1000); ~~~ ### xRevRange ----- ##### *Prototype* ~~~php $obj_redis->xRevRange($str_stream, $str_end, $str_start [, $i_count]); ~~~ _**Description**_: This is identical to xRange except the results come back in reverse order. Also note that Redis reverses the order of "start" and "end". ##### *Return value* *Array*: The messages in the range specified. ##### *Example* ~~~php $obj_redis->xRevRange('mystream', '+', '-'); ~~~ ### xTrim ----- ##### *Prototype* ~~~php $obj_redis->xTrim($str_stream, $i_max_len [, $boo_approximate]); ~~~ _**Description**_: Trim the stream length to a given maximum. If the "approximate" flag is pasesed, Redis will use your size as a hint but only trim trees in whole nodes (this is more efficient). ##### *Return value* *long*: The number of messages trimmed from the stream. ##### *Example* ~~~php /* Trim to exactly 100 messages */ $obj_redis->xTrim('mystream', 100); /* Let Redis approximate the trimming */ $obj_redis->xTrim('mystream', 100, true); ~~~ ## Pub/sub * [pSubscribe](#psubscribe) - Subscribe to channels by pattern * [publish](#publish) - Post a message to a channel * [subscribe](#subscribe) - Subscribe to channels * [pubSub](#pubsub) - Introspection into the pub/sub subsystem ### pSubscribe ----- _**Description**_: Subscribe to channels by pattern ##### *Parameters* *patterns*: An array of patterns to match *callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~php function pSubscribe($redis, $pattern, $chan, $msg) { echo "Pattern: $pattern\n"; echo "Channel: $chan\n"; echo "Payload: $msg\n"; } ~~~ ### publish ----- _**Description**_: Publish messages to channels. Warning: this function will probably change in the future. ##### *Parameters* *channel*: a channel to publish to *message*: string ##### *Example* ~~~php $redis->publish('chan-1', 'hello, world!'); // send message. ~~~ ### subscribe ----- _**Description**_: Subscribe to channels. Warning: this function will probably change in the future. ##### *Parameters* *channels*: an array of channels to subscribe to *callback*: either a string or an Array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~php function f($redis, $chan, $msg) { switch($chan) { case 'chan-1': ... break; case 'chan-2': ... break; case 'chan-2': ... break; } } $redis->subscribe(['chan-1', 'chan-2', 'chan-3'], 'f'); // subscribe to 3 chans ~~~ ### pubSub ----- _**Description**_: A command allowing you to get information on the Redis pub/sub system. ##### *Parameters* *keyword*: String, which can be: "channels", "numsub", or "numpat" *argument*: Optional, variant. For the "channels" subcommand, you can pass a string pattern. For "numsub" an array of channel names. ##### *Return value* *CHANNELS*: Returns an array where the members are the matching channels. *NUMSUB*: Returns a key/value array where the keys are channel names and values are their counts. *NUMPAT*: Integer return containing the number active pattern subscriptions ##### *Example* ~~~php $redis->pubSub("channels"); /*All channels */ $redis->pubSub("channels", "*pattern*"); /* Just channels matching your pattern */ $redis->pubSub("numsub", ["chan1", "chan2"]); /*Get subscriber counts for 'chan1' and 'chan2'*/ $redis->pubSub("numpat"); /* Get the number of pattern subscribers */ ~~~ ## Generic 1. [rawCommand](#rawcommand) - Execute any generic command against the server. ### rawCommand ----- _**Description**_: A method to execute any arbitrary command against the a Redis server ##### *Parameters* This method is variadic and takes a dynamic number of arguments of various types (string, long, double), but must be passed at least one argument (the command keyword itself). ##### *Return value* The return value can be various types depending on what the server itself returns. No post processing is done to the returned value and must be handled by the client code. ##### *Example* ```php /* Returns: true */ $redis->rawCommand("set", "foo", "bar"); /* Returns: "bar" */ $redis->rawCommand("get", "foo"); /* Returns: 3 */ $redis->rawCommand("rpush", "mylist", "one", 2, 3.5); /* Returns: ["one", "2", "3.5000000000000000"] */ $redis->rawCommand("lrange", "mylist", 0, -1); ``` ## Transactions 1. [multi, exec, discard](#multi-exec-discard) - Enter and exit transactional mode 2. [watch, unwatch](#watch-unwatch) - Watches a key for modifications by another client. ### multi, exec, discard. ----- _**Description**_: Enter and exit transactional mode. ##### *Parameters* (optional) `Redis::MULTI` or `Redis::PIPELINE`. Defaults to `Redis::MULTI`. A `Redis::MULTI` block of commands runs as a single transaction; a `Redis::PIPELINE` block is simply transmitted faster to the server, but without any guarantee of atomicity. `discard` cancels a transaction. ##### *Return value* `multi()` returns the Redis instance and enters multi-mode. Once in multi-mode, all subsequent method calls return the same object until `exec()` is called. ##### *Example* ~~~php $ret = $redis->multi() ->set('key1', 'val1') ->get('key1') ->set('key2', 'val2') ->get('key2') ->exec(); /* $ret == Array(0 => TRUE, 1 => 'val1', 2 => TRUE, 3 => 'val2'); */ ~~~ ### watch, unwatch ----- _**Description**_: Watches a key for modifications by another client. If the key is modified between `WATCH` and `EXEC`, the MULTI/EXEC transaction will fail (return `FALSE`). `unwatch` cancels all the watching of all keys by this client. ##### *Parameters* *keys*: string for one key or array for a list of keys ##### *Example* ~~~php $redis->watch('x'); // or for a list of keys: $redis->watch(['x','another key']); /* long code here during the execution of which other clients could well modify `x` */ $ret = $redis->multi() ->incr('x') ->exec(); /* $ret = FALSE if x has been modified between the call to WATCH and the call to EXEC. */ ~~~ ## Scripting * [eval](#eval) - Evaluate a LUA script serverside * [evalSha](#evalsha) - Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself * [script](#script) - Execute the Redis SCRIPT command to perform various operations on the scripting subsystem * [getLastError](#getlasterror) - The last error message (if any) * [clearLastError](#clearlasterror) - Clear the last error message * [_prefix](#_prefix) - A utility method to prefix the value with the prefix setting for phpredis * [_unserialize](#_unserialize) - A utility method to unserialize data with whatever serializer is set up * [_serialize](#_serialize) - A utility method to serialize data with whatever serializer is set up ### eval ----- _**Description**_: Evaluate a LUA script serverside ##### *Parameters* *script* string. *args* array, optional. *num_keys* int, optional. ##### *Return value* Mixed. What is returned depends on what the LUA script itself returns, which could be a scalar value (int/string), or an array. Arrays that are returned can also contain other arrays, if that's how it was set up in your LUA script. If there is an error executing the LUA script, the getLastError() function can tell you the message that came back from Redis (e.g. compile error). ##### *Examples* ~~~php $redis->eval("return 1"); // Returns an integer: 1 $redis->eval("return {1,2,3}"); // Returns [1,2,3] $redis->del('mylist'); $redis->rpush('mylist','a'); $redis->rpush('mylist','b'); $redis->rpush('mylist','c'); // Nested response: [1,2,3,['a','b','c']]; $redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}"); ~~~ ### evalSha ----- _**Description**_: Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself. In order to run this command Redis will have to have already loaded the script, either by running it or via the SCRIPT LOAD command. ##### *Parameters* *script_sha* string. The sha1 encoded hash of the script you want to run. *args* array, optional. Arguments to pass to the LUA script. *num_keys* int, optional. The number of arguments that should go into the KEYS array, vs. the ARGV array when Redis spins the script ##### *Return value* Mixed. See EVAL ##### *Examples* ~~~php $script = 'return 1'; $sha = $redis->script('load', $script); $redis->evalSha($sha); // Returns 1 ~~~ ### script ----- _**Description**_: Execute the Redis SCRIPT command to perform various operations on the scripting subsystem. ##### *Usage* ~~~php $redis->script('load', $script); $redis->script('flush'); $redis->script('kill'); $redis->script('exists', $script1, [$script2, $script3, ...]); ~~~ ##### *Return value* * SCRIPT LOAD will return the SHA1 hash of the passed script on success, and FALSE on failure. * SCRIPT FLUSH should always return TRUE * SCRIPT KILL will return true if a script was able to be killed and false if not * SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script ### client ----- _**Description**_: Issue the CLIENT command with various arguments. The Redis CLIENT command can be used in four ways. * CLIENT LIST * CLIENT GETNAME * CLIENT SETNAME [name] * CLIENT KILL [ip:port] ##### *Usage* ~~~php $redis->client('list'); // Get a list of clients $redis->client('getname'); // Get the name of the current connection $redis->client('setname', 'somename'); // Set the name of the current connection $redis->client('kill', ); // Kill the process at ip:port ~~~ ##### *Return value* This will vary depending on which client command was executed. * CLIENT LIST will return an array of arrays with client information. * CLIENT GETNAME will return the client name or false if none has been set * CLIENT SETNAME will return true if it can be set and false if not * CLIENT KILL will return true if the client can be killed, and false if not Note: phpredis will attempt to reconnect so you can actually kill your own connection but may not notice losing it! ### getLastError ----- _**Description**_: The last error message (if any) ##### *Parameters* *none* ##### *Return value* A string with the last returned script based error message, or NULL if there is no error ##### *Examples* ~~~php $redis->eval('this-is-not-lua'); $err = $redis->getLastError(); // "ERR Error compiling script (new function): user_script:1: '=' expected near '-'" ~~~ ### clearLastError ----- _**Description**_: Clear the last error message ##### *Parameters* *none* ##### *Return value* *BOOL* TRUE ##### *Examples* ~~~php $redis->set('x', 'a'); $redis->incr('x'); $err = $redis->getLastError(); // "ERR value is not an integer or out of range" $redis->clearLastError(); $err = $redis->getLastError(); // NULL ~~~ ### _prefix ----- _**Description**_: A utility method to prefix the value with the prefix setting for phpredis. ##### *Parameters* *value* string. The value you wish to prefix ##### *Return value* If a prefix is set up, the value now prefixed. If there is no prefix, the value will be returned unchanged. ##### *Examples* ~~~php $redis->setOption(Redis::OPT_PREFIX, 'my-prefix:'); $redis->_prefix('my-value'); // Will return 'my-prefix:my-value' ~~~ ### _serialize ----- _**Description**_: A utility method to serialize values manually. This method allows you to serialize a value with whatever serializer is configured, manually. This can be useful for serialization/unserialization of data going in and out of EVAL commands as phpredis can't automatically do this itself. Note that if no serializer is set, phpredis will change Array values to 'Array', and Objects to 'Object'. ##### *Parameters* *value*: Mixed. The value to be serialized ##### *Examples* ~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); $redis->_serialize("foo"); // returns "foo" $redis->_serialize([]); // Returns "Array" $redis->_serialize(new stdClass()); // Returns "Object" $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); $redis->_serialize("foo"); // Returns 's:3:"foo";' ~~~ ### _unserialize ----- _**Description**_: A utility method to unserialize data with whatever serializer is set up. If there is no serializer set, the value will be returned unchanged. If there is a serializer set up, and the data passed in is malformed, an exception will be thrown. This can be useful if phpredis is serializing values, and you return something from redis in a LUA script that is serialized. ##### *Parameters* *value* string. The value to be unserialized ##### *Examples* ~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); $redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return [1,2,3] ~~~ ## Introspection ### isConnected ----- _**Description**_: A method to determine if a phpredis object thinks it's connected to a server ##### *Parameters* None ##### *Return value* *Boolean* Returns TRUE if phpredis thinks it's connected and FALSE if not ### getHost ----- _**Description**_: Retrieve our host or unix socket that we're connected to ##### *Parameters* None ##### *Return value* *Mixed* The host or unix socket we're connected to or FALSE if we're not connected ### getPort ----- _**Description**_: Get the port we're connected to ##### *Parameters* None ##### *Return value* *Mixed* Returns the port we're connected to or FALSE if we're not connected ### getDbNum ----- _**Description**_: Get the database number phpredis is pointed to ##### *Parameters* None ##### *Return value* *Mixed* Returns the database number (LONG) phpredis thinks it's pointing to or FALSE if we're not connected ### getTimeout ----- _**Description**_: Get the (write) timeout in use for phpredis ##### *Parameters* None ##### *Return value* *Mixed* The timeout (DOUBLE) specified in our connect call or FALSE if we're not connected ### getReadTimeout _**Description**_: Get the read timeout specified to phpredis or FALSE if we're not connected ##### *Parameters* None ##### *Return value* *Mixed* Returns the read timeout (which can be set using setOption and Redis::OPT_READ_TIMEOUT) or FALSE if we're not connected ### getPersistentID ----- _**Description**_: Gets the persistent ID that phpredis is using ##### *Parameters* None ##### *Return value* *Mixed* Returns the persistent id phpredis is using (which will only be set if connected with pconnect), NULL if we're not using a persistent ID, and FALSE if we're not connected ### getAuth ----- _**Description**_: Get the password (or username and password if using Redis 6 ACLs) used to authenticate the connection. ### *Parameters* None ### *Return value* *Mixed* Returns NULL if no username/password are set, the password string if a password is set, and a `[username, password]` array if authenticated with a username and password. redis-6.0.2/INSTALL.md0000644000175000000120000000756714515245367015067 0ustar pyatsukhnenkowheel# Installation from pecl/pickle To pull latest stable released version, from [pecl](https://pecl.php.net/package/redis) / [pickle](https://wiki.php.net/rfc/deprecate-pear-include-composer): ~~~ pecl install redis // If using PHP >= 7.3 pickle install redis ~~~ # Installation from sources To build this extension for the sources tree: ~~~ git clone https://github.com/phpredis/phpredis.git cd phpredis phpize ./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd] [--enable-redis-lz4] make && make install ~~~ If you would like phpredis to serialize your data using the igbinary library, run configure with `--enable-redis-igbinary`. If you would like to use the msgpack serializer, run configure with `--enable-redis-msgpack` (note: Requires php-msgpack >= 2.0.3) The extension also may compress data before sending it to Redis server, if you run configure with `--enable-redis-lzf`. If you want to use lzf library pre-installed into your system use `--with-liblzf` configuration option to specify the path where to search files. `make install` copies `redis.so` to an appropriate location, but you still need to enable the module in the PHP config file. To do so, either edit your php.ini or add a redis.ini file in `/etc/php5/conf.d` with the following contents: `extension=redis.so`. You can generate a debian package for PHP5, accessible from Apache 2 by running `./mkdeb-apache2.sh` or with `dpkg-buildpackage` or `svn-buildpackage`. This extension exports a single class, [Redis](./README.md#class-redis) (and [RedisException](./README.md#class-redisexception) used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion. # Binary packages Most distributions provides pre-build binary packages of this extension. ## Windows: Follow the DLL link on the [https://pecl.php.net/package/redis](https://pecl.php.net/package/redis) page or use [https://windows.php.net/downloads/pecl/releases/redis/](https://windows.php.net/downloads/pecl/releases/redis/) ## Fedora Fedora users can install the package from the official repository. ### Fedora ≥ 29, Version 5 Installation of the [php-pecl-redis5](https://apps.fedoraproject.org/packages/php-pecl-redis5) package: ~~~ dnf install php-pecl-redis5 ~~~ ## RHEL / CentOS Installation of the [php-pecl-redis](https://apps.fedoraproject.org/packages/php-pecl-redis) package, from the [EPEL repository](https://fedoraproject.org/wiki/EPEL): ~~~ yum install php-pecl-redis ~~~ ### openSUSE ≥ 15.1 Installation of the [php7-redis](https://software.opensuse.org/package/php7-redis?search_term=php7-redis) package: ~~~ zypper in php7-redis ~~~ # Installation on OSX If the install fails on OSX, type the following commands in your shell before trying again: ~~~ MACOSX_DEPLOYMENT_TARGET=10.6 CFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp" CCFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" CXXFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" LDFLAGS="-arch i386 -arch x86_64 -bind_at_load" export CFLAGS CXXFLAGS LDFLAGS CCFLAGS MACOSX_DEPLOYMENT_TARGET ~~~ If that still fails and you are running Zend Server CE, try this right before "make": `./configure CFLAGS="-arch i386"`. Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tagged/phpredis). See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports). You can install it using MacPorts: - [Get macports-php](https://www.macports.org/) - `sudo port install php56-redis` (or php53-redis, php54-redis, php55-redis, php70-redis, php71-redis, php72-redis, php73-redis, php74-redis) # Building on Windows See [instructions from @char101](https://github.com/phpredis/phpredis/issues/213#issuecomment-11361242) on how to build phpredis on Windows. redis-6.0.2/arrays.md0000644000175000000120000002344314515245367015251 0ustar pyatsukhnenkowheelRedis Arrays ============ A Redis array is an isolated namespace in which keys are related in some manner. Keys are distributed across a number of Redis instances, using consistent hashing. A hash function is used to spread the keys across the array in order to keep a uniform distribution. **This feature was added as the result of a generous sponsorship by [A+E Networks](http://www.aetn.com/).** An array is composed of the following: * A list of Redis hosts. * A key extraction function, used to hash part of the key in order to distribute related keys on the same node (optional). This is set by the "function" option. * A list of nodes previously in the ring, only present after a node has been added or removed. When a read command is sent to the array (e.g. GET, LRANGE...), the key is first queryied in the main ring, and then in the secondary ring if it was not found in the main one. Optionally, the keys can be migrated automatically when this happens. Write commands will always go to the main ring. This is set by the "previous" option. * An optional index in the form of a Redis set per node, used to migrate keys when nodes are added or removed; set by the "index" option. * An option to rehash the array automatically as nodes are added or removed, set by the "autorehash" option. ## Creating an array There are several ways of creating Redis arrays; they can be pre-defined in redis.ini using `new RedisArray(string $name);`, or created dynamically using `new RedisArray(array $hosts, array $options);` #### Declaring a new array with a list of nodes
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"));
#### Declaring a new array with a list of nodes and a function to extract a part of the key
function extract_key_part($k) {
    return substr($k, 0, 3);	// hash only on first 3 characters.
}
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("function" => "extract_key_part"));
#### Defining a "previous" array when nodes are added or removed. When a new node is added to an array, phpredis needs to know about it. The old list of nodes becomes the “previous” array, and the new list of nodes is used as a main ring. Right after a node has been added, some read commands will point to the wrong nodes and will need to look up the keys in the previous ring.
// adding host3 to a ring containing host1 and host2. Read commands will look in the previous ring if the data is not found in the main ring.
$ra = new RedisArray(array("host1", "host2", "host3"), array("previous" => array("host1", "host2")));
#### Specifying the "retry_interval" parameter The retry_interval is used to specify a delay in milliseconds between reconnection attempts in case the client loses connection with a server
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_timeout" => 100));
#### Specifying the "lazy_connect" parameter This option is useful when a cluster has many shards but not of them are necessarily used at one time.
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("lazy_connect" => true));
#### Specifying the "connect_timeout" parameter The connect_timeout value is a double and is used to specify a timeout in number of seconds when creating redis socket connections used in the RedisArray.
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("connect_timeout" => 0.5));
#### Specifying the "read_timeout" parameter The read_timeout value is a double and is used to specify a timeout in number of seconds when waiting response from the server.
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("read_timeout" => 0.5));
#### Specifying the "algorithm" parameter The algorithm value is a string and is used to specify the name of key hashing algorithm. The list of possible values may be found using PHP function `hash_algos`. If algorithm is not supported by PHP `hash` function default algorithm will be used (CRC32 with 0xffffffff initial value).
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("algorithm" => "md5"));
#### Specifying the "consistent" parameter The value is boolean. When enabled RedisArray uses "ketama" distribution algorithm (currently without ability to set weight to each server). This option applies to main and previous ring if specified.
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("consistent" => true));
#### Specifying the "auth" parameter The value is string and used to specify the password for authenticate with the server prior to sending commands
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("auth" => "mysecretpassword"));
#### Defining arrays in Redis.ini Because php.ini parameters must be pre-defined, Redis Arrays must all share the same .ini settings.
// list available Redis Arrays
ini_set('redis.array.names', 'users,friends');

// set host names for each array.
ini_set('redis.arrays.hosts', 'users[]=localhost:6379&users[]=localhost:6380&users[]=localhost:6381&users[]=localhost:6382&friends[]=localhost');

// set functions
ini_set('redis.arrays.functions', 'users=user_hash');

// use index only for users
ini_set('redis.arrays.index', 'users=1,friends=0');

// use password for authentication
ini_set('redis.arrays.auth', 'users=mysecretpassword')
## Usage Redis arrays can be used just as Redis objects:
$ra = new RedisArray("users");
$ra->set("user1:name", "Joe");
$ra->set("user2:name", "Mike");
## Key hashing By default and in order to be compatible with other libraries, phpredis will try to find a substring enclosed in curly braces within the key name, and use it to distribute the data. For instance, the keys “{user:1}:name” and “{user:1}:email” will be stored on the same server as only “user:1” will be hashed. You can provide a custom function name in your redis array with the "function" option; this function will be called every time a key needs to be hashed. It should take a string and return a string. ## Custom key distribution function In order to control the distribution of keys by hand, you can provide a custom function or closure that returns the server number, which is the index in the array of servers that you created the RedisArray object with. For instance, instantiate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. ### Example
$ra = new RedisArray(array("host1", "host2", "host3", "host4", "host5", "host6", "host7", "host8"), array("distributor" => array(2, 2)));
This declares that we started with 2 shards and moved to 4 then 8 shards. The number of initial shards is 2 and the resharding level (or number of iterations) is 2. ## Migrating keys When a node is added or removed from a ring, RedisArray instances must be instantiated with a “previous” list of nodes. A single call to `$ra->_rehash()` causes all the keys to be redistributed according to the new list of nodes. Passing a callback function to `_rehash()` makes it possible to track the progress of that operation: the function is called with a node name and a number of keys that will be examined, e.g. `_rehash(function ($host, $count){ ... });`. It is possible to automate this process, by setting `'autorehash' => TRUE` in the constructor options. This will cause keys to be migrated when they need to be read from the previous array. In order to migrate keys, they must all be examined and rehashed. If the "index" option was set, a single key per node lists all keys present there. Otherwise, the `KEYS` command is used to list them. If a “previous” list of servers is provided, it will be used as a backup ring when keys can not be found in the current ring. Writes will always go to the new ring, whilst reads will go to the new ring first, and to the second ring as a backup. Adding and/or removing several instances is supported. ### Example
$ra = new RedisArray("users"); // load up a new config from redis.ini, using the “.previous” listing.
$ra->_rehash();
Running this code will: * Create a new ring with the updated list of nodes. * Server by server, look up all the keys in the previous list of nodes. * Rehash each key and possibly move it to another server. * Update the array object with the new list of nodes. ## Multi/exec Multi/exec is still available, but must be run on a single node:
$host = $ra->_target("{users}:user1:name");	// find host first
$ra->multi($host)	// then run transaction on that host.
   ->del("{users}:user1:name")
   ->srem("{users}:index", "user1")
   ->exec();
## Limitations Key arrays offer no guarantee when using Redis commands that span multiple keys. Except for the use of MGET, MSET, and DEL, a single connection will be used and all the keys read or written there. Running KEYS() on a RedisArray object will execute the command on each node and return an associative array of keys, indexed by host name. ## Array info RedisArray objects provide several methods to help understand the state of the cluster. These methods start with an underscore. * `$ra->_hosts()` → returns a list of hosts for the selected array. * `$ra->_function()` → returns the name of the function used to extract key parts during consistent hashing. * `$ra->_target($key)` → returns the host to be used for a certain key. * `$ra->_instance($host)` → returns a redis instance connected to a specific node; use with `_target` to get a single Redis object. * `$ra->_continuum()` → returns a list of points on continuum; may be useful with custom distributor function. ## Running the unit tests
$ cd tests
$ ./mkring.sh start
$ php array-tests.php
redis-6.0.2/cluster.md0000644000175000000120000002737014515245367015434 0ustar pyatsukhnenkowheelRedis Cluster ============= Redis introduces cluster support as of version 3.0.0, and to communicate with a cluster using phpredis one needs to use the RedisCluster class. For the majority of operations the RedisCluster class can act as a drop-in replacement for the Redis class without needing to modify how it's called. **This feature was added as the result of a generous sponsorship by [Tradesy](https://www.tradesy.com/)** ## Creating and connecting to a cluster To maintain consistency with the RedisArray class, one can create and connect to a cluster either by passing it one or more 'seed' nodes, or by defining these in redis.ini as a 'named' cluster. #### Declaring a cluster with an array of seeds ```php // Create a cluster setting three nodes as seeds $obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003')); // Connect and specify timeout and read_timeout $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5); // Connect with read/write timeout as well as specify that phpredis should use // persistent connections to each node. $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true); // Connect with cluster using password. $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, "password"); // Connect with cluster using SSL/TLS // last argument is an array with [SSL context](https://www.php.net/manual/en/context.ssl.php) options $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, NULL, Array("verify_peer" => false)); ``` #### Loading a cluster configuration by name In order to load a named array, one must first define the seed nodes in redis.ini. The following lines would define the cluster 'mycluster', and be loaded automatically by phpredis. ```ini # In redis.ini redis.clusters.seeds = "mycluster[]=localhost:7000&test[]=localhost:7001" redis.clusters.timeout = "mycluster=5" redis.clusters.read_timeout = "mycluster=10" redis.clusters.auth = "mycluster=password" ``` Then, this cluster can be loaded by doing the following ```php $obj_cluster = new RedisCluster('mycluster'); ``` ## Connection process On construction, the RedisCluster class will iterate over the provided seed nodes until it can attain a connection to the cluster and run CLUSTER SLOTS to map every node in the cluster locally. Once the keyspace is mapped, RedisCluster will only connect to nodes when it needs to (e.g. you're getting a key that we believe is on that node.) ## Slot caching Each time the `RedisCluster` class is constructed from scratch, phpredis needs to execute a `CLUSTER SLOTS` command to map the keyspace. Although this isn't an expensive command, it does require a round trip for each newly created object, which is inefficient. Starting from PhpRedis 5.0.0 these slots can be cached by setting `redis.clusters.cache_slots = 1` in `php.ini`. ## Timeouts Because Redis cluster is intended to provide high availability, timeouts do not work in the same way they do in normal socket communication. It's fully possible to have a timeout or even exception on a given socket (say in the case that a master node has failed), and continue to serve the request if and when a slave can be promoted as the new master. The way RedisCluster handles user specified timeout values is that every time a command is sent to the cluster, we record the time at the start of the request and then again every time we have to re-issue the command to a different node (either because Redis cluster responded with MOVED/ASK or because we failed to communicate with a given node). Once we detect having been in the command loop for longer than our specified timeout, an error is raised. ## Keyspace map As previously described, RedisCluster makes an initial mapping of every master (and any slaves) on construction, which it uses to determine which nodes to direct a given command. However, one of the core functionalities of Redis cluster is that this keyspace can change while the cluster is running. Because of this, the RedisCluster class will update its keyspace mapping whenever it receives a MOVED error when requesting data. In the case that we receive ASK redirection, it follows the Redis specification and requests the key from the ASK node, prefixed with an ASKING command. ## Automatic slave failover / distribution By default, RedisCluster will only ever send commands to master nodes, but can be configured differently for readonly commands if requested. ```php // The default option, only send commands to master nodes $obj_cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_NONE); // In the event we can't reach a master, and it has slaves, failover for read commands $obj_cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_ERROR); // Always distribute readonly commands between masters and slaves, at random $obj_cluster->setOption( RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE ); // Always distribute readonly commands to the slaves, at random $obj_cluster->setOption( RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE_SLAVES ); ``` ## Main command loop With the exception of commands that are directed to a specific node, each command executed via RedisCluster is processed through a command loop, where we make the request, handle any MOVED or ASK redirection, and repeat if necessary. This continues until one of the following conditions is met: 1. We fail to communicate with *any* node that we are aware of, in which case a `RedisClusterException` is raised. 2. We have been bounced around longer than the timeout which was set on construction. 3. Redis cluster returns to us a `CLUSTERDOWN` error, in which case a `RedisClusterException` is raised. 4. We receive a valid response, in which case the data is returned to the caller. ## Transactions The RedisCluster class fully supports MULTI ... EXEC transactions, including commands such as MGET and MSET which operate on multiple keys. There are considerations that must be taken into account here however. When you call `RedisCluster->multi()`, the cluster is put into a MULTI state, but the MULTI command is not delivered to any nodes until a key is requested on that node. In addition, calls to EXEC will always return an array (even in the event that a transaction to a given node failed), as the commands can be going to any number of nodes depending on what is called. Consider the following example: ```php // Cluster is put into MULTI state locally $obj_cluster->multi(); // The cluster will issue MULTI on this node first (and only once) $obj_cluster->get("mykey"); $obj_cluster->set("mykey", "new_value"); // If 'myotherkey' maps to a different node, MULTI will be issued there // before requesting the key $obj_cluster->get("myotherkey"); // This will always return an array, even in the event of a failed transaction // on one of the nodes, in which case that element will be FALSE print_r($obj_cluster->exec()); ``` ## Pipelining The RedisCluster class does not support pipelining as there is no way to detect whether the keys still live where our map indicates that they do and would therefore be inherently unsafe. It would be possible to implement this support as an option if there is demand for such a feature. ## Multiple key commands Redis cluster does allow commands that operate on multiple keys, but only if all of those keys hash to the same slot. Note that it is not enough that the keys are all on the same node, but must actually hash to the exact same hash slot. For all of these multiple key commands (with the exception of MGET and MSET), the RedisCluster class will verify each key maps to the same hash slot and raise a "CROSSSLOT" warning, returning false if they don't. ### MGET, MSET, DEL, and UNLINK RedisCluster has specialized processing for MGET, MSET, DEL, and UNLINK which allows you to send any number of keys (hashing to whichever slots) without having to consider where they live. The way this works, is that the RedisCluster class will split the command as it iterates through keys, delivering a subset of commands per each key's slot. *Note: If you send keys that hash to more than one slot, these commands are no longer atomic.* ```php // This will send two `MGET` commands. One for `{hash1}` keys, and one for `otherkey` $obj_cluster->mget(["{hash1}key1","{hash1}key2","{hash1}key3","otherkey"]); ``` This operation can also be done in MULTI mode transparently. ## Directed node commands There are a variety of commands which have to be directed at a specific node. In the case of these commands, the caller can either pass a key (which will be hashed and used to direct our command), or an array with host:port. ```php // This will be directed at the slot/node which would store "mykey" $obj_cluster->echo("mykey","Hello World!"); // Here we're iterating all of our known masters, and delivering the command there foreach ($obj_cluster->_masters() as $arr_master) { $obj_cluster->echo($arr_master, "Hello: " . implode(':', $arr_master)); } ``` In the case of all commands which need to be directed at a node, the calling convention is identical to the Redis call, except that they require an additional (first) argument in order to deliver the command. Following is a list of each of these commands: 1. ACL 1. BGREWRITEAOF 1. BGSAVE 1. CLIENT 1. CLUSTER 1. CONFIG 1. DBSIZE 1. ECHO 1. FLUSHALL 1. FLUSHDB 1. INFO 1. LASTSAVE 1. PING 1. PUBSUB 1. RANDOMKEY 1. RAWCOMMAND 1. ROLE 1. SAVE 1. SCAN 1. SCRIPT 1. SLOWLOG 1. TIME ## Session Handler You can use the cluster functionality of phpredis to store PHP session information in a Redis cluster as you can with a non cluster-enabled Redis instance. To do this, you must configure your `session.save_handler` and `session.save_path` INI variables to give phpredis enough information to communicate with the cluster. ```ini session.save_handler = rediscluster session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1&auth=password&stream[verify_peer]=0" ``` ### session.session_handler Set this variable to "rediscluster" to inform phpredis that this is a cluster instance. ### session.save_path The save path for cluster based session storage takes the form of a PHP GET request, and requires that you specify at least one `seed` node. Other options you can specify are as follows: * _timeout (double)_: The amount of time phpredis will wait when connecting or writing to the cluster. * _read\_timeout (double)_: The amount of time phpredis will wait for a result from the cluster. * _persistent_: Tells phpredis whether persistent connections should be used. * _failover (string)_: How phpredis should distribute session reads between master and slave nodes. * _none_ : phpredis will only communicate with master nodes * _error_: phpredis will communicate with master nodes unless one fails, in which case an attempt will be made to read session information from a slave. * _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing). * _auth (string, empty by default)_: The password used to authenticate with the server prior to sending commands. * _stream (array)_: ssl/tls stream context options. ### redis.session.early_refresh Under normal operation, the client will refresh the session's expiry ttl whenever the session is closed. However, we can save this additional round-trip by updating the ttl when the session is opened instead ( This means that sessions that have not been modified will not send further commands to the server ). To enable, set the following INI variable: ```ini redis.session.early_refresh = 1 ``` Note: This is disabled by default since it may significantly reduce the session lifetime for long-running scripts. Redis server version 6.2+ required. redis-6.0.2/sentinel.md0000644000175000000120000001707614515245367015576 0ustar pyatsukhnenkowheelRedis Sentinel ============== Redis Sentinel provides high availability for Redis. In practical terms this means that using Sentinel you can create a Redis deployment that resists without human intervention certain kinds of failures. Redis Sentinel also provides other collateral tasks such as monitoring, notifications and acts as a configuration provider for clients. ## Class RedisSentinel ----- ##### *Parameters* *host*: String, IP address or hostname *port*: Int (optional, default is 26379) *timeout*: Float, value in seconds (optional, default is 0 meaning unlimited) *persistent*: String, persistent connection id (optional, default is NULL meaning not persistent) *retry_interval*: Int, value in milliseconds (optional, default is 0) *read_timeout*: Float, value in seconds (optional, default is 0 meaning unlimited) *auth*:String, or an Array with one or two elements, used to authenticate with the redis-sentinel. (optional, default is NULL meaning NOAUTH) ##### *Examples for version 6.0 or later* ~~~php $sentinel = new RedisSentinel([ 'host' => '127.0.0.1', ]); // default parameters $sentinel = new RedisSentinel([ 'host' => '127.0.0.1', 'port' => 26379, 'connectTimeout' => 2.5, ]); // 2.5 sec timeout. $sentinel = new RedisSentinel([ 'host' => '127.0.0.1', 'port' => 26379, 'connectTimeout' => 2.5, 'persistent' => 'sentinel', ]); // persistent connection with id 'sentinel' $sentinel = new RedisSentinel([ 'host' => '127.0.0.1', 'port' => 26379, 'connectTimeout' => 2.5, 'persistent' => '', ]); // also persistent connection with id '' $sentinel = new RedisSentinel([ 'host' => '127.0.0.1', 'port' => 26379, 'connectTimeout' => 1, 'persistent' => null, 'retryInterval' => 100, ]); // 1 sec timeout, 100ms delay between reconnection attempts. $sentinel = new RedisSentinel([ 'host' => '127.0.0.1', 'port' => 26379, 'connectTimeout' => 0, 'persistent' => null, 'retryInterval' => 0, 'readTimeout' => 0, 'auth' => 'secret', ]); // connect sentinel with password authentication ~~~ ##### *Examples for versions older than 6.0* ~~~php $sentinel = new RedisSentinel('127.0.0.1'); // default parameters $sentinel = new RedisSentinel('127.0.0.1', 26379, 2.5); // 2.5 sec timeout. $sentinel = new RedisSentinel('127.0.0.1', 26379, 0, 'sentinel'); // persistent connection with id 'sentinel' $sentinel = new RedisSentinel('127.0.0.1', 26379, 0, ''); // also persistent connection with id '' $sentinel = new RedisSentinel('127.0.0.1', 26379, 1, null, 100); // 1 sec timeout, 100ms delay between reconnection attempts. $sentinel = new RedisSentinel('127.0.0.1', 26379, 0, NULL, 0, 0, "secret"); // connect sentinel with password authentication ~~~ ### Usage ----- * [ckquorum](#ckquorum) - Check if the current Sentinel configuration is able to reach the quorum needed to failover. * [failover](#failover) - Force a failover as if the master was not reachable. * [flushconfig](#flushconfig) - Force Sentinel to rewrite its configuration on disk. * [getMasterAddrByName](#getMasterAddrByName) - Return the ip and port number of the master with that name. * [master](#master) - Return the state and info of the specified master. * [masters](#masters) - Return a list of monitored masters and their state. * [ping](#ping) - Ping the sentinel. * [reset](#reset) - Reset all the masters with matching name. * [sentinels](#sentinels) - Return a list of sentinel instances for this master, and their state. * [slaves](#slaves) - Return a list of replicas for this master, and their state. ----- ### ckquorum ----- _**Description**_: Check if the current Sentinel configuration is able to reach the quorum needed to failover a master, and the majority needed to authorize the failover. This command should be used in monitoring systems to check if a Sentinel deployment is ok. ##### *Parameters* *String*: master name ##### *Return value* *Bool*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->ckquorum('mymaster'); ~~~ ### failover ----- _**Description**_: Force a failover as if the master was not reachable, and without asking for agreement to other Sentinels (however a new version of the configuration will be published so that the other Sentinels will update their configurations). ##### *Parameters* *String*: master name ##### *Return value* *Bool*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->failover('mymaster'); ~~~ ### flushconfig ----- _**Description**_: Force Sentinel to rewrite its configuration on disk, including the current Sentinel state. Normally Sentinel rewrites the configuration every time something changes in its state (in the context of the subset of the state which is persisted on disk across restart). However sometimes it is possible that the configuration file is lost because of operation errors, disk failures, package upgrade scripts or configuration managers. In those cases a way to to force Sentinel to rewrite the configuration file is handy. This command works even if the previous configuration file is completely missing. ##### *Parameters* (none) ##### *Return value* *Bool*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->flushconfig(); ~~~ ### getMasterAddrByName ----- _**Description**_: Return the ip and port number of the master with that name. If a failover is in progress or terminated successfully for this master it returns the address and port of the promoted replica. ##### *Parameters* *String*: master name ##### *Return value* *Array*, *Bool*: ['address', 'port'] in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->getMasterAddrByName('mymaster'); ~~~ ### master ----- _**Description**_: Return the state and info of the specified master. ##### *Parameters* *String*: master name ##### *Return value* *Array*, *Bool*: Associative array with info in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->master('mymaster'); ~~~ ### masters ----- _**Description**_: Return a list of monitored masters and their state. ##### *Parameters* (none) ##### *Return value* *Array*, *Bool*: List of arrays with info for each master in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->masters(); ~~~ ### ping ----- _**Description**_: Ping the sentinel. ##### *Parameters* (none) ##### *Return value* *Bool*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->ping(); ~~~ ### reset ----- _**Description**_: This command will reset all the masters with matching name. The pattern argument is a glob-style pattern. The reset process clears any previous state in a master (including a failover in progress), and removes every replica and sentinel already discovered and associated with the master. ##### *Parameters* *String*: pattern ##### *Return value* *Bool*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->reset('*'); ~~~ ### sentinels ----- _**Description**_: Return a list of sentinel instances for this master, and their state. ##### *Parameters* *String*: master name ##### *Return value* *Array*, *Bool*: List of arrays with info for each sentinels in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->sentinels('mymaster'); ~~~ ### slaves ----- _**Description**_: Return a list of replicas for this master, and their state. ##### *Parameters* *String*: master name ##### *Return value* *Array*, *Bool*: List of arrays with info for each replicas in case of success, `FALSE` in case of failure. ##### *Example* ~~~php $sentinel->slaves('mymaster'); ~~~ redis-6.0.2/backoff.c0000644000175000000120000000571114515245367015163 0ustar pyatsukhnenkowheel#include "common.h" #include #if PHP_VERSION_ID >= 70100 #include #else static zend_long php_mt_rand_range(zend_long min, zend_long max) { zend_long number = php_rand(); RAND_RANGE(number, min, max, PHP_RAND_MAX); return number; } #endif #include "backoff.h" static zend_ulong random_range(zend_ulong min, zend_ulong max) { if (max < min) { return php_mt_rand_range(max, min); } return php_mt_rand_range(min, max); } static zend_ulong redis_default_backoff(struct RedisBackoff *self, unsigned int retry_index) { zend_ulong backoff = retry_index ? self->base : random_range(0, self->base); return MIN(self->cap, backoff); } static zend_ulong redis_constant_backoff(struct RedisBackoff *self, unsigned int retry_index) { zend_ulong backoff = self->base; return MIN(self->cap, backoff); } static zend_ulong redis_uniform_backoff(struct RedisBackoff *self, unsigned int retry_index) { zend_ulong backoff = random_range(0, self->base); return MIN(self->cap, backoff); } static zend_ulong redis_exponential_backoff(struct RedisBackoff *self, unsigned int retry_index) { zend_ulong pow = MIN(retry_index, 10); zend_ulong backoff = self->base * (1 << pow); return MIN(self->cap, backoff); } static zend_ulong redis_full_jitter_backoff(struct RedisBackoff *self, unsigned int retry_index) { zend_ulong pow = MIN(retry_index, 10); zend_ulong backoff = self->base * (1 << pow); zend_ulong cap = MIN(self->cap, backoff); return random_range(0, cap); } static zend_ulong redis_equal_jitter_backoff(struct RedisBackoff *self, unsigned int retry_index) { zend_ulong pow = MIN(retry_index, 10); zend_ulong backoff = self->base * (1 << pow); zend_ulong temp = MIN(self->cap, backoff); return temp / 2 + random_range(0, temp) / 2; } static zend_ulong redis_decorrelated_jitter_backoff(struct RedisBackoff *self, unsigned int retry_index) { self->previous_backoff = random_range(self->base, self->previous_backoff * 3); return MIN(self->cap, self->previous_backoff); } typedef zend_ulong (*redis_backoff_algorithm)(struct RedisBackoff *self, unsigned int retry_index); static redis_backoff_algorithm redis_backoff_algorithms[REDIS_BACKOFF_ALGORITHMS] = { redis_default_backoff, redis_decorrelated_jitter_backoff, redis_full_jitter_backoff, redis_equal_jitter_backoff, redis_exponential_backoff, redis_uniform_backoff, redis_constant_backoff, }; void redis_initialize_backoff(struct RedisBackoff *self, unsigned long retry_interval) { self->algorithm = 0; // default backoff self->base = retry_interval; self->cap = retry_interval; self->previous_backoff = 0; } void redis_backoff_reset(struct RedisBackoff *self) { self->previous_backoff = 0; } zend_ulong redis_backoff_compute(struct RedisBackoff *self, unsigned int retry_index) { return redis_backoff_algorithms[self->algorithm](self, retry_index); } redis-6.0.2/backoff.h0000644000175000000120000000123114515245367015161 0ustar pyatsukhnenkowheel#ifndef REDIS_BACKOFF_H #define REDIS_BACKOFF_H /* {{{ struct RedisBackoff */ struct RedisBackoff { unsigned int algorithm; /* index of algorithm function, returns backoff in microseconds*/ zend_ulong base; /* base backoff in microseconds */ zend_ulong cap; /* max backoff in microseconds */ zend_ulong previous_backoff; /* previous backoff in microseconds */ }; /* }}} */ void redis_initialize_backoff(struct RedisBackoff *self, unsigned long retry_interval); void redis_backoff_reset(struct RedisBackoff *self); zend_ulong redis_backoff_compute(struct RedisBackoff *self, unsigned int retry_index); #endif redis-6.0.2/cluster_library.c0000644000175000000120000027403514515245367017004 0ustar pyatsukhnenkowheel#include "php_redis.h" #include "common.h" #include "library.h" #include "redis_commands.h" #include "cluster_library.h" #include "crc16.h" #include extern zend_class_entry *redis_cluster_exception_ce; int le_cluster_slot_cache; /* Debugging methods/ static void cluster_dump_nodes(redisCluster *c) { redisClusterNode *p; ZEND_HASH_FOREACH_PTR(c->nodes, p) { if (p == NULL) { continue; } const char *slave = (p->slave) ? "slave" : "master"; php_printf("%d %s %d %d", p->sock->port, slave,p->sock->prefix_len, p->slot); php_printf("\n"); } ZEND_HASH_FOREACH_END(); } static void cluster_log(char *fmt, ...) { va_list args; char buffer[1024]; va_start(args, fmt); vsnprintf(buffer,sizeof(buffer),fmt,args); va_end(args); fprintf(stderr, "%s\n", buffer); } // Debug function to dump a clusterReply structure recursively static void dump_reply(clusterReply *reply, int indent) { smart_string buf = {0}; int i; switch(reply->type) { case TYPE_ERR: smart_string_appendl(&buf, "(error) ", sizeof("(error) ")-1); smart_string_appendl(&buf, reply->str, reply->len); break; case TYPE_LINE: smart_string_appendl(&buf, reply->str, reply->len); break; case TYPE_INT: smart_string_appendl(&buf, "(integer) ", sizeof("(integer) ")-1); smart_string_append_long(&buf, reply->integer); break; case TYPE_BULK: smart_string_appendl(&buf,"\"", 1); smart_string_appendl(&buf, reply->str, reply->len); smart_string_appendl(&buf, "\"", 1); break; case TYPE_MULTIBULK: if (reply->elements < 0) { smart_string_appendl(&buf, "(nil)", sizeof("(nil)")-1); } else { for (i = 0; i < reply->elements; i++) { dump_reply(reply->element[i], indent+2); } } break; default: break; } if (buf.len > 0) { for (i = 0; i < indent; i++) { php_printf(" "); } smart_string_0(&buf); php_printf("%s", buf.c); php_printf("\n"); efree(buf.c); } } */ /* Recursively free our reply object. If free_data is non-zero we'll also free * the payload data (strings) themselves. If not, we just free the structs */ void cluster_free_reply(clusterReply *reply, int free_data) { long long i; switch(reply->type) { case TYPE_ERR: case TYPE_LINE: case TYPE_BULK: if (free_data && reply->str) efree(reply->str); break; case TYPE_MULTIBULK: if (reply->element) { if (reply->elements > 0) { for (i = 0; i < reply->elements && reply->element[i]; i++) { cluster_free_reply(reply->element[i], free_data); } } efree(reply->element); } break; default: break; } efree(reply); } static int cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, clusterReply **element, int status_strings) { int i; size_t sz; clusterReply *r; long len; char buf[1024]; for (i = 0; i < elements; i++) { r = element[i] = ecalloc(1, sizeof(clusterReply)); // Bomb out, flag error condition on a communication failure if (redis_read_reply_type(sock, &r->type, &len) < 0) { return FAILURE; } /* Set our reply len */ r->len = len; switch(r->type) { case TYPE_ERR: case TYPE_LINE: if (redis_sock_gets(sock,buf,sizeof(buf),&sz) < 0) { return FAILURE; } r->len = (long long)sz; if (status_strings) r->str = estrndup(buf, r->len); break; case TYPE_INT: r->integer = len; break; case TYPE_BULK: if (r->len >= 0) { r->str = redis_sock_read_bulk_reply(sock,r->len); if (!r->str) { return FAILURE; } } break; case TYPE_MULTIBULK: r->elements = r->len; if (r->elements > 0) { r->element = ecalloc(r->len, sizeof(*r->element)); if (cluster_multibulk_resp_recursive(sock, r->elements, r->element, status_strings) < 0) { return FAILURE; } } break; default: return FAILURE; } } return SUCCESS; } /* Return the socket for a slot and slave index */ static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, zend_ulong slaveidx) { redisClusterNode *node; /* Return the master if we're not looking for a slave */ if (slaveidx == 0) { return SLOT_SOCK(c, slot); } /* Abort if we can't find this slave */ if (!SLOT_SLAVES(c, slot) || (node = zend_hash_index_find_ptr(SLOT_SLAVES(c,slot), slaveidx)) == NULL ) { return NULL; } /* Success, return the slave */ return node->sock; } /* Read the response from a cluster */ clusterReply *cluster_read_resp(redisCluster *c, int status_strings) { return cluster_read_sock_resp(c->cmd_sock, c->reply_type, status_strings ? c->line_reply : NULL, c->reply_len); } /* Read any sort of response from the socket, having already issued the * command and consumed the reply type and meta info (length) */ clusterReply* cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, char *line_reply, long long len) { clusterReply *r; r = ecalloc(1, sizeof(clusterReply)); r->type = type; switch(r->type) { case TYPE_INT: r->integer = len; break; case TYPE_LINE: if (line_reply) { r->str = estrndup(line_reply, len); r->len = len; } REDIS_FALLTHROUGH; case TYPE_ERR: return r; case TYPE_BULK: r->len = len; r->str = redis_sock_read_bulk_reply(redis_sock, len); if (r->len != -1 && !r->str) { cluster_free_reply(r, 1); return NULL; } break; case TYPE_MULTIBULK: r->elements = len; if (r->elements > 0) { r->element = ecalloc(len, sizeof(clusterReply*)); if (cluster_multibulk_resp_recursive(redis_sock, len, r->element, line_reply != NULL) < 0) { cluster_free_reply(r, 1); return NULL; } } break; default: cluster_free_reply(r, 1); return NULL; } // Success, return the reply return r; } /* * Helpers to send various 'control type commands to a specific node, e.g. * MULTI, ASKING, READONLY, READWRITE, etc */ /* Send a command to the specific socket and validate reply type */ static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len, REDIS_REPLY_TYPE type) { char buf[1024]; /* Connect to the socket if we aren't yet and send our command, validate the reply type, and consume the first line */ if (!CLUSTER_SEND_PAYLOAD(redis_sock,cmd,cmd_len) || !CLUSTER_VALIDATE_REPLY_TYPE(redis_sock, type) || !redis_sock_gets_raw(redis_sock, buf, sizeof(buf))) return -1; /* Success! */ return 0; } static int cluster_send_asking(RedisSock *redis_sock) { return cluster_send_direct(redis_sock, ZEND_STRL(RESP_ASKING_CMD), TYPE_LINE); } /* Send READONLY to a specific RedisSock unless it's already flagged as being * in READONLY mode. If we can send the command, we flag the socket as being * in that mode. */ static int cluster_send_readonly(RedisSock *redis_sock) { int ret; /* We don't have to do anything if we're already in readonly mode */ if (redis_sock->readonly) return 0; /* Return success if we can send it */ ret = cluster_send_direct(redis_sock, ZEND_STRL(RESP_READONLY_CMD), TYPE_LINE); /* Flag this socket as READONLY if our command worked */ redis_sock->readonly = !ret; /* Return the result of our send */ return ret; } /* Send MULTI to a specific ReidsSock */ static int cluster_send_multi(redisCluster *c, short slot) { if (cluster_send_direct(SLOT_SOCK(c,slot), ZEND_STRL(RESP_MULTI_CMD), TYPE_LINE) == 0) { c->flags->txBytes += sizeof(RESP_MULTI_CMD) - 1; c->cmd_sock->mode = MULTI; return 0; } return -1; } /* Send EXEC to a given slot. We can use the normal command processing mechanism * here because we know we'll only have sent MULTI to the master nodes. We can't * failover inside a transaction, as we don't know if the transaction will only * be readonly commands, or contain write commands as well */ PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot) { int retval; /* Send exec */ retval = cluster_send_slot(c, slot, ZEND_STRL(RESP_EXEC_CMD), TYPE_MULTIBULK); /* We'll either get a length corresponding to the number of commands sent to * this node, or -1 in the case of EXECABORT or WATCH failure. */ c->multi_len[slot] = c->reply_len > 0 ? 1 : -1; /* Return our retval */ return retval; } PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot) { if (cluster_send_direct(SLOT_SOCK(c,slot), ZEND_STRL(RESP_DISCARD_CMD), TYPE_LINE)) { return 0; } return -1; } /* * Cluster key distribution helpers. For a small handlful of commands, we want * to distribute them across 1-N nodes. These methods provide simple containers * for the purposes of splitting keys/values in this way * */ /* Free cluster distribution list inside a HashTable */ static void cluster_dist_free_ht(zval *p) { clusterDistList *dl = *(clusterDistList**)p; int i; for (i = 0; i < dl->len; i++) { if (dl->entry[i].key_free) efree(dl->entry[i].key); if (dl->entry[i].val_free) efree(dl->entry[i].val); } efree(dl->entry); efree(dl); } /* Spin up a HashTable that will contain distribution lists */ HashTable *cluster_dist_create(void) { HashTable *ret; ALLOC_HASHTABLE(ret); zend_hash_init(ret, 0, NULL, cluster_dist_free_ht, 0); return ret; } /* Free distribution list */ void cluster_dist_free(HashTable *ht) { zend_hash_destroy(ht); efree(ht); } /* Create a clusterDistList object */ static clusterDistList *cluster_dl_create(void) { clusterDistList *dl; dl = emalloc(sizeof(clusterDistList)); dl->entry = emalloc(CLUSTER_KEYDIST_ALLOC * sizeof(clusterKeyVal)); dl->size = CLUSTER_KEYDIST_ALLOC; dl->len = 0; return dl; } /* Add a key to a dist list, returning the keval entry */ static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, int key_len, int key_free) { // Reallocate if required if (dl->len == dl->size) { dl->entry = erealloc(dl->entry, sizeof(clusterKeyVal) * dl->size * 2); dl->size *= 2; } // Set key info dl->entry[dl->len].key = key; dl->entry[dl->len].key_len = key_len; dl->entry[dl->len].key_free = key_free; // NULL out any values dl->entry[dl->len].val = NULL; dl->entry[dl->len].val_len = 0; dl->entry[dl->len].val_free = 0; return &(dl->entry[dl->len++]); } /* Add a key, returning a pointer to the entry where passed for easy adding * of values to match this key */ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, size_t key_len, clusterKeyVal **kv) { int key_free; short slot; clusterDistList *dl; clusterKeyVal *retptr; // Prefix our key and hash it key_free = redis_key_prefix(c->flags, &key, &key_len); slot = cluster_hash_key(key, key_len); // We can't do this if we don't fully understand the keyspace if (c->master[slot] == NULL) { if (key_free) efree(key); return FAILURE; } // Look for this slot if ((dl = zend_hash_index_find_ptr(ht, (zend_ulong)slot)) == NULL) { dl = cluster_dl_create(); zend_hash_index_update_ptr(ht, (zend_ulong)slot, dl); } // Now actually add this key retptr = cluster_dl_add_key(dl, key, key_len, key_free); // Push our return pointer if requested if (kv) *kv = retptr; return SUCCESS; } /* Provided a clusterKeyVal, add a value */ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val ) { char *val; size_t val_len; int val_free; // Serialize our value val_free = redis_pack(c->flags, z_val, &val, &val_len); // Attach it to the provied keyval entry kv->val = val; kv->val_len = val_len; kv->val_free = val_free; } /* Free allocated memory for a clusterMultiCmd */ void cluster_multi_free(clusterMultiCmd *mc) { efree(mc->cmd.c); efree(mc->args.c); } /* Add an argument to a clusterMultiCmd */ void cluster_multi_add(clusterMultiCmd *mc, char *data, int data_len) { mc->argc++; redis_cmd_append_sstr(&(mc->args), data, data_len); } /* Finalize a clusterMutliCmd by constructing the whole thing */ void cluster_multi_fini(clusterMultiCmd *mc) { mc->cmd.len = 0; redis_cmd_init_sstr(&(mc->cmd), mc->argc, mc->kw, mc->kw_len); smart_string_appendl(&(mc->cmd), mc->args.c, mc->args.len); } /* Set our last error string encountered */ static void cluster_set_err(redisCluster *c, char *err, int err_len) { // Free our last error if (c->err != NULL) { zend_string_release(c->err); c->err = NULL; } if (err != NULL && err_len > 0) { c->err = zend_string_init(err, err_len, 0); if (err_len >= sizeof("CLUSTERDOWN") - 1 && !memcmp(err, "CLUSTERDOWN", sizeof("CLUSTERDOWN") - 1) ) { c->clusterdown = 1; } } } /* Destructor for slaves */ static void ht_free_slave(zval *data) { if (*(redisClusterNode**)data) { cluster_free_node(*(redisClusterNode**)data); } } /* Get the hash slot for a given key */ unsigned short cluster_hash_key(const char *key, int len) { int s, e; // Find first occurrence of {, if any for (s = 0; s < len; s++) { if (key[s]=='{') break; } // There is no '{', hash everything if (s == len) return crc16(key, len) & REDIS_CLUSTER_MOD; // Found it, look for a tailing '}' for (e =s + 1; e < len; e++) { if (key[e] == '}') break; } // Hash the whole key if we don't find a tailing } or if {} is empty if (e == len || e == s+1) return crc16(key, len) & REDIS_CLUSTER_MOD; // Hash just the bit between { and } return crc16((char*)key+s+1,e-s-1) & REDIS_CLUSTER_MOD; } unsigned short cluster_hash_key_zstr(zend_string *key) { return cluster_hash_key(ZSTR_VAL(key), ZSTR_LEN(key)); } /* Grab the current time in milliseconds */ long long mstime(void) { struct timeval tv; long long mst; gettimeofday(&tv, NULL); mst = ((long long)tv.tv_sec)*1000; mst += tv.tv_usec/1000; return mst; } /* Hash a key from a ZVAL */ unsigned short cluster_hash_key_zval(zval *z_key) { const char *kptr; char buf[255]; int klen; // Switch based on ZVAL type switch(Z_TYPE_P(z_key)) { case IS_STRING: kptr = Z_STRVAL_P(z_key); klen = Z_STRLEN_P(z_key); break; case IS_LONG: klen = snprintf(buf,sizeof(buf),ZEND_LONG_FMT,Z_LVAL_P(z_key)); kptr = (const char *)buf; break; case IS_DOUBLE: klen = snprintf(buf,sizeof(buf),"%f",Z_DVAL_P(z_key)); kptr = (const char *)buf; break; case IS_ARRAY: kptr = "Array"; klen = sizeof("Array")-1; break; case IS_OBJECT: kptr = "Object"; klen = sizeof("Object")-1; break; default: kptr = ""; klen = 0; } // Hash the string representation return cluster_hash_key(kptr, klen); } /* Fisher-Yates shuffle for integer array */ static void fyshuffle(int *array, size_t len) { int temp, n = len; size_t r; /* Randomize */ while (n > 1) { r = ((int)((double)n-- * (rand() / (RAND_MAX+1.0)))); temp = array[n]; array[n] = array[r]; array[r] = temp; }; } /* Execute a CLUSTER SLOTS command against the seed socket, and return the * reply or NULL on failure. */ clusterReply* cluster_get_slots(RedisSock *redis_sock) { clusterReply *r; REDIS_REPLY_TYPE type; long len; // Send the command to the socket and consume reply type if (redis_sock_write(redis_sock, RESP_CLUSTER_SLOTS_CMD, sizeof(RESP_CLUSTER_SLOTS_CMD)-1) < 0 || redis_read_reply_type(redis_sock, &type, &len) < 0) { return NULL; } // Consume the rest of our response if ((r = cluster_read_sock_resp(redis_sock, type, NULL, len)) == NULL || r->type != TYPE_MULTIBULK || r->elements < 1) { if (r) cluster_free_reply(r, 1); return NULL; } // Return our reply return r; } /* Create a cluster node */ static redisClusterNode* cluster_node_create(redisCluster *c, char *host, size_t host_len, unsigned short port, unsigned short slot, short slave) { redisClusterNode *node = emalloc(sizeof(redisClusterNode)); // It lives in at least this slot, flag slave status node->slot = slot; node->slave = slave; node->slaves = NULL; /* Initialize our list of slot ranges */ zend_llist_init(&node->slots, sizeof(redisSlotRange), NULL, 0); // Attach socket node->sock = redis_sock_create(host, host_len, port, c->flags->timeout, c->flags->read_timeout, c->flags->persistent, NULL, 0); /* Stream context */ node->sock->stream_ctx = c->flags->stream_ctx; redis_sock_set_auth(node->sock, c->flags->user, c->flags->pass); return node; } /* Attach a slave to a master */ PHP_REDIS_API int cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) { zend_ulong index; // Allocate our slaves hash table if we haven't yet if (!master->slaves) { ALLOC_HASHTABLE(master->slaves); zend_hash_init(master->slaves, 0, NULL, ht_free_slave, 0); index = 1; } else { index = master->slaves->nNextFreeElement; } return zend_hash_index_update_ptr(master->slaves, index, slave) != NULL; } /* Sanity check/validation for CLUSTER SLOTS command */ #define VALIDATE_SLOTS_OUTER(r) \ (r->elements >= 3 && r2->element[0]->type == TYPE_INT && \ r->element[1]->type == TYPE_INT) #define VALIDATE_SLOTS_INNER(r) \ (r->type == TYPE_MULTIBULK && r->elements >= 2 && \ r->element[0]->type == TYPE_BULK && r->element[1]->type == TYPE_INT) /* Use the output of CLUSTER SLOTS to map our nodes */ static int cluster_map_slots(redisCluster *c, clusterReply *r) { redisClusterNode *pnode, *master, *slave; redisSlotRange range; int i,j, hlen, klen; short low, high; clusterReply *r2, *r3; unsigned short port; char *host, key[1024]; zend_hash_clean(c->nodes); for (i = 0; i < r->elements; i++) { // Inner response r2 = r->element[i]; // Validate outer and master slot if (!VALIDATE_SLOTS_OUTER(r2) || !VALIDATE_SLOTS_INNER(r2->element[2])) { return -1; } // Master r3 = r2->element[2]; // Grab our slot range, as well as master host/port low = (unsigned short)r2->element[0]->integer; high = (unsigned short)r2->element[1]->integer; host = r3->element[0]->str; hlen = r3->element[0]->len; port = (unsigned short)r3->element[1]->integer; // If the node is new, create and add to nodes. Otherwise use it. klen = snprintf(key, sizeof(key), "%s:%d", host, port); if ((pnode = zend_hash_str_find_ptr(c->nodes, key, klen)) == NULL) { master = cluster_node_create(c, host, hlen, port, low, 0); zend_hash_str_update_ptr(c->nodes, key, klen, master); // Attach slaves first time we encounter a given master in order to avoid regitering the slaves multiple times for (j = 3; j< r2->elements; j++) { r3 = r2->element[j]; if (!VALIDATE_SLOTS_INNER(r3)) { return -1; } // Skip slaves where the host is "" if (r3->element[0]->len == 0) continue; // Attach this node to our slave slave = cluster_node_create(c, r3->element[0]->str, (int)r3->element[0]->len, (unsigned short)r3->element[1]->integer, low, 1); cluster_node_add_slave(master, slave); } } else { master = pnode; } // Attach this node to each slot in the range for (j = low; j<= high; j++) { c->master[j] = master; } /* Append to our list of slot ranges */ range.low = low; range.high = high; zend_llist_add_element(&master->slots, &range); } // Success return 0; } /* Free a redisClusterNode structure */ PHP_REDIS_API void cluster_free_node(redisClusterNode *node) { if (node->slaves) { zend_hash_destroy(node->slaves); efree(node->slaves); } zend_llist_destroy(&node->slots); redis_free_socket(node->sock); efree(node); } /* Get or create a redisClusterNode that corresponds to the asking redirection */ static redisClusterNode *cluster_get_asking_node(redisCluster *c) { redisClusterNode *pNode; char key[1024]; int key_len; /* Hashed by host:port */ key_len = snprintf(key, sizeof(key), "%s:%u", c->redir_host, c->redir_port); /* See if we've already attached to it */ if ((pNode = zend_hash_str_find_ptr(c->nodes, key, key_len)) != NULL) { return pNode; } /* This host:port is unknown to us, so add it */ pNode = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); /* Return the node */ return pNode; } /* Get or create a node at the host:port we were asked to check, and return the * redis_sock for it. */ static RedisSock *cluster_get_asking_sock(redisCluster *c) { return cluster_get_asking_node(c)->sock; } /* Our context seeds will be a hash table with RedisSock* pointers */ static void ht_free_seed(zval *data) { RedisSock *redis_sock = *(RedisSock**)data; if (redis_sock) redis_free_socket(redis_sock); } /* Free redisClusterNode objects we've stored */ static void ht_free_node(zval *data) { redisClusterNode *node = *(redisClusterNode**)data; cluster_free_node(node); } /* zend_llist of slot ranges -> persistent array */ static redisSlotRange *slot_range_list_clone(zend_llist *src, size_t *count) { redisSlotRange *dst, *range; size_t i = 0; *count = zend_llist_count(src); dst = pemalloc(*count * sizeof(*dst), 1); range = zend_llist_get_first(src); while (range) { memcpy(&dst[i++], range, sizeof(*range)); range = zend_llist_get_next(src); } return dst; } /* Construct a redisCluster object */ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, int failover, int persistent) { redisCluster *c; /* Actual our actual cluster structure */ c = ecalloc(1, sizeof(redisCluster)); /* Initialize flags and settings */ c->flags = ecalloc(1, sizeof(RedisSock)); c->flags->timeout = timeout; c->flags->read_timeout = read_timeout; c->flags->persistent = persistent; c->subscribed_slot = -1; c->clusterdown = 0; c->failover = failover; c->err = NULL; /* Set up our waitms based on timeout */ c->waitms = (long)(1000 * timeout); /* Allocate our seeds hash table */ ALLOC_HASHTABLE(c->seeds); zend_hash_init(c->seeds, 0, NULL, ht_free_seed, 0); /* Allocate our nodes HashTable */ ALLOC_HASHTABLE(c->nodes); zend_hash_init(c->nodes, 0, NULL, ht_free_node, 0); return c; } PHP_REDIS_API void cluster_free(redisCluster *c, int free_ctx) { /* Disconnect from each node we're connected to */ cluster_disconnect(c, 0); /* Free any allocated prefix */ if (c->flags->prefix) zend_string_release(c->flags->prefix); redis_sock_free_auth(c->flags); efree(c->flags); /* Call hash table destructors */ zend_hash_destroy(c->seeds); zend_hash_destroy(c->nodes); /* Free hash tables themselves */ efree(c->seeds); efree(c->nodes); /* Free any error we've got */ if (c->err) zend_string_release(c->err); if (c->cache_key) { /* Invalidate persistent cache if the cluster has changed */ if (c->redirections) { zend_hash_del(&EG(persistent_list), c->cache_key); } /* Release our hold on the cache key */ zend_string_release(c->cache_key); } /* Free structure itself */ if (free_ctx) efree(c); } /* Create a cluster slot cache structure */ PHP_REDIS_API redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes) { redisCachedCluster *cc; redisCachedMaster *cm; redisClusterNode *node, *slave; cc = pecalloc(1, sizeof(*cc), 1); cc->hash = zend_string_dup(hash, 1); /* Copy nodes */ cc->master = pecalloc(zend_hash_num_elements(nodes), sizeof(*cc->master), 1); ZEND_HASH_FOREACH_PTR(nodes, node) { /* Skip slaves */ if (node->slave) continue; cm = &cc->master[cc->count]; /* Duplicate host/port and clone slot ranges */ cm->host.addr = zend_string_dup(node->sock->host, 1); cm->host.port = node->sock->port; /* Copy over slot ranges */ cm->slot = slot_range_list_clone(&node->slots, &cm->slots); /* Attach any slave nodes we have. */ if (node->slaves) { /* Allocate memory for slaves */ cm->slave = pecalloc(zend_hash_num_elements(node->slaves), sizeof(*cm->slave), 1); /* Copy host/port information for each slave */ ZEND_HASH_FOREACH_PTR(node->slaves, slave) { cm->slave[cm->slaves].addr = zend_string_dup(slave->sock->host, 1); cm->slave[cm->slaves].port = slave->sock->port; cm->slaves++; } ZEND_HASH_FOREACH_END(); } cc->count++; } ZEND_HASH_FOREACH_END(); return cc; } static void cluster_free_cached_master(redisCachedMaster *cm) { size_t i; /* Free each slave entry */ for (i = 0; i < cm->slaves; i++) { zend_string_release(cm->slave[i].addr); } /* Free other elements */ zend_string_release(cm->host.addr); pefree(cm->slave, 1); pefree(cm->slot, 1); } static redisClusterNode* cached_master_clone(redisCluster *c, redisCachedMaster *cm) { redisClusterNode *node; size_t i; node = cluster_node_create(c, ZSTR_VAL(cm->host.addr), ZSTR_LEN(cm->host.addr), cm->host.port, cm->slot[0].low, 0); /* Now copy in our slot ranges */ for (i = 0; i < cm->slots; i++) { zend_llist_add_element(&node->slots, &cm->slot[i]); } return node; } /* Destroy a persistent cached cluster */ PHP_REDIS_API void cluster_cache_free(redisCachedCluster *rcc) { size_t i; /* Free masters */ for (i = 0; i < rcc->count; i++) { cluster_free_cached_master(&rcc->master[i]); } zend_string_release(rcc->hash); pefree(rcc->master, 1); pefree(rcc, 1); } /* Initialize cluster from cached slots */ PHP_REDIS_API void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) { RedisSock *sock; redisClusterNode *mnode, *slave; redisCachedMaster *cm; char key[HOST_NAME_MAX]; size_t keylen, i, j, s; int *map; /* Randomize seeds */ map = emalloc(sizeof(*map) * cc->count); for (i = 0; i < cc->count; i++) map[i] = i; fyshuffle(map, cc->count); /* Duplicate the hash key so we can invalidate when redirected */ c->cache_key = zend_string_copy(cc->hash); /* Iterate over masters */ for (i = 0; i < cc->count; i++) { /* Grab the next master */ cm = &cc->master[map[i]]; /* Hash our host and port */ keylen = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(cm->host.addr), cm->host.port); /* Create socket */ sock = redis_sock_create(ZSTR_VAL(cm->host.addr), ZSTR_LEN(cm->host.addr), cm->host.port, c->flags->timeout, c->flags->read_timeout, c->flags->persistent, NULL, 0); /* Stream context */ sock->stream_ctx = c->flags->stream_ctx; /* Add to seed nodes */ zend_hash_str_update_ptr(c->seeds, key, keylen, sock); /* Create master node */ mnode = cached_master_clone(c, cm); /* Add our master */ zend_hash_str_update_ptr(c->nodes, key, keylen, mnode); /* Attach any slaves */ for (s = 0; s < cm->slaves; s++) { zend_string *host = cm->slave[s].addr; slave = cluster_node_create(c, ZSTR_VAL(host), ZSTR_LEN(host), cm->slave[s].port, 0, 1); cluster_node_add_slave(mnode, slave); } /* Hook up direct slot access */ for (j = 0; j < cm->slots; j++) { for (s = cm->slot[j].low; s <= cm->slot[j].high; s++) { c->master[s] = mnode; } } } efree(map); } /* Initialize seeds. By the time we get here we've already validated our * seeds array and know we have a non-empty array of strings all in * host:port format. */ PHP_REDIS_API void cluster_init_seeds(redisCluster *c, zend_string **seeds, uint32_t nseeds) { RedisSock *sock; char *seed, *sep, key[1024]; int key_len, i, *map; /* Get a randomized order to hit our seeds */ map = ecalloc(nseeds, sizeof(*map)); for (i = 0; i < nseeds; i++) map[i] = i; fyshuffle(map, nseeds); for (i = 0; i < nseeds; i++) { seed = ZSTR_VAL(seeds[map[i]]); sep = strrchr(seed, ':'); ZEND_ASSERT(sep != NULL); // Allocate a structure for this seed sock = redis_sock_create(seed, sep - seed, atoi(sep + 1), c->flags->timeout, c->flags->read_timeout, c->flags->persistent, NULL, 0); /* Stream context */ sock->stream_ctx = c->flags->stream_ctx; /* Credentials */ redis_sock_set_auth(sock, c->flags->user, c->flags->pass); // Index this seed by host/port key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(sock->host), sock->port); // Add to our seed HashTable zend_hash_str_update_ptr(c->seeds, key, key_len, sock); } efree(map); } /* Initial mapping of our cluster keyspace */ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c) { RedisSock *seed; clusterReply *slots = NULL; int mapped = 0; // Iterate over seeds until we can get slots ZEND_HASH_FOREACH_PTR(c->seeds, seed) { // Attempt to connect to this seed node if (seed == NULL || redis_sock_server_open(seed) != SUCCESS) { continue; } // Parse out cluster nodes. Flag mapped if we are valid slots = cluster_get_slots(seed); if (slots) { mapped = !cluster_map_slots(c, slots); // Bin anything mapped, if we failed somewhere if (!mapped) { memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); } } redis_sock_disconnect(seed, 0, 1); if (mapped) break; } ZEND_HASH_FOREACH_END(); // Clean up slots reply if we got one if (slots) cluster_free_reply(slots, 1); // Throw an exception if we couldn't map if (!mapped) { CLUSTER_THROW_EXCEPTION("Couldn't map cluster keyspace using any provided seed", 0); return FAILURE; } return SUCCESS; } /* Parse the MOVED OR ASK redirection payload when we get such a response * and apply this information to our cluster. If we encounter a parse error * nothing in the cluster will be modified, and -1 is returned. */ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) { char *host, *port; /* The Redis Cluster specification suggests clients do not update * their slot mapping for an ASK redirection, only for MOVED */ if (moved) c->redirections++; /* Move past "MOVED" or "ASK */ msg += moved ? MOVED_LEN : ASK_LEN; /* Make sure we can find host */ if ((host = strchr(msg, ' ')) == NULL) return -1; *host++ = '\0'; /* Find port, searching right to left in case of IPv6 */ if ((port = strrchr(host, ':')) == NULL) return -1; *port++ = '\0'; // Success, apply it c->redir_type = moved ? REDIR_MOVED : REDIR_ASK; strncpy(c->redir_host, host, sizeof(c->redir_host) - 1); c->redir_host_len = port - host - 1; c->redir_slot = (unsigned short)atoi(msg); c->redir_port = (unsigned short)atoi(port); return 0; } /* Once we write a command to a node in our cluster, this function will check * the reply type and extract information from those that will specify a length * bit. If we encounter an error condition, we'll check for MOVED or ASK * redirection, parsing out slot host and port so the caller can take * appropriate action. * * In the case of a non MOVED/ASK error, we wlll set our cluster error * condition so GetLastError can be queried by the client. * * This function will return -1 on a critical error (e.g. parse/communication * error, 0 if no redirection was encountered, and 1 if the data was moved. */ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type) { size_t sz; // Clear out any prior error state and our last line response CLUSTER_CLEAR_ERROR(c); CLUSTER_CLEAR_REPLY(c); if (-1 == redis_check_eof(c->cmd_sock, 1, 1) || EOF == (*reply_type = redis_sock_getc(c->cmd_sock))) { return -1; } // In the event of an ERROR, check if it's a MOVED/ASK error if (*reply_type == TYPE_ERR) { char inbuf[4096]; size_t nbytes; int moved; // Attempt to read the error if (!redis_sock_get_line(c->cmd_sock, inbuf, sizeof(inbuf), &nbytes)) { return -1; } // Check for MOVED or ASK redirection if ((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { /* Make sure we can parse the redirection host and port */ return !cluster_set_redirection(c, inbuf, moved) ? 1 : -1; } // Capture the error string Redis returned cluster_set_err(c, inbuf, strlen(inbuf)-2); return 0; } // Fetch the first line of our response from Redis. if (redis_sock_gets(c->cmd_sock,c->line_reply,sizeof(c->line_reply), &sz) < 0) { return -1; } // For replies that will give us a numeric length, convert it if (*reply_type != TYPE_LINE) { c->reply_len = strtol(c->line_reply, NULL, 10); } else { c->reply_len = (long long)sz; } // Clear out any previous error, and return that the data is here CLUSTER_CLEAR_ERROR(c); return 0; } /* Disconnect from each node we're connected to */ PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force) { redisClusterNode *node, *slave; ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) continue; /* Disconnect from the master */ redis_sock_disconnect(node->sock, force, 1); /* We also want to disconnect any slave connections so they will be pooled * in the event we are using persistent connections and connection pooling. */ if (node->slaves) { ZEND_HASH_FOREACH_PTR(node->slaves, slave) { redis_sock_disconnect(slave->sock, force, 1); } ZEND_HASH_FOREACH_END(); } } ZEND_HASH_FOREACH_END(); } /* This method attempts to write our command at random to the master and any * attached slaves, until we either successufly do so, or fail. */ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, int nomaster) { int i, count = 1, *nodes; RedisSock *redis_sock; /* Determine our overall node count */ if (c->master[c->cmd_slot]->slaves) { count += zend_hash_num_elements(c->master[c->cmd_slot]->slaves); } /* Allocate memory for master + slaves or just slaves */ nodes = emalloc(sizeof(int)*count); /* Populate our array with the master and each of it's slaves, then * randomize them, so we will pick from the master or some slave. */ for (i = 0; i < count; i++) nodes[i] = i; fyshuffle(nodes, count); /* Iterate through our nodes until we find one we can write to or fail */ for (i = 0; i < count; i++) { /* Skip if this is the master node and we don't want to query that */ if (nomaster && nodes[i] == 0) continue; /* Get the slave for this index */ redis_sock = cluster_slot_sock(c, c->cmd_slot, nodes[i]); if (!redis_sock) continue; /* If we're not on the master, attempt to send the READONLY command to * this slave, and skip it if that fails */ if (nodes[i] == 0 || cluster_send_readonly(redis_sock) == 0) { /* Attempt to send the command */ if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz)) { c->cmd_sock = redis_sock; efree(nodes); return 0; } } } /* Clean up our shuffled array */ efree(nodes); /* Couldn't send to the master or any slave */ return -1; } /* Attempt to write our command to the current c->cmd_sock socket. For write * commands, we attempt to query the master for this slot, and in the event of * a failure, try to query every remaining node for a redirection. * * If we're issuing a readonly command, we use one of three strategies, depending * on our redisCluster->failover setting. * * REDIS_FAILOVER_NONE: * The command is treated just like a write command, and will only be executed * against the known master for this slot. * REDIS_FAILOVER_ERROR: * If we're unable to communicate with this slot's master, we attempt the query * against any slaves (at random) that this master has. * REDIS_FAILOVER_DISTRIBUTE: * We pick at random from the master and any slaves it has. This option will * load balance between masters and slaves * REDIS_FAILOVER_DISTRIBUTE_SLAVES: * We pick at random from slave nodes of a given master. This option is * used to load balance read queries against N slaves. * * Once we are able to find a node we can write to, we check for MOVED or * ASKING redirection, such that the keyspace can be updated. */ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, int direct) { redisClusterNode *seed_node; RedisSock *redis_sock; int failover, nomaster; /* First try the socket requested */ redis_sock = c->cmd_sock; /* Readonly is irrelevant if we're not configured to failover */ failover = c->readonly && c->failover != REDIS_FAILOVER_NONE ? c->failover : REDIS_FAILOVER_NONE; /* If in ASK redirection, get/create the node for that host:port, otherwise * just use the command socket. */ if (c->redir_type == REDIR_ASK) { if (cluster_send_asking(c->cmd_sock) < 0) { return -1; } } /* Attempt to send our command payload to the cluster. If we're not set up * to failover, just try the master. If we're configured to failover on * error, try the master and then fall back to any slaves. When we're set * up to distribute the commands, try to write to any node on this slot * at random. */ if (failover == REDIS_FAILOVER_NONE) { /* Success if we can send our payload to the master */ if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz)) return 0; } else if (failover == REDIS_FAILOVER_ERROR) { /* Try the master, then fall back to any slaves we may have */ if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz) || !cluster_dist_write(c, cmd, sz, 1)) return 0; } else { /* Include or exclude master node depending on failover option and * attempt to make our write */ nomaster = failover == REDIS_FAILOVER_DISTRIBUTE_SLAVES; if (!cluster_dist_write(c, cmd, sz, nomaster)) { /* We were able to write to a master or slave at random */ return 0; } } /* Don't fall back if direct communication with this slot is required. */ if (direct) return -1; /* Fall back by attempting the request against every known node */ ZEND_HASH_FOREACH_PTR(c->nodes, seed_node) { /* Skip this node if it's the one that failed, or if it's a slave */ if (seed_node == NULL || seed_node->sock == redis_sock || seed_node->slave) continue; /* Connect to this node if we haven't already and attempt to write our request to this node */ if (CLUSTER_SEND_PAYLOAD(seed_node->sock, cmd, sz)) { c->cmd_slot = seed_node->slot; c->cmd_sock = seed_node->sock; return 0; } } ZEND_HASH_FOREACH_END(); /* We were unable to write to any node in our cluster */ return -1; } /* Helper to find if we've got a host:port mapped in our cluster nodes. */ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, unsigned short port) { int key_len; char key[1024]; key_len = snprintf(key,sizeof(key),"%s:%d", host, port); return zend_hash_str_find_ptr(c->nodes, key, key_len); } /* Provided a redisCluster object, the slot where we thought data was and * the slot where data was moved, update our node mapping */ static void cluster_update_slot(redisCluster *c) { redisClusterNode *node; char key[1024]; size_t klen; /* Do we already have the new slot mapped */ if (c->master[c->redir_slot]) { /* No need to do anything if it's the same node */ if (!CLUSTER_REDIR_CMP(c, SLOT_SOCK(c,c->redir_slot))) { return; } /* Check to see if we have this new node mapped */ node = cluster_find_node(c, c->redir_host, c->redir_port); if (node) { /* Just point to this slot */ c->master[c->redir_slot] = node; } else { /* If the redirected node is a replica of the previous slot owner, a failover has taken place. We must then remap the cluster's keyspace in order to update the cluster's topology. */ redisClusterNode *prev_master = SLOT(c,c->redir_slot); redisClusterNode *slave; ZEND_HASH_FOREACH_PTR(prev_master->slaves, slave) { if (slave == NULL) { continue; } if (!CLUSTER_REDIR_CMP(c, slave->sock)) { // Detected a failover, the redirected node was a replica // Remap the cluster's keyspace cluster_map_keyspace(c); return; } } ZEND_HASH_FOREACH_END(); /* Create our node */ node = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); /* Our node is new, so keep track of it for cleanup */ klen = snprintf(key, sizeof(key), "%s:%d", c->redir_host, c->redir_port); zend_hash_str_update_ptr(c->nodes, key, klen, node); /* Now point our slot at the node */ c->master[c->redir_slot] = node; } } else { /* Check to see if the ip and port are mapped */ node = cluster_find_node(c, c->redir_host, c->redir_port); if (!node) { node = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); } /* Map the slot to this node */ c->master[c->redir_slot] = node; } /* Update slot inside of node, so it can be found for command sending */ node->slot = c->redir_slot; /* Make sure we unflag this node as a slave, as Redis Cluster will only ever * direct us to master nodes. */ node->slave = 0; } /* Abort any transaction in process, by sending DISCARD to any nodes that * have active transactions in progress. If we can't send DISCARD, we need * to disconnect as it would leave us in an undefined state. */ PHP_REDIS_API int cluster_abort_exec(redisCluster *c) { clusterFoldItem *fi = c->multi_head; /* Loop through our fold items */ while (fi) { if (SLOT_SOCK(c,fi->slot)->mode == MULTI) { if (cluster_send_discard(c, fi->slot) < 0) { cluster_disconnect(c, 0); return -1; } SLOT_SOCK(c,fi->slot)->mode = ATOMIC; SLOT_SOCK(c,fi->slot)->watching = 0; } fi = fi->next; } /* Update our overall cluster state */ c->flags->mode = ATOMIC; /* Success */ return 0; } /* Iterate through our slots, looking for the host/port in question. This * should perform well enough as in almost all situations, a few or a few * dozen servers will map all the slots */ PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, unsigned short port) { int i; for (i = 0; i < REDIS_CLUSTER_SLOTS; i++) { if (c->master[i] && c->master[i]->sock && c->master[i]->sock->port == port && !strcasecmp(ZSTR_VAL(c->master[i]->sock->host), host)) { return i; } } // We didn't find it return -1; } /* Send a command to a specific slot */ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype) { /* Point our cluster to this slot and it's socket */ c->cmd_slot = slot; c->cmd_sock = SLOT_SOCK(c, slot); /* Enable multi mode on this slot if we've been directed to but haven't * send it to this node yet */ if (c->flags->mode == MULTI && c->cmd_sock->mode != MULTI) { if (cluster_send_multi(c, slot) == -1) { CLUSTER_THROW_EXCEPTION("Unable to enter MULTI mode on requested slot", 0); return -1; } } /* Try the slot */ if (cluster_sock_write(c, cmd, cmd_len, 1) == -1) { return -1; } c->flags->txBytes += cmd_len; /* Check our response */ if (cluster_check_response(c, &c->reply_type) != 0 || (rtype != TYPE_EOF && rtype != c->reply_type)) return -1; /* Success */ return 0; } /* Send a command to given slot in our cluster. If we get a MOVED or ASK error * we attempt to send the command to the node as directed. */ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len) { int resp, timedout = 0; long msstart; if (!SLOT(c, slot)) { zend_throw_exception_ex(redis_cluster_exception_ce, 0, "The slot %d is not covered by any node in this cluster", slot); return -1; } /* Set the slot we're operating against as well as it's socket. These can * change during our request loop if we have a master failure and are * configured to fall back to slave nodes, or if we have to fall back to * a different slot due to no nodes serving this slot being reachable. */ c->cmd_slot = slot; c->cmd_sock = SLOT_SOCK(c, slot); /* Get the current time in milliseconds to handle any timeout */ msstart = mstime(); /* Our main cluster request/reply loop. This loop runs until we're able to * get a valid reply from a node, hit our "request" timeout, or encounter a * CLUSTERDOWN state from Redis Cluster. */ do { /* Send MULTI to the socket if we're in MULTI mode but haven't yet */ if (c->flags->mode == MULTI && c->cmd_sock->mode != MULTI) { /* We have to fail if we can't send MULTI to the node */ if (cluster_send_multi(c, slot) == -1) { CLUSTER_THROW_EXCEPTION("Unable to enter MULTI mode on requested slot", 0); return -1; } } /* Attempt to deliver our command to the node, and that failing, to any * node until we find one that is available. */ if (cluster_sock_write(c, cmd, cmd_len, 0) == -1) { /* We have to abort, as no nodes are reachable */ CLUSTER_THROW_EXCEPTION("Can't communicate with any node in the cluster", 0); return -1; } /* Check response and short-circuit on success or communication error */ resp = cluster_check_response(c, &c->reply_type); if (resp <= 0) { break; } /* Handle MOVED or ASKING redirection */ if (resp == 1) { /* Abort if we're in a transaction as it will be invalid */ if (c->flags->mode == MULTI) { CLUSTER_THROW_EXCEPTION("Can't process MULTI sequence when cluster is resharding", 0); return -1; } if (c->redir_type == REDIR_MOVED) { /* For MOVED redirection we want to update our cached mapping */ cluster_update_slot(c); c->cmd_sock = SLOT_SOCK(c, slot); } else if (c->redir_type == REDIR_ASK) { /* For ASK redirection we want to redirect but not update slot mapping */ c->cmd_sock = cluster_get_asking_sock(c); } } /* See if we've timed out in the command loop */ timedout = c->waitms ? mstime() - msstart >= c->waitms : 0; } while (!c->clusterdown && !timedout); // If we've detected the cluster is down, throw an exception if (c->clusterdown) { CLUSTER_THROW_EXCEPTION("The Redis Cluster is down (CLUSTERDOWN)", 0); return -1; } else if (timedout || resp == -1) { // Make sure the socket is reconnected, it such that it is in a clean state redis_sock_disconnect(c->cmd_sock, 1, 1); if (timedout) { CLUSTER_THROW_EXCEPTION("Timed out attempting to find data in the correct node!", 0); } else { CLUSTER_THROW_EXCEPTION("Error processing response from Redis node!", 0); } return -1; } /* Clear redirection flag */ c->redir_type = REDIR_NONE; // Success, return the slot where data exists. return 0; } /* RedisCluster response handlers. These methods all have the same prototype * and set the proper return value for the calling cluster method. These * methods will never be called in the case of a communication error when * we try to send the request to the Cluster *or* if a non MOVED or ASK * error is encountered, in which case our response processing macro will * short circuit and RETURN_FALSE, as the error will have already been * consumed. */ /* RAW bulk response handler */ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; // Make sure we can read the response if (c->reply_type != TYPE_BULK || (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) { if (c->flags->mode != MULTI) { RETURN_FALSE; } else { add_next_index_bool(&c->multi_resp, 0); return; } } // Return our response raw CLUSTER_RETURN_STRING(c, resp, c->reply_len); efree(resp); } PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *p; /* Cluster already has the reply so abort if this isn't a LINE response *or* if for * some freaky reason we don't detect a null terminator */ if (c->reply_type != TYPE_LINE || !(p = memchr(c->line_reply,'\0',sizeof(c->line_reply)))) { CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { CLUSTER_RETURN_STRING(c, c->line_reply, p - c->line_reply); } else { add_next_index_stringl(&c->multi_resp, c->line_reply, p - c->line_reply); } } /* BULK response handler */ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; // Make sure we can read the response if (c->reply_type != TYPE_BULK || (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) { CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { if (!redis_unpack(c->flags, resp, c->reply_len, return_value)) { CLUSTER_RETURN_STRING(c, resp, c->reply_len); } } else { zval z_unpacked; if (redis_unpack(c->flags, resp, c->reply_len, &z_unpacked)) { add_next_index_zval(&c->multi_resp, &z_unpacked); } else { add_next_index_stringl(&c->multi_resp, resp, c->reply_len); } } efree(resp); } /* Bulk response where we expect a double */ PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; double dbl; // Make sure we can read the response if (c->reply_type != TYPE_BULK || (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) { CLUSTER_RETURN_FALSE(c); } // Convert to double, free response dbl = atof(resp); efree(resp); CLUSTER_RETURN_DOUBLE(c, dbl); } /* A boolean response. If we get here, we've consumed the '+' reply * type and will now just verify we can read the OK */ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { // Check that we have +OK if (c->reply_type != TYPE_LINE || c->reply_len != 2 || c->line_reply[0] != 'O' || c->line_reply[1] != 'K') { CLUSTER_RETURN_FALSE(c); } CLUSTER_RETURN_BOOL(c, 1); } /* Boolean response, specialized for PING */ PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (c->reply_type != TYPE_LINE || c->reply_len != 4 || memcmp(c->line_reply,"PONG",sizeof("PONG")-1)) { CLUSTER_RETURN_FALSE(c); } CLUSTER_RETURN_BOOL(c, 1); } PHP_REDIS_API void cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (ctx == NULL) { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { return cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { ZEND_ASSERT(!"memory corruption?"); } } PHP_REDIS_API void cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (ctx == NULL) { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { ZEND_ASSERT(!"memory corruption?"); } } PHP_REDIS_API void cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval zret = {0}; c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null; if (redis_read_lpos_response(&zret, c->cmd_sock, c->reply_type, c->reply_len, ctx) < 0) { ZVAL_FALSE(&zret); } if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(&zret, 0, 1); } else { add_next_index_zval(&c->multi_resp, &zret); } } PHP_REDIS_API void cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval zret = {0}; c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null; if (c->reply_type != TYPE_MULTIBULK || redis_read_geosearch_response(&zret, c->cmd_sock, c->reply_len, ctx != NULL) < 0) { ZVAL_FALSE(&zret); } if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(&zret, 0, 1); } else { add_next_index_zval(&c->multi_resp, &zret); } } PHP_REDIS_API void cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (ctx == NULL) { cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { ZEND_ASSERT(!"memory corruption?"); } } PHP_REDIS_API void cluster_zadd_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); if (ctx == NULL) { cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { cluster_dbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } } PHP_REDIS_API void cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (ctx == NULL) { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { ZEND_ASSERT(!"memory corruption?"); } } PHP_REDIS_API void cluster_srandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (ctx == NULL) { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { ZEND_ASSERT(!"memory corruption?"); } } PHP_REDIS_API void cluster_object_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); if (ctx == PHPREDIS_CTX_PTR) { cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } } PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (ctx == NULL) { cluster_bool_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } } /* 1 or 0 response, for things like SETNX */ PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { // Validate our reply type, and check for a zero if (c->reply_type != TYPE_INT || c->reply_len == 0) { CLUSTER_RETURN_FALSE(c); } CLUSTER_RETURN_BOOL(c, 1); } /* Generic integer response */ PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (c->reply_type != TYPE_INT) { CLUSTER_RETURN_FALSE(c); } CLUSTER_RETURN_LONG(c, c->reply_len); } /* TYPE response handler */ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { // Make sure we got the right kind of response if (c->reply_type != TYPE_LINE) { CLUSTER_RETURN_FALSE(c); } // Switch on the type if (strncmp (c->line_reply, "string", 6) == 0) { CLUSTER_RETURN_LONG(c, REDIS_STRING); } else if (strncmp(c->line_reply, "set", 3) == 0) { CLUSTER_RETURN_LONG(c, REDIS_SET); } else if (strncmp(c->line_reply, "list", 4) == 0) { CLUSTER_RETURN_LONG(c, REDIS_LIST); } else if (strncmp(c->line_reply, "hash", 4) == 0) { CLUSTER_RETURN_LONG(c, REDIS_HASH); } else if (strncmp(c->line_reply, "zset", 4) == 0) { CLUSTER_RETURN_LONG(c, REDIS_ZSET); } else if (strncmp(c->line_reply, "stream", 6) == 0) { CLUSTER_RETURN_LONG(c, REDIS_STREAM); } else { CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND); } } /* SUBSCRIBE/PSCUBSCRIBE handler */ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; zval z_tab, *z_tmp; int pull = 0; // Consume each MULTI BULK response (one per channel/pattern) while (sctx->argc--) { if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw, &z_tab) ) { efree(sctx); RETURN_FALSE; } if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_tab), 0)) == NULL || strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) != 0 ) { zval_dtor(&z_tab); efree(sctx); RETURN_FALSE; } zval_dtor(&z_tab); pull = 1; } // Set up our callback pointers zval z_ret, z_args[4]; sctx->cb.fci.retval = &z_ret; sctx->cb.fci.params = z_args; /* We're in a subscribe loop */ c->subscribed_slot = c->cmd_slot; /* Multibulk response, {[pattern], type, channel, payload} */ while (1) { /* Arguments */ zval *z_type, *z_chan, *z_pat = NULL, *z_data; int tab_idx = 1, is_pmsg; // Get the next subscribe response if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, mbulk_resp_loop, &z_tab) || (z_type = zend_hash_index_find(Z_ARRVAL(z_tab), 0)) == NULL ) { break; } // Make sure we have a message or pmessage if (!strncmp(Z_STRVAL_P(z_type), "message", 7) || !strncmp(Z_STRVAL_P(z_type), "pmessage", 8) ) { is_pmsg = *Z_STRVAL_P(z_type) == 'p'; } else { zval_dtor(&z_tab); continue; } if (is_pmsg && (z_pat = zend_hash_index_find(Z_ARRVAL(z_tab), tab_idx++)) == NULL) { break; } // Extract channel and data if ((z_chan = zend_hash_index_find(Z_ARRVAL(z_tab), tab_idx++)) == NULL || (z_data = zend_hash_index_find(Z_ARRVAL(z_tab), tab_idx++)) == NULL ) { break; } // Always pass our object through z_args[0] = *getThis(); // Set up calbacks depending on type if (is_pmsg) { z_args[1] = *z_pat; z_args[2] = *z_chan; z_args[3] = *z_data; } else { z_args[1] = *z_chan; z_args[2] = *z_data; } // Set arg count sctx->cb.fci.param_count = tab_idx; // Execute our callback if (zend_call_function(&sctx->cb.fci, &sctx->cb.fci_cache) != SUCCESS) { break; } // If we have a return value, free it zval_ptr_dtor(&z_ret); zval_dtor(&z_tab); } // We're no longer subscribing, due to an error c->subscribed_slot = -1; // Cleanup zval_dtor(&z_tab); efree(sctx); // Failure RETURN_FALSE; } /* UNSUBSCRIBE/PUNSUBSCRIBE */ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; zval z_tab, *z_chan, *z_flag; int pull = 0, argc = sctx->argc; efree(sctx); array_init(return_value); // Consume each response while (argc--) { // Fail if we didn't get an array or can't find index 1 if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw, &z_tab) || (z_chan = zend_hash_index_find(Z_ARRVAL(z_tab), 1)) == NULL ) { zval_dtor(&z_tab); zval_dtor(return_value); RETURN_FALSE; } // Find the flag for this channel/pattern if ((z_flag = zend_hash_index_find(Z_ARRVAL(z_tab), 2)) == NULL || Z_STRLEN_P(z_flag) != 2 ) { zval_dtor(&z_tab); zval_dtor(return_value); RETURN_FALSE; } // Redis will give us either :1 or :0 here char *flag = Z_STRVAL_P(z_flag); // Add result add_assoc_bool(return_value, Z_STRVAL_P(z_chan), flag[1] == '1'); zval_dtor(&z_tab); pull = 1; } } /* Recursive MULTI BULK -> PHP style response handling */ static void cluster_mbulk_variant_resp(clusterReply *r, int null_mbulk_as_null, zval *z_ret) { zval z_sub_ele; long long i; switch(r->type) { case TYPE_INT: add_next_index_long(z_ret, r->integer); break; case TYPE_LINE: if (r->str) { add_next_index_stringl(z_ret, r->str, r->len); } else { add_next_index_bool(z_ret, 1); } break; case TYPE_BULK: if (r->len > -1) { add_next_index_stringl(z_ret, r->str, r->len); } else { add_next_index_null(z_ret); } break; case TYPE_MULTIBULK: if (r->elements < 0 && null_mbulk_as_null) { add_next_index_null(z_ret); } else { array_init(&z_sub_ele); for (i = 0; i < r->elements; i++) { cluster_mbulk_variant_resp(r->element[i], null_mbulk_as_null, &z_sub_ele); } add_next_index_zval(z_ret, &z_sub_ele); } break; default: add_next_index_bool(z_ret, 0); break; } } /* Variant response handling, for things like EVAL and various other responses * where we just map the replies from Redis type values to PHP ones directly. */ static void cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int status_strings, void *ctx) { clusterReply *r; zval zv, *z_arr = &zv; long long i; // Make sure we can read it if ((r = cluster_read_resp(c, status_strings)) == NULL) { CLUSTER_RETURN_FALSE(c); } // Handle ATOMIC vs. MULTI mode in a separate switch if (CLUSTER_IS_ATOMIC(c)) { switch(r->type) { case TYPE_INT: RETVAL_LONG(r->integer); break; case TYPE_ERR: RETVAL_FALSE; break; case TYPE_LINE: if (status_strings) { RETVAL_STRINGL(r->str, r->len); } else { RETVAL_TRUE; } break; case TYPE_BULK: if (r->len < 0) { RETVAL_NULL(); } else { RETVAL_STRINGL(r->str, r->len); } break; case TYPE_MULTIBULK: if (r->elements < 0 && c->flags->null_mbulk_as_null) { RETVAL_NULL(); } else { array_init(z_arr); for (i = 0; i < r->elements; i++) { cluster_mbulk_variant_resp(r->element[i], c->flags->null_mbulk_as_null, z_arr); } RETVAL_ZVAL(z_arr, 0, 0); } break; default: RETVAL_FALSE; break; } } else { switch(r->type) { case TYPE_INT: add_next_index_long(&c->multi_resp, r->integer); break; case TYPE_ERR: add_next_index_bool(&c->multi_resp, 0); break; case TYPE_LINE: if (status_strings) { add_next_index_stringl(&c->multi_resp, r->str, r->len); } else { add_next_index_bool(&c->multi_resp, 1); } break; case TYPE_BULK: if (r->len < 0) { add_next_index_null(&c->multi_resp); } else { add_next_index_stringl(&c->multi_resp, r->str, r->len); } break; case TYPE_MULTIBULK: if (r->elements < 0 && c->flags->null_mbulk_as_null) { add_next_index_null(&c->multi_resp); } else { cluster_mbulk_variant_resp(r, c->flags->null_mbulk_as_null, &c->multi_resp); } break; default: add_next_index_bool(&c->multi_resp, 0); break; } } // Free our response structs, but not allocated data itself cluster_free_reply(r, 1); } PHP_REDIS_API void cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_cb cb; ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); cb = ctx ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp; cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); } PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 0, ctx); } PHP_REDIS_API void cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, c->flags->reply_literal, ctx); } PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, ctx); } /* Generic MULTI BULK response processor */ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb cb, void *ctx) { zval z_result; /* Abort if the reply isn't MULTIBULK or has an invalid length */ if (c->reply_type != TYPE_MULTIBULK || c->reply_len < -1) { CLUSTER_RETURN_FALSE(c); } if (c->reply_len == -1 && c->flags->null_mbulk_as_null) { ZVAL_NULL(&z_result); } else { array_init(&z_result); if (c->reply_len > 0) { /* Push serialization settings from the cluster into our socket */ c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; /* Call our specified callback */ if (cb(c->cmd_sock, &z_result, c->reply_len, ctx) == FAILURE) { zval_dtor(&z_result); CLUSTER_RETURN_FALSE(c); } } } // Success, make this array our return value if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(&z_result, 0, 1); } else { add_next_index_zval(&c->multi_resp, &z_result); } } /* HSCAN, SSCAN, ZSCAN */ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, REDIS_SCAN_TYPE type, long *it) { char *pit; // We always want to see a MULTIBULK response with two elements if (c->reply_type != TYPE_MULTIBULK || c->reply_len != 2) { return FAILURE; } // Read the BULK size if (cluster_check_response(c, &c->reply_type) || c->reply_type != TYPE_BULK) { return FAILURE; } // Read the iterator if ((pit = redis_sock_read_bulk_reply(c->cmd_sock,c->reply_len)) == NULL) { return FAILURE; } // Push the new iterator value to our caller *it = atol(pit); efree(pit); // We'll need another MULTIBULK response for the payload if (cluster_check_response(c, &c->reply_type) < 0) { return FAILURE; } // Use the proper response callback depending on scan type switch(type) { case TYPE_SCAN: cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); break; case TYPE_SSCAN: cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); break; case TYPE_HSCAN: cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); break; case TYPE_ZSCAN: cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); break; default: return FAILURE; } // Success return SUCCESS; } /* INFO response */ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval z_result; char *info; // Read our bulk response if ((info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) { CLUSTER_RETURN_FALSE(c); } /* Parse response, free memory */ redis_parse_info_response(info, &z_result); efree(info); // Return our array if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(&z_result, 0, 1); } else { add_next_index_zval(&c->multi_resp, &z_result); } } /* CLIENT LIST response */ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *info; zval z_result; /* Read the bulk response */ info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len); if (info == NULL) { CLUSTER_RETURN_FALSE(c); } /* Parse it and free the bulk string */ redis_parse_client_list_response(info, &z_result); efree(info); if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(&z_result, 0, 1); } else { add_next_index_zval(&c->multi_resp, &z_result); } } /* XRANGE */ PHP_REDIS_API void cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval z_messages; array_init(&z_messages); c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; if (redis_read_stream_messages(c->cmd_sock, c->reply_len, &z_messages) < 0) { zval_dtor(&z_messages); CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(&z_messages, 0, 1); } else { add_next_index_zval(&c->multi_resp, &z_messages); } } /* XREAD */ PHP_REDIS_API void cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval z_streams; c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; if (c->reply_len == -1 && c->flags->null_mbulk_as_null) { ZVAL_NULL(&z_streams); } else { array_init(&z_streams); if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, &z_streams) < 0) { zval_dtor(&z_streams); CLUSTER_RETURN_FALSE(c); } } if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(&z_streams, 0, 1); } else { add_next_index_zval(&c->multi_resp, &z_streams); } } /* XCLAIM */ PHP_REDIS_API void cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval z_msg; array_init(&z_msg); ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); if (redis_read_xclaim_reply(c->cmd_sock, c->reply_len, ctx == PHPREDIS_CTX_PTR, &z_msg) < 0) { zval_dtor(&z_msg); CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(&z_msg, 0, 1); } else { add_next_index_zval(&c->multi_resp, &z_msg); } } /* XINFO */ PHP_REDIS_API void cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval z_ret; array_init(&z_ret); if (redis_read_xinfo_response(c->cmd_sock, &z_ret, c->reply_len) != SUCCESS) { zval_dtor(&z_ret); CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { RETURN_ZVAL(&z_ret, 0, 1); } add_next_index_zval(&c->multi_resp, &z_ret); } /* LMPOP, ZMPOP, BLMPOP, BZMPOP */ PHP_REDIS_API void cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval z_ret; c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null; if (redis_read_mpop_response(c->cmd_sock, &z_ret, c->reply_len, ctx) == FAILURE) { CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { RETURN_ZVAL(&z_ret, 0, 0); } add_next_index_zval(&c->multi_resp, &z_ret); } static void cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx, int (*cb)(RedisSock*, zval*, long)) { zval z_ret; array_init(&z_ret); if (cb(c->cmd_sock, &z_ret, c->reply_len) != SUCCESS) { zval_dtor(&z_ret); CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { RETURN_ZVAL(&z_ret, 0, 1); } add_next_index_zval(&c->multi_resp, &z_ret); } PHP_REDIS_API void cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx, redis_read_acl_getuser_reply); } PHP_REDIS_API void cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx, redis_read_acl_log_reply); } /* MULTI BULK response loop where we might pull the next one */ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb, zval *z_ret) { ZVAL_NULL(z_ret); // Pull our next response if directed if (pull) { if (cluster_check_response(c, &c->reply_type) < 0) { return NULL; } } // Validate reply type and length if (c->reply_type != TYPE_MULTIBULK || c->reply_len == -1) { return NULL; } array_init(z_ret); // Call our callback if (cb(c->cmd_sock, z_ret, c->reply_len, NULL) == FAILURE) { zval_dtor(z_ret); return NULL; } return z_ret; } /* MULTI MULTI BULK reply (for EXEC) */ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval *multi_resp = &c->multi_resp; array_init(multi_resp); clusterFoldItem *fi = c->multi_head; while (fi) { /* Make sure our transaction didn't fail here */ if (c->multi_len[fi->slot] > -1) { /* Set the slot where we should look for responses. We don't allow * failover inside a transaction, so it will be the master we have * mapped. */ c->cmd_slot = fi->slot; c->cmd_sock = SLOT_SOCK(c, fi->slot); if (cluster_check_response(c, &c->reply_type) < 0) { zval_dtor(multi_resp); RETURN_FALSE; } fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx); } else { /* Just add false */ add_next_index_bool(multi_resp, 0); } fi = fi->next; } // Set our return array zval_dtor(return_value); RETVAL_ZVAL(multi_resp, 0, 1); } /* Generic handler for MGET */ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; /* Protect against an invalid response type, -1 response length, and failure * to consume the responses. */ c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL) == FAILURE; // If we had a failure, pad results with FALSE to indicate failure. Non // existent keys (e.g. for MGET will come back as NULL) if (fail) { while (mctx->count--) { add_next_index_bool(mctx->z_multi, 0); } } // If this is the tail of our multi command, we can set our returns if (mctx->last) { if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(mctx->z_multi, 0, 1); } else { add_next_index_zval(&c->multi_resp, mctx->z_multi); } efree(mctx->z_multi); } // Clean up this context item efree(mctx); } /* Handler for MSETNX */ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; int real_argc = mctx->count/2; // Protect against an invalid response type if (c->reply_type != TYPE_INT) { php_error_docref(0, E_WARNING, "Invalid response type for MSETNX"); while (real_argc--) { add_next_index_bool(mctx->z_multi, 0); } return; } // Response will be 1/0 per key, so the client can match them up while (real_argc--) { add_next_index_long(mctx->z_multi, c->reply_len); } // Set return value if it's our last response if (mctx->last) { if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(mctx->z_multi, 0, 0); } else { add_next_index_zval(&c->multi_resp, mctx->z_multi); } efree(mctx->z_multi); } // Free multi context efree(mctx); } /* Handler for DEL */ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; // If we get an invalid reply, inform the client if (c->reply_type != TYPE_INT) { php_error_docref(0, E_WARNING, "Invalid reply type returned for DEL command"); efree(mctx); return; } // Increment by the number of keys deleted Z_LVAL_P(mctx->z_multi) += c->reply_len; if (mctx->last) { if (CLUSTER_IS_ATOMIC(c)) { ZVAL_LONG(return_value, Z_LVAL_P(mctx->z_multi)); } else { add_next_index_long(&c->multi_resp, Z_LVAL_P(mctx->z_multi)); } efree(mctx->z_multi); } efree(ctx); } /* Handler for MSET */ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; // If we get an invalid reply type something very wrong has happened, // and we have to abort. if (c->reply_type != TYPE_LINE) { php_error_docref(0, E_ERROR, "Invalid reply type returned for MSET command"); zval_dtor(mctx->z_multi); efree(mctx->z_multi); efree(mctx); RETURN_FALSE; } // Set our return if it's the last call if (mctx->last) { if (CLUSTER_IS_ATOMIC(c)) { ZVAL_BOOL(return_value, zval_is_true(mctx->z_multi)); } else { add_next_index_bool(&c->multi_resp, zval_is_true(mctx->z_multi)); } efree(mctx->z_multi); } efree(mctx); } /* Raw MULTI BULK reply */ PHP_REDIS_API void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_raw, NULL); } /* Unserialize all the things */ PHP_REDIS_API void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop, NULL); } /* For handling responses where we get key, value, key, value that * we will turn into key => value, key => value. */ PHP_REDIS_API void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_zipstr, NULL); } /* Handling key,value to key=>value where the values are doubles */ PHP_REDIS_API void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_zipdbl, NULL); } PHP_REDIS_API void cluster_mbulk_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_dbl, ctx); } /* Associate multi bulk response (for HMGET really) */ PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_assoc, ctx); } /* * Various MULTI BULK reply callback functions */ int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result, long long count, void *ctx) { char *line; int line_len; while (count--) { line = redis_sock_read(redis_sock, &line_len); if (line != NULL) { add_next_index_double(z_result, atof(line)); efree(line); } else { add_next_index_bool(z_result, 0); } } return SUCCESS; } /* MULTI BULK response where we don't touch the values (e.g. KEYS) */ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, long long count, void *ctx) { char *line; int line_len; // Iterate over the number we have while (count--) { // Read the line, which should never come back null line = redis_sock_read(redis_sock, &line_len); if (line == NULL) return FAILURE; // Add to our result array add_next_index_stringl(z_result, line, line_len); efree(line); } // Success! return SUCCESS; } /* MULTI BULK response where we unserialize everything */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count, void *ctx) { char *line; int line_len; /* Iterate over the lines we have to process */ while (count--) { /* Read our line */ line = redis_sock_read(redis_sock, &line_len); if (line != NULL) { zval z_unpacked; if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) { add_next_index_zval(z_result, &z_unpacked); } else { add_next_index_stringl(z_result, line, line_len); } efree(line); } else { add_next_index_bool(z_result, 0); } } return SUCCESS; } /* MULTI BULK response where we turn key1,value1 into key1=>value1 */ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count, void *ctx) { char *line, *key = NULL; int line_len, key_len = 0; long long idx = 0; // Our count will need to be divisible by 2 if (count % 2 != 0) { return -1; } // Iterate through our elements while (count--) { // Grab our line, bomb out on failure line = redis_sock_read(redis_sock, &line_len); if (!line) return -1; if (idx++ % 2 == 0) { // Save our key and length key = line; key_len = line_len; } else { /* Attempt unpacking */ zval z_unpacked; if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) { add_assoc_zval(z_result, key, &z_unpacked); } else { add_assoc_stringl_ex(z_result, key, key_len, line, line_len); } efree(line); efree(key); } } return SUCCESS; } /* MULTI BULK loop processor where we expect key,score key, score */ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, long long count, void *ctx) { char *line, *key = NULL; int line_len, key_len = 0; long long idx = 0; // Our context will need to be divisible by 2 if (count %2 != 0) { return -1; } // While we have elements while (count--) { line = redis_sock_read(redis_sock, &line_len); if (line != NULL) { if (idx++ % 2 == 0) { key = line; key_len = line_len; } else { zval zv, *z = &zv; if (redis_unpack(redis_sock,key,key_len, z)) { zend_string *zstr = zval_get_string(z); add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line)); zend_string_release(zstr); zval_dtor(z); } else { add_assoc_double_ex(z_result, key, key_len, atof(line)); } /* Free our key and line */ efree(key); efree(line); } } } return SUCCESS; } /* MULTI BULK where we're passed the keys, and we attach vals */ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, long long count, void *ctx) { char *line; int line_len, i; zval *z_keys = ctx; // Loop while we've got replies for (i = 0; i < count; ++i) { zend_string *zstr = zval_get_string(&z_keys[i]); line = redis_sock_read(redis_sock, &line_len); if (line != NULL) { zval z_unpacked; if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) { add_assoc_zval_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked); } else { add_assoc_stringl_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), line, line_len); } efree(line); } else { add_assoc_bool_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0); } // Clean up key context zend_string_release(zstr); zval_dtor(&z_keys[i]); } // Clean up our keys overall efree(z_keys); // Success! return SUCCESS; } /* Free an array of zend_string seeds */ void free_seed_array(zend_string **seeds, uint32_t nseeds) { int i; if (seeds == NULL) return; for (i = 0; i < nseeds; i++) zend_string_release(seeds[i]); efree(seeds); } static zend_string **get_valid_seeds(HashTable *input, uint32_t *nseeds) { HashTable *valid; uint32_t count, idx = 0; zval *z_seed; zend_string *zkey, **seeds = NULL; /* Short circuit if we don't have any sees */ count = zend_hash_num_elements(input); if (count == 0) return NULL; ALLOC_HASHTABLE(valid); zend_hash_init(valid, count, NULL, NULL, 0); ZEND_HASH_FOREACH_VAL(input, z_seed) { ZVAL_DEREF(z_seed); if (Z_TYPE_P(z_seed) != IS_STRING) { php_error_docref(NULL, E_WARNING, "Skipping non-string entry in seeds array"); continue; } else if (strrchr(Z_STRVAL_P(z_seed), ':') == NULL) { php_error_docref(NULL, E_WARNING, "Seed '%s' not in host:port format, ignoring", Z_STRVAL_P(z_seed)); continue; } /* Add as a key to avoid duplicates */ zend_hash_str_update_ptr(valid, Z_STRVAL_P(z_seed), Z_STRLEN_P(z_seed), NULL); } ZEND_HASH_FOREACH_END(); /* We need at least one valid seed */ count = zend_hash_num_elements(valid); if (count == 0) goto cleanup; /* Populate our return array */ seeds = ecalloc(count, sizeof(*seeds)); ZEND_HASH_FOREACH_STR_KEY(valid, zkey) { seeds[idx++] = zend_string_copy(zkey); } ZEND_HASH_FOREACH_END(); *nseeds = idx; cleanup: zend_hash_destroy(valid); FREE_HASHTABLE(valid); return seeds; } /* Validate cluster construction arguments and return a sanitized and validated * array of seeds */ zend_string** cluster_validate_args(double timeout, double read_timeout, HashTable *seeds, uint32_t *nseeds, char **errstr) { zend_string **retval; if (timeout > INT_MAX) { if (errstr) *errstr = "Invalid timeout"; return NULL; } if (read_timeout > INT_MAX) { if (errstr) *errstr = "Invalid read timeout"; return NULL; } retval = get_valid_seeds(seeds, nseeds); if (retval == NULL && errstr) *errstr = "No valid seeds detected"; return retval; } /* Helper function to compare to host:port seeds */ static int cluster_cmp_seeds(const void *a, const void *b) { zend_string *za = *(zend_string **)a; zend_string *zb = *(zend_string **)b; return strcmp(ZSTR_VAL(za), ZSTR_VAL(zb)); } static void cluster_swap_seeds(void *a, void *b) { zend_string **za, **zb, *tmp; za = a; zb = b; tmp = *za; *za = *zb; *zb = tmp; } /* Turn an array of cluster seeds into a string we can cache. If we get here we know * we have at least one entry and that every entry is a string in the form host:port */ #define SLOT_CACHE_PREFIX "phpredis_slots:" zend_string *cluster_hash_seeds(zend_string **seeds, uint32_t count) { smart_str hash = {0}; size_t i; /* Sort our seeds so any any array with identical seeds hashes to the same key * regardless of what order the user gives them to us in. */ zend_sort(seeds, count, sizeof(*seeds), cluster_cmp_seeds, cluster_swap_seeds); /* Global phpredis hash prefix */ smart_str_appendl(&hash, SLOT_CACHE_PREFIX, sizeof(SLOT_CACHE_PREFIX) - 1); /* Construct our actual hash */ for (i = 0; i < count; i++) { smart_str_appendc(&hash, '['); smart_str_append_ex(&hash, seeds[i], 0); smart_str_appendc(&hash, ']'); } /* Null terminate */ smart_str_0(&hash); /* Return the internal zend_string */ return hash.s; } PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash) { zend_resource *le; /* Look for cached slot information */ le = zend_hash_find_ptr(&EG(persistent_list), hash); if (le != NULL) { /* Sanity check on our list type */ if (le->type == le_cluster_slot_cache) { /* Success, return the cached entry */ return le->ptr; } php_error_docref(0, E_WARNING, "Invalid slot cache resource"); } /* Not found */ return NULL; } /* Cache a cluster's slot information in persistent_list if it's enabled */ PHP_REDIS_API void cluster_cache_store(zend_string *hash, HashTable *nodes) { redisCachedCluster *cc = cluster_cache_create(hash, nodes); redis_register_persistent_resource(cc->hash, cc, le_cluster_slot_cache); } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ redis-6.0.2/cluster_library.h0000644000175000000120000004641514515245367017010 0ustar pyatsukhnenkowheel#ifndef _PHPREDIS_CLUSTER_LIBRARY_H #define _PHPREDIS_CLUSTER_LIBRARY_H #include "common.h" #ifdef ZTS #include "TSRM.h" #endif /* Redis cluster hash slots and N-1 which we'll use to find it */ #define REDIS_CLUSTER_SLOTS 16384 #define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) /* Complete representation for various commands in RESP */ #define RESP_UNWATCH_CMD "*1\r\n$7\r\nUNWATCH\r\n" #define RESP_CLUSTER_SLOTS_CMD "*2\r\n$7\r\nCLUSTER\r\n$5\r\nSLOTS\r\n" #define RESP_ASKING_CMD "*1\r\n$6\r\nASKING\r\n" #define RESP_READONLY_CMD "*1\r\n$8\r\nREADONLY\r\n" #define RESP_READWRITE_CMD "*1\r\n$9\r\nREADWRITE\r\n" #define RESP_READONLY_CMD_LEN (sizeof(RESP_READONLY_CMD)-1) /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ p[4]=='D' && p[5]==' ') #define IS_ASK(p) (p[0]=='A' && p[1]=='S' && p[2]=='K' && p[3]==' ') /* MOVED/ASK lengths */ #define MOVED_LEN (sizeof("MOVED ")-1) #define ASK_LEN (sizeof("ASK ")-1) /* Initial allocation size for key distribution container */ #define CLUSTER_KEYDIST_ALLOC 8 /* Macros to access nodes, sockets, and streams for a given slot */ #define SLOT(c,s) (c->master[s]) #define SLOT_SOCK(c,s) (SLOT(c,s)->sock) #define SLOT_STREAM(c,s) (SLOT_SOCK(c,s)->stream) #define SLOT_SLAVES(c,s) (c->master[s]->slaves) /* Compare redirection slot information with the passed node */ #define CLUSTER_REDIR_CMP(c, sock) \ (sock->port != c->redir_port || \ ZSTR_LEN(sock->host) != c->redir_host_len || \ memcmp(ZSTR_VAL(sock->host),c->redir_host,c->redir_host_len)) /* Clear out our "last error" */ #define CLUSTER_CLEAR_ERROR(c) do { \ if (c->err) { \ zend_string_release(c->err); \ c->err = NULL; \ } \ c->clusterdown = 0; \ } while (0) /* Protected sending of data down the wire to a RedisSock->stream */ #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \ (sock && !redis_sock_server_open(sock) && sock->stream && !redis_check_eof(sock, 0, 1) && \ redis_sock_write_raw(sock, buf, len) == len) /* Macro to read our reply type character */ #define CLUSTER_VALIDATE_REPLY_TYPE(sock, type) \ (redis_check_eof(sock, 1, 1) == 0 && redis_sock_getc(sock) == type) /* Reset our last single line reply buffer and length */ #define CLUSTER_CLEAR_REPLY(c) \ *c->line_reply = '\0'; c->reply_len = 0; /* Helper to determine if we're in MULTI mode */ #define CLUSTER_IS_ATOMIC(c) (c->flags->mode != MULTI) /* Helper that either returns false or adds false in multi mode */ #define CLUSTER_RETURN_FALSE(c) \ if(CLUSTER_IS_ATOMIC(c)) { \ RETURN_FALSE; \ } else { \ add_next_index_bool(&c->multi_resp, 0); \ return; \ } /* Helper to either return a bool value or add it to MULTI response */ #define CLUSTER_RETURN_BOOL(c, b) \ if(CLUSTER_IS_ATOMIC(c)) { \ RETURN_BOOL(b); \ } else { \ add_next_index_bool(&c->multi_resp, b); \ } /* Helper to respond with a double or add it to our MULTI response */ #define CLUSTER_RETURN_DOUBLE(c, d) \ if(CLUSTER_IS_ATOMIC(c)) { \ RETURN_DOUBLE(d); \ } else { \ add_next_index_double(&c->multi_resp, d); \ } /* Helper to return a string value */ #define CLUSTER_RETURN_STRING(c, str, len) \ if(CLUSTER_IS_ATOMIC(c)) { \ RETVAL_STRINGL(str, len); \ } else { \ add_next_index_stringl(&c->multi_resp, str, len); \ } \ /* Return a LONG value */ #define CLUSTER_RETURN_LONG(c, val) \ if(CLUSTER_IS_ATOMIC(c)) { \ RETURN_LONG(val); \ } else { \ add_next_index_long(&c->multi_resp, val); \ } /* Macro to clear out a clusterMultiCmd structure */ #define CLUSTER_MULTI_CLEAR(mc) \ mc->cmd.len = 0; \ mc->args.len = 0; \ mc->argc = 0; \ /* Initialzie a clusterMultiCmd with a keyword and length */ #define CLUSTER_MULTI_INIT(mc, keyword, keyword_len) \ mc.kw = keyword; \ mc.kw_len = keyword_len; \ #define CLUSTER_CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1) /* Cluster redirection enum */ typedef enum CLUSTER_REDIR_TYPE { REDIR_NONE, REDIR_MOVED, REDIR_ASK } CLUSTER_REDIR_TYPE; /* MULTI BULK response callback typedef */ typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void*); /* A list of covered slot ranges */ typedef struct redisSlotRange { unsigned short low; unsigned short high; } redisSlotRange; /* Simple host/port information for our cache */ typedef struct redisCachedHost { zend_string *addr; unsigned short port; } redisCachedHost; /* Storage for a cached master node */ typedef struct redisCachedMaster { redisCachedHost host; redisSlotRange *slot; /* Slots and count */ size_t slots; redisCachedHost *slave; /* Slaves and their count */ size_t slaves; } redisCachedMaster; typedef struct redisCachedCluster { // int rsrc_id; /* Zend resource ID */ zend_string *hash; /* What we're cached by */ redisCachedMaster *master; /* Array of masters */ size_t count; /* Number of masters */ } redisCachedCluster; /* A Redis Cluster master node */ typedef struct redisClusterNode { RedisSock *sock; /* Our Redis socket in question */ short slot; /* One slot we believe this node serves */ zend_llist slots; /* List of all slots we believe this node serves */ unsigned short slave; /* Are we a slave */ HashTable *slaves; /* Hash table of slaves */ } redisClusterNode; /* Forward declarations */ typedef struct clusterFoldItem clusterFoldItem; /* RedisCluster implementation structure */ typedef struct redisCluster { /* One RedisSock struct for serialization and prefix information */ RedisSock *flags; /* How long in milliseconds should we wait when being bounced around */ long waitms; /* Are we flagged as being in readonly mode, meaning we could fall back to * a given master's slave */ short readonly; /* RedisCluster failover options (never, on error, to load balance) */ short failover; /* Hash table of seed host/ports */ HashTable *seeds; /* RedisCluster masters, by direct slot */ redisClusterNode *master[REDIS_CLUSTER_SLOTS]; /* All RedisCluster objects we've created/are connected to */ HashTable *nodes; /* Transaction handling linked list, and where we are as we EXEC */ clusterFoldItem *multi_head; clusterFoldItem *multi_curr; /* When we issue EXEC to nodes, we need to keep track of how many replies * we have, as this can fail for various reasons (EXECABORT, watch, etc.) */ char multi_len[REDIS_CLUSTER_SLOTS]; /* Variable to store MULTI response */ zval multi_resp; /* Flag for when we get a CLUSTERDOWN error */ short clusterdown; /* Key to our persistent list cache and number of redirections we've * received since construction */ zend_string *cache_key; uint64_t redirections; /* The last ERROR we encountered */ zend_string *err; /* The slot our command is operating on, as well as it's socket */ unsigned short cmd_slot; RedisSock *cmd_sock; /* The slot where we're subscribed */ short subscribed_slot; /* The first line of our last reply, not including our reply type byte * or the trailing \r\n */ char line_reply[1024]; /* The last reply type and length or integer response we got */ REDIS_REPLY_TYPE reply_type; long long reply_len; /* Last MOVED or ASK redirection response information */ CLUSTER_REDIR_TYPE redir_type; char redir_host[255]; int redir_host_len; unsigned short redir_slot; unsigned short redir_port; /* Zend object handler */ zend_object std; } redisCluster; /* RedisCluster response processing callback */ typedef void (*cluster_cb)(INTERNAL_FUNCTION_PARAMETERS, redisCluster*, void*); /* Context for processing transactions */ struct clusterFoldItem { /* Response processing callback */ cluster_cb callback; /* The actual socket where we send this request */ unsigned short slot; /* Any context we need to send to our callback */ void *ctx; /* Next item in our list */ struct clusterFoldItem *next; }; /* Key and value container, with info if they need freeing */ typedef struct clusterKeyVal { char *key, *val; int key_len, val_len; int key_free, val_free; } clusterKeyVal; /* Container to hold keys (and possibly values) for when we need to distribute * commands across more than 1 node (e.g. WATCH, MGET, MSET, etc) */ typedef struct clusterDistList { clusterKeyVal *entry; size_t len, size; } clusterDistList; /* Context for things like MGET/MSET/MSETNX. When executing in MULTI mode, * we'll want to re-integrate into one running array, except for the last * command execution, in which we'll want to return the value (or add it) */ typedef struct clusterMultiCtx { /* Our running array */ zval *z_multi; /* How many keys did we request for this bit */ int count; /* Is this the last entry */ short last; } clusterMultiCtx; /* Container for things like MGET, MSET, and MSETNX, which split the command * into a header and payload while aggregating to a specific slot. */ typedef struct clusterMultiCmd { /* Keyword and keyword length */ char *kw; int kw_len; /* Arguments in our payload */ int argc; /* The full command, built into cmd, and args as we aggregate */ smart_string cmd; smart_string args; } clusterMultiCmd; /* Hiredis like structure for processing any sort of reply Redis Cluster might * give us, including N level deep nested multi-bulk replies. Unlike hiredis * we don't encode errors, here as that's handled in the cluster structure. */ typedef struct clusterReply { REDIS_REPLY_TYPE type; /* Our reply type */ size_t integer; /* Integer reply */ long long len; /* Length of our string */ char *str; /* String reply */ long long elements; /* Count of array elements */ struct clusterReply **element; /* Array elements */ } clusterReply; /* Direct variant response handler */ clusterReply *cluster_read_resp(redisCluster *c, int status_strings); clusterReply *cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, char *line_reply, long long reply_len); void cluster_free_reply(clusterReply *reply, int free_data); /* Cluster distribution helpers for WATCH */ HashTable *cluster_dist_create(void); void cluster_dist_free(HashTable *ht); int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, size_t key_len, clusterKeyVal **kv); void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val ); /* Aggregation for multi commands like MGET, MSET, and MSETNX */ void cluster_multi_init(clusterMultiCmd *mc, char *kw, int kw_len); void cluster_multi_free(clusterMultiCmd *mc); void cluster_multi_add(clusterMultiCmd *mc, char *data, int data_len); void cluster_multi_fini(clusterMultiCmd *mc); /* Hash a key to it's slot, using the Redis Cluster hash algorithm */ unsigned short cluster_hash_key_zval(zval *key); unsigned short cluster_hash_key(const char *key, int len); unsigned short cluster_hash_key_zstr(zend_string *key); /* Validate and sanitize cluster construction args */ zend_string** cluster_validate_args(double timeout, double read_timeout, HashTable *seeds, uint32_t *nseeds, char **errstr); void free_seed_array(zend_string **seeds, uint32_t nseeds); /* Generate a unique hash string from seeds array */ zend_string *cluster_hash_seeds(zend_string **seeds, uint32_t nseeds); /* Get the current time in milliseconds */ long long mstime(void); PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len); PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force); PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot); PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot); PHP_REDIS_API int cluster_abort_exec(redisCluster *c); PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, unsigned short port); PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype); PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, int failover, int persistent); PHP_REDIS_API void cluster_free(redisCluster *c, int free_ctx); PHP_REDIS_API void cluster_init_seeds(redisCluster *c, zend_string **seeds, uint32_t nseeds); PHP_REDIS_API int cluster_map_keyspace(redisCluster *c); PHP_REDIS_API void cluster_free_node(redisClusterNode *node); /* Functions for interacting with cached slots maps */ PHP_REDIS_API redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes); PHP_REDIS_API void cluster_cache_free(redisCachedCluster *rcc); PHP_REDIS_API void cluster_init_cache(redisCluster *c, redisCachedCluster *rcc); /* Functions to facilitate cluster slot caching */ PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len); PHP_REDIS_API void cluster_cache_store(zend_string *hash, HashTable *nodes); PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash); /* * Redis Cluster response handlers. Our response handlers generally take the * following form: * PHP_REDIS_API void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, * void *ctx) * * Reply handlers are responsible for setting the PHP return value (either to * something valid, or FALSE in the case of some failures). */ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_object_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_zadd_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_srandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* MULTI BULK response functions */ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb func, void *ctx); PHP_REDIS_API void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb, zval *z_ret); /* Handlers for things like DEL/MGET/MSET/MSETNX */ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* Response handler for ZSCAN, SSCAN, and HSCAN */ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, REDIS_SCAN_TYPE type, long *it); /* INFO response handler */ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* CLIENT LIST response handler */ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* Custom STREAM handlers */ PHP_REDIS_API void cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* Custom ACL handlers */ PHP_REDIS_API void cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); #endif /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ redis-6.0.2/common.h0000644000175000000120000002565714515245367015100 0ustar pyatsukhnenkowheel#include "php.h" #include "php_ini.h" #ifndef REDIS_COMMON_H #define REDIS_COMMON_H #define PHPREDIS_CTX_PTR ((char *)0xDEADC0DE) #define PHPREDIS_NOTUSED(v) ((void)v) #include "zend_llist.h" #include #include #include #include #define PHPREDIS_GET_OBJECT(class_entry, o) (class_entry *)((char *)o - XtOffsetOf(class_entry, std)) #define PHPREDIS_ZVAL_GET_OBJECT(class_entry, z) PHPREDIS_GET_OBJECT(class_entry, Z_OBJ_P(z)) /* We'll fallthrough if we want to */ #ifndef __has_attribute #define __has_attribute(x) 0 #endif #if __has_attribute(__fallthrough__) #define REDIS_FALLTHROUGH __attribute__((__fallthrough__)) #else #define REDIS_FALLTHROUGH do { } while (0) #endif /* NULL check so Eclipse doesn't go crazy */ #ifndef NULL #define NULL ((void *) 0) #endif #include "backoff.h" typedef enum { REDIS_SOCK_STATUS_FAILED = -1, REDIS_SOCK_STATUS_DISCONNECTED, REDIS_SOCK_STATUS_CONNECTED, REDIS_SOCK_STATUS_AUTHENTICATED, REDIS_SOCK_STATUS_READY } redis_sock_status; #define _NL "\r\n" /* properties */ #define REDIS_NOT_FOUND 0 #define REDIS_STRING 1 #define REDIS_SET 2 #define REDIS_LIST 3 #define REDIS_ZSET 4 #define REDIS_HASH 5 #define REDIS_STREAM 6 #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) #define phpredis_atoi64(p) _atoi64((p)) #else #define PHP_REDIS_API #define phpredis_atoi64(p) atoll((p)) #endif /* reply types */ typedef enum _REDIS_REPLY_TYPE { TYPE_EOF = -1, TYPE_LINE = '+', TYPE_INT = ':', TYPE_ERR = '-', TYPE_BULK = '$', TYPE_MULTIBULK = '*' } REDIS_REPLY_TYPE; /* SCAN variants */ typedef enum _REDIS_SCAN_TYPE { TYPE_SCAN, TYPE_SSCAN, TYPE_HSCAN, TYPE_ZSCAN } REDIS_SCAN_TYPE; /* PUBSUB subcommands */ typedef enum _PUBSUB_TYPE { PUBSUB_CHANNELS, PUBSUB_NUMSUB, PUBSUB_NUMPAT } PUBSUB_TYPE; #define REDIS_SUBSCRIBE_IDX 0 #define REDIS_PSUBSCRIBE_IDX 1 #define REDIS_SSUBSCRIBE_IDX 2 #define REDIS_SUBS_BUCKETS 3 /* options */ #define REDIS_OPT_SERIALIZER 1 #define REDIS_OPT_PREFIX 2 #define REDIS_OPT_READ_TIMEOUT 3 #define REDIS_OPT_SCAN 4 #define REDIS_OPT_FAILOVER 5 #define REDIS_OPT_TCP_KEEPALIVE 6 #define REDIS_OPT_COMPRESSION 7 #define REDIS_OPT_REPLY_LITERAL 8 #define REDIS_OPT_COMPRESSION_LEVEL 9 #define REDIS_OPT_NULL_MBULK_AS_NULL 10 #define REDIS_OPT_MAX_RETRIES 11 #define REDIS_OPT_BACKOFF_ALGORITHM 12 #define REDIS_OPT_BACKOFF_BASE 13 #define REDIS_OPT_BACKOFF_CAP 14 /* cluster options */ #define REDIS_FAILOVER_NONE 0 #define REDIS_FAILOVER_ERROR 1 #define REDIS_FAILOVER_DISTRIBUTE 2 #define REDIS_FAILOVER_DISTRIBUTE_SLAVES 3 /* serializers */ typedef enum { REDIS_SERIALIZER_NONE, REDIS_SERIALIZER_PHP, REDIS_SERIALIZER_IGBINARY, REDIS_SERIALIZER_MSGPACK, REDIS_SERIALIZER_JSON } redis_serializer; /* compression */ #define REDIS_COMPRESSION_NONE 0 #define REDIS_COMPRESSION_LZF 1 #define REDIS_COMPRESSION_ZSTD 2 #define REDIS_COMPRESSION_LZ4 3 /* SCAN options */ #define REDIS_SCAN_NORETRY 0 #define REDIS_SCAN_RETRY 1 #define REDIS_SCAN_PREFIX 2 #define REDIS_SCAN_NOPREFIX 3 /* BACKOFF_ALGORITHM options */ #define REDIS_BACKOFF_ALGORITHMS 7 #define REDIS_BACKOFF_ALGORITHM_DEFAULT 0 #define REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER 1 #define REDIS_BACKOFF_ALGORITHM_FULL_JITTER 2 #define REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER 3 #define REDIS_BACKOFF_ALGORITHM_EXPONENTIAL 4 #define REDIS_BACKOFF_ALGORITHM_UNIFORM 5 #define REDIS_BACKOFF_ALGORITHM_CONSTANT 6 /* GETBIT/SETBIT offset range limits */ #define BITOP_MIN_OFFSET 0 #define BITOP_MAX_OFFSET 4294967295U /* Transaction modes */ #define ATOMIC 0 #define MULTI 1 #define PIPELINE 2 #define PHPREDIS_DEBUG_LOGGING 0 #if PHP_VERSION_ID < 80000 #define Z_PARAM_ARRAY_HT_OR_NULL(dest) \ Z_PARAM_ARRAY_HT_EX(dest, 1, 0) #define Z_PARAM_STR_OR_NULL(dest) \ Z_PARAM_STR_EX(dest, 1, 0) #define Z_PARAM_ZVAL_OR_NULL(dest) \ Z_PARAM_ZVAL_EX(dest, 1, 0) #define Z_PARAM_BOOL_OR_NULL(dest, is_null) \ Z_PARAM_BOOL_EX(dest, is_null, 1, 0) #endif #if PHPREDIS_DEBUG_LOGGING == 1 #define redisDbgFmt(fmt, ...) \ php_printf("%s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, __VA_ARGS__) #define redisDbgStr(str) phpredisDebugFmt("%s", str) #else #define redisDbgFmt(fmt, ...) ((void)0) #define redisDbgStr(str) ((void)0) #endif #define IS_ATOMIC(redis_sock) (redis_sock->mode == ATOMIC) #define IS_MULTI(redis_sock) (redis_sock->mode & MULTI) #define IS_PIPELINE(redis_sock) (redis_sock->mode & PIPELINE) #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \ if (redis_sock->pipeline_cmd == NULL) { \ redis_sock->pipeline_cmd = zend_string_init(cmd, cmd_len, 0); \ } else { \ size_t pipeline_len = ZSTR_LEN(redis_sock->pipeline_cmd); \ redis_sock->pipeline_cmd = zend_string_realloc(redis_sock->pipeline_cmd, pipeline_len + cmd_len, 0); \ memcpy(&ZSTR_VAL(redis_sock->pipeline_cmd)[pipeline_len], cmd, cmd_len); \ } \ } while (0) #define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) \ if(redis_sock_write(redis_sock, cmd, cmd_len) < 0) { \ efree(cmd); \ RETURN_FALSE; \ } #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \ fold_item *fi = malloc(sizeof(fold_item)); \ fi->fun = callback; \ fi->ctx = closure_context; \ fi->next = NULL; \ if (redis_sock->current) { \ redis_sock->current->next = fi; \ } \ redis_sock->current = fi; \ if (NULL == redis_sock->head) { \ redis_sock->head = redis_sock->current; \ } \ } while (0) #define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \ if (IS_PIPELINE(redis_sock)) { \ PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \ } else { \ SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \ } \ efree(cmd); #define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ if (!IS_PIPELINE(redis_sock)) { \ if (redis_response_enqueued(redis_sock) != SUCCESS) { \ RETURN_FALSE; \ } \ } \ REDIS_SAVE_CALLBACK(function, closure_context); \ RETURN_ZVAL(getThis(), 1, 0); \ #define REDIS_PROCESS_RESPONSE(function) else { \ REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) \ } /* Process a command assuming our command where our command building * function is redis__cmd */ #define REDIS_PROCESS_CMD(cmdname, resp_func) \ RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || \ redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, \ &cmd, &cmd_len, NULL, &ctx)==FAILURE) { \ RETURN_FALSE; \ } \ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ if (IS_ATOMIC(redis_sock)) { \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \ } else { \ REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \ } /* Process a command but with a specific command building function * and keyword which is passed to us*/ #define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || \ cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, \ &cmd_len, NULL, &ctx)==FAILURE) { \ RETURN_FALSE; \ } \ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ if (IS_ATOMIC(redis_sock)) { \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \ } else { \ REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \ } /* Case sensitive compare against compile-time static string */ #define REDIS_STRCMP_STATIC(s, len, sstr) \ (len == sizeof(sstr) - 1 && !strncmp(s, sstr, len)) /* Case insensitive compare against compile-time static string */ #define REDIS_STRICMP_STATIC(s, len, sstr) \ (len == sizeof(sstr) - 1 && !strncasecmp(s, sstr, len)) /* Test if a zval is a string and (case insensitive) matches a static string */ #define ZVAL_STRICMP_STATIC(zv, sstr) \ REDIS_STRICMP_STATIC(Z_STRVAL_P(zv), Z_STRLEN_P(zv), sstr) /* Case insensitive compare of a zend_string to a static string */ #define ZSTR_STRICMP_STATIC(zs, sstr) \ REDIS_STRICMP_STATIC(ZSTR_VAL(zs), ZSTR_LEN(zs), sstr) #define REDIS_ENABLE_MODE(redis_sock, m) (redis_sock->mode |= m) #define REDIS_DISABLE_MODE(redis_sock, m) (redis_sock->mode &= ~m) /* HOST_NAME_MAX doesn't exist everywhere */ #ifndef HOST_NAME_MAX #if defined(_POSIX_HOST_NAME_MAX) #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX #elif defined(MAXHOSTNAMELEN) #define HOST_NAME_MAX MAXHOSTNAMELEN #else #define HOST_NAME_MAX 255 #endif #endif /* Complete representation for various commands in RESP */ #define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" #define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" #define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" /* {{{ struct RedisSock */ typedef struct { php_stream *stream; php_stream_context *stream_ctx; zend_string *host; int port; zend_string *user; zend_string *pass; double timeout; double read_timeout; long retry_interval; int max_retries; struct RedisBackoff backoff; redis_sock_status status; int persistent; int watching; zend_string *persistent_id; HashTable *subs[REDIS_SUBS_BUCKETS]; redis_serializer serializer; int compression; int compression_level; long dbNumber; zend_string *prefix; short mode; struct fold_item *head; struct fold_item *current; zend_string *pipeline_cmd; zend_string *err; int scan; int readonly; int reply_literal; int null_mbulk_as_null; int tcp_keepalive; int sentinel; size_t txBytes; size_t rxBytes; } RedisSock; /* }}} */ /* Redis response handler function callback prototype */ typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*, zval*, void*); typedef struct fold_item { FailableResultCallback fun; void *ctx; struct fold_item *next; } fold_item; typedef struct { zend_llist list; int nb_active; } ConnectionPool; typedef struct { RedisSock *sock; zend_object std; } redis_object; extern const zend_function_entry *redis_get_methods(void); #endif redis-6.0.2/config.m40000644000175000000120000002525614515245367015141 0ustar pyatsukhnenkowheeldnl $Id$ dnl config.m4 for extension redis PHP_ARG_ENABLE(redis, whether to enable redis support, dnl Make sure that the comment is aligned: [ --enable-redis Enable redis support]) PHP_ARG_ENABLE(redis-session, whether to enable sessions, [ --disable-redis-session Disable session support], yes, no) PHP_ARG_ENABLE(redis-json, whether to enable json serializer support, [ --disable-redis-json Disable json serializer support], yes, no) PHP_ARG_ENABLE(redis-igbinary, whether to enable igbinary serializer support, [ --enable-redis-igbinary Enable igbinary serializer support], no, no) PHP_ARG_ENABLE(redis-msgpack, whether to enable msgpack serializer support, [ --enable-redis-msgpack Enable msgpack serializer support], no, no) PHP_ARG_ENABLE(redis-lzf, whether to enable lzf compression, [ --enable-redis-lzf Enable lzf compression support], no, no) PHP_ARG_WITH(liblzf, use system liblzf, [ --with-liblzf[=DIR] Use system liblzf], no, no) PHP_ARG_ENABLE(redis-zstd, whether to enable Zstd compression, [ --enable-redis-zstd Enable Zstd compression support], no, no) PHP_ARG_WITH(libzstd, use system libzstd, [ --with-libzstd[=DIR] Use system libzstd], yes, no) PHP_ARG_ENABLE(redis-lz4, whether to enable lz4 compression, [ --enable-redis-lz4 Enable lz4 compression support], no, no) PHP_ARG_WITH(liblz4, use system liblz4, [ --with-liblz4[=DIR] Use system liblz4], no, no) if test "$PHP_REDIS" != "no"; then if test "$PHP_REDIS_SESSION" != "no"; then AC_DEFINE(PHP_SESSION,1,[redis sessions]) fi AC_MSG_CHECKING([for hash includes]) hash_inc_path="" if test -f "$abs_srcdir/include/php/ext/hash/php_hash.h"; then hash_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/hash/php_hash.h"; then hash_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/hash/php_hash.h"; then hash_inc_path="$phpincludedir" else for i in php php7; do if test -f "$prefix/include/$i/ext/hash/php_hash.h"; then hash_inc_path="$prefix/include/$i" fi done fi if test "$hash_inc_path" = ""; then AC_MSG_ERROR([Cannot find php_hash.h]) else AC_MSG_RESULT([$hash_inc_path]) fi if test "$PHP_REDIS_JSON" != "no"; then AC_MSG_CHECKING([for json includes]) json_inc_path="" if test -f "$abs_srcdir/include/php/ext/json/php_json.h"; then json_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/json/php_json.h"; then json_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/json/php_json.h"; then json_inc_path="$phpincludedir" else for i in php php7; do if test -f "$prefix/include/$i/ext/json/php_json.h"; then json_inc_path="$prefix/include/$i" fi done fi if test "$json_inc_path" = ""; then AC_MSG_ERROR([Cannot find php_json.h]) else AC_MSG_RESULT([$json_inc_path]) fi fi AC_MSG_CHECKING([for redis json support]) if test "$PHP_REDIS_JSON" != "no"; then AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_REDIS_JSON,1,[Whether redis json serializer is enabled]) JSON_INCLUDES="-I$json_inc_path" JSON_EXT_DIR="$json_inc_path/ext" ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(redis, json) ]) PHP_ADD_INCLUDE($JSON_EXT_DIR) else JSON_INCLUDES="" AC_MSG_RESULT([disabled]) fi if test "$PHP_REDIS_IGBINARY" != "no"; then AC_MSG_CHECKING([for igbinary includes]) igbinary_inc_path="" if test -f "$abs_srcdir/include/php/ext/igbinary/igbinary.h"; then igbinary_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/igbinary/igbinary.h"; then igbinary_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/igbinary/igbinary.h"; then igbinary_inc_path="$phpincludedir" else for i in php php7; do if test -f "$prefix/include/$i/ext/igbinary/igbinary.h"; then igbinary_inc_path="$prefix/include/$i" fi done fi if test "$igbinary_inc_path" = ""; then AC_MSG_ERROR([Cannot find igbinary.h]) else AC_MSG_RESULT([$igbinary_inc_path]) fi fi AC_MSG_CHECKING([for redis igbinary support]) if test "$PHP_REDIS_IGBINARY" != "no"; then AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_REDIS_IGBINARY,1,[Whether redis igbinary serializer is enabled]) IGBINARY_INCLUDES="-I$igbinary_inc_path" IGBINARY_EXT_DIR="$igbinary_inc_path/ext" ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(redis, igbinary) ]) PHP_ADD_INCLUDE($IGBINARY_EXT_DIR) else IGBINARY_INCLUDES="" AC_MSG_RESULT([disabled]) fi if test "$PHP_REDIS_MSGPACK" != "no"; then AC_MSG_CHECKING([for msgpack includes]) msgpack_inc_path="" if test -f "$abs_srcdir/include/php/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$phpincludedir" else for i in php php7; do if test -f "$prefix/include/$i/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$prefix/include/$i" fi done fi if test "$msgpack_inc_path" = ""; then AC_MSG_ERROR([Cannot find php_msgpack.h]) else AC_MSG_RESULT([$msgpack_inc_path]) fi fi if test "$PHP_REDIS_MSGPACK" != "no"; then AC_MSG_CHECKING([for php msgpack version >= 2.0.3]) MSGPACK_VERSION=`$EGREP "define PHP_MSGPACK_VERSION" $msgpack_inc_path/ext/msgpack/php_msgpack.h | $SED -e 's/[[^0-9\.]]//g'` if test `echo $MSGPACK_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*1000 + $2*100 + $3*10 + $4}'` -lt 2030; then AC_MSG_ERROR([version >= 2.0.3 required]) else AC_MSG_RESULT([yes]) fi AC_DEFINE(HAVE_REDIS_MSGPACK,1,[Whether redis msgpack serializer is enabled]) MSGPACK_INCLUDES="-I$msgpack_inc_path" MSGPACK_EXT_DIR="$msgpack_inc_path/ext" ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(redis, msgpack) ]) PHP_ADD_INCLUDE($MSGPACK_EXT_DIR) else MSGPACK_INCLUDES="" fi AC_PATH_PROG(PKG_CONFIG, pkg-config, no) if test "$PHP_REDIS_LZF" != "no"; then AC_DEFINE(HAVE_REDIS_LZF, 1, [ ]) if test "$PHP_LIBLZF" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists liblzf; then AC_MSG_CHECKING(for liblzf using pkg-config) LIBLZF_INC=`$PKG_CONFIG liblzf --cflags` LIBLZF_LIB=`$PKG_CONFIG liblzf --libs` LIBLZF_VER=`$PKG_CONFIG liblzf --modversion` AC_MSG_RESULT(found version $LIBLZF_VER) PHP_EVAL_LIBLINE($LIBLZF_LIB, REDIS_SHARED_LIBADD) PHP_EVAL_INCLINE($LIBLZF_INC) elif test "$PHP_LIBLZF" != "no"; then AC_MSG_CHECKING(for liblzf files in default path) for i in $PHP_LIBLZF /usr/local /usr; do if test -r $i/include/lzf.h; then AC_MSG_RESULT(found in $i) LIBLZF_DIR=$i break fi done if test -z "$LIBLZF_DIR"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([Please reinstall the liblzf distribution]) fi PHP_CHECK_LIBRARY(lzf, lzf_compress, [ PHP_ADD_LIBRARY_WITH_PATH(lzf, $LIBLZF_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD) ], [ AC_MSG_ERROR([could not find usable liblzf]) ], [ -L$LIBLZF_DIR/$PHP_LIBDIR ]) else PHP_ADD_INCLUDE(liblzf) PHP_ADD_INCLUDE($srcdir/liblzf) PHP_ADD_BUILD_DIR(liblzf) lzf_sources="liblzf/lzf_c.c liblzf/lzf_d.c" fi fi if test "$PHP_REDIS_LZ4" != "no"; then AC_DEFINE(HAVE_REDIS_LZ4, 1, [ ]) if test "$PHP_LIBLZ4" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists liblz4; then AC_MSG_CHECKING(for liblz4 using pkg-config) LIBLZ4_VER=`$PKG_CONFIG liblz4 --modversion` LIBLZ4_INC=`$PKG_CONFIG liblz4 --cflags` LIBLZ4_LIB=`$PKG_CONFIG liblz4 --libs` AC_MSG_RESULT(found version $LIBLZ4_VER) PHP_EVAL_LIBLINE($LIBLZ4_LIB, REDIS_SHARED_LIBADD) PHP_EVAL_INCLINE($LIBLZ4_INC) elif test "$PHP_LIBLZ4" != "no"; then AC_MSG_CHECKING(for liblz4 files in default path) for i in $PHP_LIBLZ4 /usr/local /usr; do if test -r $i/include/lz4.h; then AC_MSG_RESULT(found in $i) LIBLZ4_DIR=$i break fi done if test -z "$LIBLZ4_DIR"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([Please reinstall the liblz4 distribution]) fi PHP_CHECK_LIBRARY(lz4, LZ4_compress, [ PHP_ADD_LIBRARY_WITH_PATH(lz4, $LIBLZ4_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD) ], [ AC_MSG_ERROR([could not find usable liblz4]) ], [ -L$LIBLZ4_DIR/$PHP_LIBDIR ]) PHP_SUBST(REDIS_SHARED_LIBADD) else AC_MSG_ERROR([only system liblz4 is supported]) fi fi if test "$PHP_REDIS_ZSTD" != "no"; then AC_DEFINE(HAVE_REDIS_ZSTD, 1, [ ]) if test "$PHP_LIBZSTD" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libzstd; then AC_MSG_CHECKING(for libzstd using pkg-config) LIBZSTD_VER=`$PKG_CONFIG libzstd --modversion` if $PKG_CONFIG libzstd --atleast-version 1.3.0; then LIBZSTD_INC=`$PKG_CONFIG libzstd --cflags` LIBZSTD_LIB=`$PKG_CONFIG libzstd --libs` AC_MSG_RESULT(found version $LIBZSTD_VER) PHP_EVAL_LIBLINE($LIBZSTD_LIB, REDIS_SHARED_LIBADD) PHP_EVAL_INCLINE($LIBZSTD_INC) else AC_MSG_ERROR([found version $LIBZSTD_VER, version 1.3.0 required]) fi elif test "$PHP_LIBZSTD" != "no"; then AC_MSG_CHECKING(for libzstd files in default path) for i in $PHP_LIBZSTD /usr/local /usr; do if test -r $i/include/zstd.h; then AC_MSG_RESULT(found in $i) LIBZSTD_DIR=$i break fi done if test -z "$LIBZSTD_DIR"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([Please reinstall the libzstd distribution]) fi PHP_CHECK_LIBRARY(zstd, ZSTD_getFrameContentSize, [ PHP_ADD_LIBRARY_WITH_PATH(zstd, $LIBZSTD_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD) ], [ AC_MSG_ERROR([could not find usable libzstd, version 1.3.0 required]) ], [ -L$LIBZSTD_DIR/$PHP_LIBDIR ]) else AC_MSG_ERROR([only system libzstd is supported]) fi fi AC_CHECK_PROG([GIT], [git], [yes], [no]) if test "$GIT" = "yes" && test -d "$srcdir/.git"; then AC_DEFINE_UNQUOTED(GIT_REVISION, ["$(git log -1 --format=%H)"], [ ]) fi PHP_SUBST(REDIS_SHARED_LIBADD) PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c backoff.c $lzf_sources, $ext_shared) fi redis-6.0.2/config.w320000644000175000000120000000171114515245367015222 0ustar pyatsukhnenkowheel// vim: ft=javascript: ARG_ENABLE("redis", "whether to enable redis support", "no"); ARG_ENABLE("redis-session", "whether to enable sessions", "yes"); ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no"); if (PHP_REDIS != "no") { var sources = "redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c backoff.c"; if (PHP_REDIS_SESSION != "no") { ADD_EXTENSION_DEP("redis", "session"); ADD_FLAG("CFLAGS_REDIS", ' /D PHP_SESSION=1 '); AC_DEFINE("HAVE_REDIS_SESSION", 1); } if (PHP_REDIS_IGBINARY != "no") { if (CHECK_HEADER_ADD_INCLUDE("igbinary.h", "CFLAGS_REDIS", configure_module_dirname + "\\..\\igbinary")) { ADD_EXTENSION_DEP("redis", "igbinary"); AC_DEFINE("HAVE_REDIS_IGBINARY", 1); } else { WARNING("redis igbinary support not enabled"); } } EXTENSION("redis", sources); } redis-6.0.2/crc16.h0000644000175000000120000001060314515245367014507 0ustar pyatsukhnenkowheel/* * Copyright 2001-2010 Georges Menie (www.menie.org) * Copyright 2010 Salvatore Sanfilippo (adapted to Redis coding style) * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the University of California, Berkeley nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* CRC16 implementation according to CCITT standards. * * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the * following parameters: * * Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" * Width : 16 bit * Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) * Initialization : 0000 * Reflect Input byte : False * Reflect Output CRC : False * Xor constant to output CRC : 0000 * Output for "123456789" : 31C3 */ #include static const uint16_t crc16tab[256]= { 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 }; static inline uint16_t crc16(const char *buf, int len) { int counter; uint16_t crc = 0; for (counter = 0; counter < len; counter++) crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF]; return crc; } redis-6.0.2/library.c0000644000175000000120000041033214515245367015233 0ustar pyatsukhnenkowheel#include "php_redis.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "common.h" #include "php_network.h" #include #ifdef HAVE_REDIS_IGBINARY #include "igbinary/igbinary.h" #endif #ifdef HAVE_REDIS_MSGPACK #include "msgpack/php_msgpack.h" #endif #ifdef HAVE_REDIS_LZF #include #ifndef LZF_MARGIN #define LZF_MARGIN 128 #endif #endif #ifdef HAVE_REDIS_ZSTD #include #endif #ifdef HAVE_REDIS_LZ4 #include #include /* uint8_t crf + int length */ #define REDIS_LZ4_HDR_SIZE (sizeof(uint8_t) + sizeof(int)) #if defined(LZ4HC_CLEVEL_MAX) /* version >= 1.7.5 */ #define REDIS_LZ4_MAX_CLEVEL LZ4HC_CLEVEL_MAX #elif defined (LZ4HC_MAX_CLEVEL) /* version >= 1.7.3 */ #define REDIS_LZ4_MAX_CLEVEL LZ4HC_MAX_CLEVEL #else /* older versions */ #define REDIS_LZ4_MAX_CLEVEL 12 #endif #endif #include #include "php_redis.h" #include "library.h" #include "redis_commands.h" #ifdef HAVE_REDIS_JSON #include #endif #include #include #define UNSERIALIZE_NONE 0 #define UNSERIALIZE_KEYS 1 #define UNSERIALIZE_VALS 2 #define UNSERIALIZE_ALL 3 #define SCORE_DECODE_NONE 0 #define SCORE_DECODE_INT 1 #define SCORE_DECODE_DOUBLE 2 /* PhpRedis often returns either FALSE or NULL depending on whether we have * an option set, so this macro just wraps that often repeated logic */ #define REDIS_ZVAL_NULL(sock_, zv_) \ do { \ if ((sock_)->null_mbulk_as_null) { \ ZVAL_NULL((zv_)); \ } else { \ ZVAL_FALSE((zv_)); \ } \ } while (0) #ifndef PHP_WIN32 #include /* TCP_NODELAY */ #include /* SO_KEEPALIVE */ #else #include #endif extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; static int redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int count); /* Register a persistent resource in a a way that works for every PHP 7 version. */ void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id) { #if PHP_VERSION_ID < 70300 zend_resource res; res.type = le_id; res.ptr = ptr; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(id), ZSTR_LEN(id), &res, sizeof(res)); #else zend_register_persistent_resource(ZSTR_VAL(id), ZSTR_LEN(id), ptr, le_id); #endif } static ConnectionPool * redis_sock_get_connection_pool(RedisSock *redis_sock) { ConnectionPool *pool; zend_resource *le; zend_string *persistent_id; /* Generate our unique pool id depending on configuration */ persistent_id = redis_pool_spprintf(redis_sock, INI_STR("redis.pconnect.pool_pattern")); /* Return early if we can find the pool */ if ((le = zend_hash_find_ptr(&EG(persistent_list), persistent_id))) { zend_string_release(persistent_id); return le->ptr; } /* Create the pool and store it in our persistent list */ pool = pecalloc(1, sizeof(*pool), 1); zend_llist_init(&pool->list, sizeof(php_stream *), NULL, 1); redis_register_persistent_resource(persistent_id, pool, le_redis_pconnect); zend_string_release(persistent_id); return pool; } /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock) { char *cmd, *response; int cmd_len, response_len; cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "SELECT", "d", redis_sock->dbNumber); if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); return -1; } efree(cmd); if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { return -1; } if (strncmp(response, "+OK", 3)) { efree(response); return -1; } efree(response); return 0; } /* Append an AUTH command to a smart string if neccessary. This will either * append the new style AUTH , old style AUTH , or * append no command at all. Function returns 1 if we appended a command * and 0 otherwise. */ static int redis_sock_append_auth(RedisSock *redis_sock, smart_string *str) { /* We need a password at least */ if (redis_sock->pass == NULL) return 0; REDIS_CMD_INIT_SSTR_STATIC(str, !!redis_sock->user + !!redis_sock->pass, "AUTH"); if (redis_sock->user) redis_cmd_append_sstr_zstr(str, redis_sock->user); redis_cmd_append_sstr_zstr(str, redis_sock->pass); /* We appended a command */ return 1; } PHP_REDIS_API void redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass) { /* Release existing user/pass */ redis_sock_free_auth(redis_sock); /* Set new user/pass */ redis_sock->user = user ? zend_string_copy(user) : NULL; redis_sock->pass = pass ? zend_string_copy(pass) : NULL; } PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv) { zend_string *user, *pass; if (redis_extract_auth_info(zv, &user, &pass) == FAILURE) return; redis_sock_set_auth(redis_sock, user, pass); if (user) zend_string_release(user); if (pass) zend_string_release(pass); } PHP_REDIS_API void redis_sock_free_auth(RedisSock *redis_sock) { if (redis_sock->user) { zend_string_release(redis_sock->user); redis_sock->user = NULL; } if (redis_sock->pass) { zend_string_release(redis_sock->pass); redis_sock->pass = NULL; } } PHP_REDIS_API char * redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen) { char *cmd; /* AUTH requires at least a password */ if (redis_sock->pass == NULL) return NULL; if (redis_sock->user) { *cmdlen = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "SS", redis_sock->user, redis_sock->pass); } else { *cmdlen = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "S", redis_sock->pass); } return cmd; } /* Send Redis AUTH and process response */ PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) { char *cmd, inbuf[4096]; int cmdlen; size_t len; if ((cmd = redis_sock_auth_cmd(redis_sock, &cmdlen)) == NULL) return SUCCESS; if (redis_sock_write(redis_sock, cmd, cmdlen) < 0) { efree(cmd); return FAILURE; } efree(cmd); if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "+OK", 3)) { return FAILURE; } return SUCCESS; } /* Helper function and macro to test a RedisSock error prefix. */ #define REDIS_SOCK_ERRCMP_STATIC(rs, s) redis_sock_errcmp(rs, s, sizeof(s)-1) static int redis_sock_errcmp(RedisSock *redis_sock, const char *err, size_t errlen) { return ZSTR_LEN(redis_sock->err) >= errlen && memcmp(ZSTR_VAL(redis_sock->err), err, errlen) == 0; } /* Helper function that will throw an exception for a small number of ERR codes * returned by Redis. Typically we just return FALSE to the caller in the event * of an ERROR reply, but for the following error types: * 1) MASTERDOWN * 2) AUTH * 3) LOADING */ static void redis_error_throw(RedisSock *redis_sock) { /* Short circuit if we have no redis_sock or any error */ if (redis_sock == NULL || redis_sock->err == NULL) return; /* Redis 6 decided to add 'ERR AUTH' which has a normal 'ERR' prefix * but is actually an authentication error that we will want to throw * an exception for, so just short circuit if this is any other 'ERR' * prefixed error. */ if (REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR") && !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR AUTH")) return; /* We may want to flip this logic and check for MASTERDOWN, AUTH, * and LOADING but that may have side effects (esp for things like * Disque) */ if (!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOSCRIPT") && !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOQUORUM") && !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGOODSLAVE") && !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "WRONGTYPE") && !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "BUSYGROUP") && !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGROUP")) { REDIS_THROW_EXCEPTION(ZSTR_VAL(redis_sock->err), 0); } } static int read_mbulk_header(RedisSock *redis_sock, int *nelem) { char line[4096]; size_t len; /* Throws exception on failure */ if (redis_sock_gets(redis_sock, line, sizeof(line) - 1, &len) < 0) { return FAILURE; } if (*line != TYPE_MULTIBULK) { if (*line == TYPE_ERR) { redis_sock_set_err(redis_sock, line + 1, len - 1); } return FAILURE; } *nelem = atoi(line + 1); return SUCCESS; } PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) { unsigned int retry_index; char *errmsg; if (!redis_sock || !redis_sock->stream || redis_sock->status == REDIS_SOCK_STATUS_FAILED) { if (!no_throw) { REDIS_THROW_EXCEPTION( "Connection closed", 0); } return -1; } /* NOITCE: set errno = 0 here * * There is a bug in php socket stream to check liveness of a connection: * if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) { * alive = 0; * } * If last errno is EWOULDBLOCK and recv returns 0 because of connection closed, alive would not be * set to 0. However, the connection is close indeed. The php_stream_eof is not reliable. This will * cause a "read error on connection" exception when use a closed persistent connection. * * We work around this by set errno = 0 first. * * Bug fix of php: https://github.com/php/php-src/pull/1456 * */ errno = 0; if (php_stream_eof(redis_sock->stream) == 0) { /* Success */ return 0; } else if (redis_sock->mode == MULTI || redis_sock->watching) { errmsg = "Connection lost and socket is in MULTI/watching mode"; } else { errmsg = "Connection lost"; redis_backoff_reset(&redis_sock->backoff); for (retry_index = 0; !no_retry && retry_index < redis_sock->max_retries; ++retry_index) { /* close existing stream before reconnecting */ if (redis_sock->stream) { /* reconnect no need to reset mode, it will cause pipeline mode socket exception */ redis_sock_disconnect(redis_sock, 1, 0); } /* Sleep based on our backoff algorithm */ zend_ulong delay = redis_backoff_compute(&redis_sock->backoff, retry_index); if (delay != 0) usleep(delay); /* reconnect */ if (redis_sock_connect(redis_sock) == 0) { /* check for EOF again. */ errno = 0; if (php_stream_eof(redis_sock->stream) == 0) { if (redis_sock_auth(redis_sock) != SUCCESS) { errmsg = "AUTH failed while reconnecting"; break; } redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; /* If we're using a non-zero db, reselect it */ if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) { errmsg = "SELECT failed while reconnecting"; break; } redis_sock->status = REDIS_SOCK_STATUS_READY; /* Success */ return 0; } } } } /* close stream and mark socket as failed */ redis_sock_disconnect(redis_sock, 1, 1); redis_sock->status = REDIS_SOCK_STATUS_FAILED; if (!no_throw) { REDIS_THROW_EXCEPTION( errmsg, 0); } return -1; } PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, zend_long *iter) { REDIS_REPLY_TYPE reply_type; long reply_info; char err[4096], *p_iter; size_t errlen; /* Our response should have two multibulk replies */ if(redis_read_reply_type(redis_sock, &reply_type, &reply_info)<0 || reply_type != TYPE_MULTIBULK || reply_info != 2) { if (reply_type == TYPE_ERR) { if (redis_sock_gets(redis_sock, err, sizeof(err), &errlen) == 0) { redis_sock_set_err(redis_sock, err, errlen); } } return -1; } /* The BULK response iterator */ if(redis_read_reply_type(redis_sock, &reply_type, &reply_info)<0 || reply_type != TYPE_BULK) { return -1; } /* Attempt to read the iterator */ if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info))) { return -1; } /* Push the iterator out to the caller */ *iter = atol(p_iter); efree(p_iter); /* Read our actual keys/members/etc differently depending on what kind of scan command this is. They all come back in slightly different ways */ switch(type) { case TYPE_SCAN: return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_SSCAN: return redis_sock_read_multibulk_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_ZSCAN: return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_HSCAN: return redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); default: return -1; } } PHP_REDIS_API int redis_pubsub_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { return redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } static void ht_free_subs(zval *data) { efree(Z_PTR_P(data)); } PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { HashTable *subs; subscribeCallback *cb; subscribeContext *sctx = (subscribeContext*)ctx; zval *z_tmp, z_resp; int i; ALLOC_HASHTABLE(subs); zend_hash_init(subs, 0, NULL, ht_free_subs, 0); // Consume response(s) from subscribe, which will vary on argc while(sctx->argc--) { ZVAL_NULL(&z_resp); if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp)) { goto error; } // We'll need to find the command response if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_resp), 0)) == NULL) { goto error; } // Make sure the command response matches the command we called if(strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) !=0) { goto error; } if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL) { goto error; } zend_hash_str_update_mem(subs, Z_STRVAL_P(z_tmp), Z_STRLEN_P(z_tmp), &sctx->cb, sizeof(sctx->cb)); zval_dtor(&z_resp); } if (strcasecmp(sctx->kw, "ssubscribe") == 0) { i = REDIS_SSUBSCRIBE_IDX; } else if (strcasecmp(sctx->kw, "psubscribe") == 0) { i = REDIS_PSUBSCRIBE_IDX; } else { i = REDIS_SUBSCRIBE_IDX; } efree(sctx); if (redis_sock->subs[i]) { zend_string *zkey; ZEND_HASH_FOREACH_STR_KEY_PTR(subs, zkey, cb) { zend_hash_update_mem(redis_sock->subs[i], zkey, cb, sizeof(*cb)); } ZEND_HASH_FOREACH_END(); zend_hash_destroy(subs); efree(subs); RETVAL_TRUE; return SUCCESS; } redis_sock->subs[i] = subs; /* Multibulk response, {[pattern], type, channel, payload } */ while (redis_sock->subs[i]) { zval z_ret, z_args[4], *z_type, *z_chan, *z_pat = NULL, *z_data; int tab_idx = 1, is_pmsg = 0; HashTable *ht_tab; zend_string *zs; ZVAL_NULL(&z_resp); if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp)) { goto failure; } ht_tab = Z_ARRVAL(z_resp); if ((z_type = zend_hash_index_find(ht_tab, 0)) == NULL || Z_TYPE_P(z_type) != IS_STRING ) { goto failure; } // Check for message or pmessage if (zend_string_equals_literal_ci(Z_STR_P(z_type), "message") || zend_string_equals_literal_ci(Z_STR_P(z_type), "pmessage") || zend_string_equals_literal_ci(Z_STR_P(z_type), "smessage") ) { is_pmsg = *Z_STRVAL_P(z_type)=='p'; } else { zval_dtor(&z_resp); continue; } // Extract pattern if it's a pmessage if (is_pmsg) { z_pat = zend_hash_index_find(ht_tab, tab_idx++); if (z_pat == NULL || Z_TYPE_P(z_pat) != IS_STRING) goto failure; } /* Extract channel */ z_chan = zend_hash_index_find(ht_tab, tab_idx++); if (z_chan == NULL || Z_TYPE_P(z_chan) != IS_STRING) goto failure; /* Finally, extract data */ z_data = zend_hash_index_find(ht_tab, tab_idx++); if (z_data == NULL) goto failure; /* Find our callback, either by channel or pattern string */ zs = z_pat != NULL ? Z_STR_P(z_pat) : Z_STR_P(z_chan); if ((cb = zend_hash_find_ptr(redis_sock->subs[i], zs)) == NULL) goto failure; // Different args for SUBSCRIBE and PSUBSCRIBE z_args[0] = *getThis(); if(is_pmsg) { z_args[1] = *z_pat; z_args[2] = *z_chan; z_args[3] = *z_data; } else { z_args[1] = *z_chan; z_args[2] = *z_data; } // Set arg count cb->fci.param_count = tab_idx; cb->fci.retval = &z_ret; cb->fci.params = z_args; // Execute callback if (zend_call_function(&cb->fci, &cb->fci_cache) != SUCCESS) { goto failure; } // If we have a return value free it zval_ptr_dtor(&z_ret); zval_dtor(&z_resp); } RETVAL_TRUE; return SUCCESS; // This is an error state, clean up error: efree(sctx); zend_hash_destroy(subs); efree(subs); failure: zval_dtor(&z_resp); RETVAL_FALSE; return FAILURE; } PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; zval *z_chan, z_ret, z_resp; int i; if (strcasecmp(sctx->kw, "sunsubscribe") == 0) { i = REDIS_SSUBSCRIBE_IDX; } else if (strcasecmp(sctx->kw, "punsubscribe") == 0) { i = REDIS_PSUBSCRIBE_IDX; } else { i = REDIS_SUBSCRIBE_IDX; } if (!sctx->argc && redis_sock->subs[i]) { sctx->argc = zend_hash_num_elements(redis_sock->subs[i]); } array_init(&z_ret); while (sctx->argc--) { ZVAL_NULL(&z_resp); if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp) || (z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL ) { efree(sctx); zval_dtor(&z_resp); zval_dtor(&z_ret); RETVAL_FALSE; return FAILURE; } if (!redis_sock->subs[i] || !zend_hash_str_exists(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan)) ) { add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 0); } else { zend_hash_str_del(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan)); add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 1); } zval_dtor(&z_resp); } efree(sctx); if (redis_sock->subs[i] && !zend_hash_num_elements(redis_sock->subs[i])) { zend_hash_destroy(redis_sock->subs[i]); efree(redis_sock->subs[i]); redis_sock->subs[i] = NULL; } RETVAL_ZVAL(&z_ret, 0, 1); return SUCCESS; } PHP_REDIS_API zval * redis_sock_read_multibulk_reply_zval(RedisSock *redis_sock, zval *z_tab) { int numElems; if (read_mbulk_header(redis_sock, &numElems) < 0) { ZVAL_NULL(z_tab); return NULL; } array_init(z_tab); redis_mbulk_reply_loop(redis_sock, z_tab, numElems, UNSERIALIZE_ALL); return z_tab; } /** * redis_sock_read_bulk_reply */ PHP_REDIS_API char * redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes) { int offset = 0, nbytes; char *reply; ssize_t got; if (-1 == bytes || -1 == redis_check_eof(redis_sock, 1, 0)) { return NULL; } /* + 2 for \r\n */ nbytes = bytes + 2; /* Allocate memory for string */ reply = emalloc(nbytes); /* Consume bulk string */ while (offset < nbytes) { got = redis_sock_read_raw(redis_sock, reply + offset, nbytes - offset); if (got < 0 || (got == 0 && php_stream_eof(redis_sock->stream))) break; offset += got; } /* Protect against reading too few bytes */ if (offset < nbytes) { /* Error or EOF */ REDIS_THROW_EXCEPTION("socket error on read socket", 0); efree(reply); return NULL; } /* Null terminate reply string */ reply[bytes] = '\0'; return reply; } /** * redis_sock_read */ PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len) { char inbuf[4096]; size_t len; *buf_len = 0; if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return NULL; } switch(inbuf[0]) { case '-': redis_sock_set_err(redis_sock, inbuf+1, len); /* Filter our ERROR through the few that should actually throw */ redis_error_throw(redis_sock); return NULL; case '$': *buf_len = atoi(inbuf + 1); return redis_sock_read_bulk_reply(redis_sock, *buf_len); case '*': /* For null multi-bulk replies (like timeouts from brpoplpush): */ if(memcmp(inbuf + 1, "-1", 2) == 0) { return NULL; } REDIS_FALLTHROUGH; case '+': case ':': /* Single Line Reply */ /* +OK or :123 */ if (len > 1) { *buf_len = len; return estrndup(inbuf, *buf_len); } REDIS_FALLTHROUGH; default: zend_throw_exception_ex(redis_exception_ce, 0, "protocol error, got '%c' as reply type byte\n", inbuf[0] ); } return NULL; } /* A simple union to store the various arg types we might handle in our * redis_spprintf command formatting function */ union resparg { char *str; zend_string *zstr; zval *zv; int ival; long lval; double dval; }; static zend_string *redis_hash_auth(zend_string *user, zend_string *pass) { zend_string *algo, *hex; smart_str salted = {0}; const php_hash_ops *ops; unsigned char *digest; void *ctx; /* No op if there is not username/password */ if (user == NULL && pass == NULL) return NULL; /* Theoretically inpossible but check anyway */ algo = zend_string_init("sha256", sizeof("sha256") - 1, 0); if ((ops = redis_hash_fetch_ops(algo)) == NULL) { zend_string_release(algo); return NULL; } /* Hash username + password with our salt global */ smart_str_alloc(&salted, 256, 0); if (user) smart_str_append_ex(&salted, user, 0); if (pass) smart_str_append_ex(&salted, pass, 0); smart_str_appendl_ex(&salted, REDIS_G(salt), sizeof(REDIS_G(salt)), 0); ctx = emalloc(ops->context_size); #if PHP_VERSION_ID >= 80100 ops->hash_init(ctx,NULL); #else ops->hash_init(ctx); #endif ops->hash_update(ctx, (const unsigned char *)ZSTR_VAL(salted.s), ZSTR_LEN(salted.s)); digest = emalloc(ops->digest_size); ops->hash_final(digest, ctx); efree(ctx); hex = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); php_hash_bin2hex(ZSTR_VAL(hex), digest, ops->digest_size); ZSTR_VAL(hex)[2 * ops->digest_size] = 0; efree(digest); zend_string_release(algo); smart_str_free(&salted); return hex; } static void append_auth_hash(smart_str *dst, zend_string *user, zend_string *pass) { zend_string *s; if ((s = redis_hash_auth(user, pass)) != NULL) { smart_str_appendc(dst, ':'); smart_str_append_ex(dst, s, 0); zend_string_release(s); } } /* A printf like function to generate our connection pool hash value. */ PHP_REDIS_API zend_string * redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...) { smart_str str = {0}; smart_str_alloc(&str, 128, 0); /* We always include phpredis_: */ smart_str_appendl(&str, "phpredis_", sizeof("phpredis_") - 1); smart_str_append_ex(&str, redis_sock->host, 0); smart_str_appendc(&str, ':'); smart_str_append_long(&str, (zend_long)redis_sock->port); /* Short circuit if we don't have a pattern */ if (fmt == NULL) { smart_str_0(&str); return str.s; } while (*fmt) { switch (*fmt) { case 'i': if (redis_sock->persistent_id) { smart_str_appendc(&str, ':'); smart_str_append_ex(&str, redis_sock->persistent_id, 0); } break; case 'u': smart_str_appendc(&str, ':'); if (redis_sock->user) { smart_str_append_ex(&str, redis_sock->user, 0); } break; case 'p': append_auth_hash(&str, NULL, redis_sock->pass); break; case 'a': append_auth_hash(&str, redis_sock->user, redis_sock->pass); break; default: /* Maybe issue a php_error_docref? */ break; } fmt++; } smart_str_0(&str); return str.s; } /* A printf like method to construct a Redis RESP command. It has been extended * to take a few different format specifiers that are convenient to phpredis. * * s - C string followed by length as a * S - Pointer to a zend_string * k - Same as 's' but the value will be prefixed if phpredis is set up do do * that and the working slot will be set if it has been passed. * v - A z_val which will be serialized if phpredis is configured to serialize. * f - A double value * F - Alias to 'f' * i - An integer * d - Alias to 'i' * l - A long * L - Alias to 'l' */ PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...) { smart_string cmd = {0}; va_list ap; union resparg arg; char *dup; int argfree; size_t arglen; va_start(ap, fmt); /* Header */ redis_cmd_init_sstr(&cmd, strlen(fmt), kw, strlen(kw)); while (*fmt) { switch (*fmt) { case 's': arg.str = va_arg(ap, char*); arglen = va_arg(ap, size_t); redis_cmd_append_sstr(&cmd, arg.str, arglen); break; case 'S': arg.zstr = va_arg(ap, zend_string*); redis_cmd_append_sstr(&cmd, ZSTR_VAL(arg.zstr), ZSTR_LEN(arg.zstr)); break; case 'k': arg.str = va_arg(ap, char*); arglen = va_arg(ap, size_t); argfree = redis_key_prefix(redis_sock, &arg.str, &arglen); redis_cmd_append_sstr(&cmd, arg.str, arglen); if (slot) *slot = cluster_hash_key(arg.str, arglen); if (argfree) efree(arg.str); break; case 'v': arg.zv = va_arg(ap, zval*); argfree = redis_pack(redis_sock, arg.zv, &dup, &arglen); redis_cmd_append_sstr(&cmd, dup, arglen); if (argfree) efree(dup); break; case 'f': case 'F': arg.dval = va_arg(ap, double); redis_cmd_append_sstr_dbl(&cmd, arg.dval); break; case 'i': case 'd': arg.ival = va_arg(ap, int); redis_cmd_append_sstr_int(&cmd, arg.ival); break; case 'l': case 'L': arg.lval = va_arg(ap, long); redis_cmd_append_sstr_long(&cmd, arg.lval); break; } fmt++; } /* varargs cleanup */ va_end(ap); /* Null terminate */ smart_string_0(&cmd); /* Push command string, return length */ *ret = cmd.c; return cmd.len; } /* * Given a smart string, number of arguments, a keyword, and the length of the keyword * initialize our smart string with the proper Redis header for the command to follow */ int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len) { smart_string_appendc(str, '*'); smart_string_append_long(str, num_args + 1); smart_string_appendl(str, _NL, sizeof(_NL) -1); smart_string_appendc(str, '$'); smart_string_append_long(str, keyword_len); smart_string_appendl(str, _NL, sizeof(_NL) - 1); smart_string_appendl(str, keyword, keyword_len); smart_string_appendl(str, _NL, sizeof(_NL) - 1); return str->len; } /* * Append a command sequence to a smart_string */ int redis_cmd_append_sstr(smart_string *str, char *append, int append_len) { smart_string_appendc(str, '$'); smart_string_append_long(str, append_len); smart_string_appendl(str, _NL, sizeof(_NL) - 1); smart_string_appendl(str, append, append_len); smart_string_appendl(str, _NL, sizeof(_NL) - 1); /* Return our new length */ return str->len; } /* * Append an integer to a smart string command */ int redis_cmd_append_sstr_int(smart_string *str, int append) { char int_buf[32]; int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); return redis_cmd_append_sstr(str, int_buf, int_len); } /* * Append a long to a smart string command */ int redis_cmd_append_sstr_long(smart_string *str, long append) { char long_buf[32]; int long_len = snprintf(long_buf, sizeof(long_buf), "%ld", append); return redis_cmd_append_sstr(str, long_buf, long_len); } /* * Append a 64-bit integer to our command */ int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) { char nbuf[64]; int len = snprintf(nbuf, sizeof(nbuf), "%" PRId64, append); return redis_cmd_append_sstr(str, nbuf, len); } /* * Append a double to a smart string command */ int redis_cmd_append_sstr_dbl(smart_string *str, double value) { char tmp[64], *p; int len; /* Convert to string */ len = snprintf(tmp, sizeof(tmp), "%.17g", value); /* snprintf depends on locale, replace comma with point */ if ((p = strchr(tmp, ',')) != NULL) *p = '.'; // Append the string return redis_cmd_append_sstr(str, tmp, len); } /* Append a zval to a redis command. If redis_sock is passed as non-null we will * the value may be serialized, if we're configured to do that. */ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock) { int valfree, retval; zend_string *zstr; size_t vallen; char *val; if (redis_sock != NULL) { valfree = redis_pack(redis_sock, z, &val, &vallen); retval = redis_cmd_append_sstr(str, val, vallen); if (valfree) efree(val); } else { zstr = zval_get_string(z); retval = redis_cmd_append_sstr_zstr(str, zstr); zend_string_release(zstr); } return retval; } int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr) { return redis_cmd_append_sstr(str, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); } /* Append a string key to a redis command. This function takes care of prefixing the key * for the caller and setting the slot argument if it is passed non null */ int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot) { int valfree, retval; valfree = redis_key_prefix(redis_sock, &key, &len); if (slot) *slot = cluster_hash_key(key, len); retval = redis_cmd_append_sstr(str, key, len); if (valfree) efree(key); return retval; } int redis_cmd_append_sstr_key_zstr(smart_string *dst, zend_string *key, RedisSock *redis_sock, short *slot) { return redis_cmd_append_sstr_key(dst, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); } int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot) { zend_string *key; int res; key = zval_get_string(zv); res = redis_cmd_append_sstr_key_zstr(dst, key, redis_sock, slot); zend_string_release(key); return res; } int redis_cmd_append_sstr_key_long(smart_string *dst, zend_long lval, RedisSock *redis_sock, short *slot) { char buf[64]; size_t len; int res; len = snprintf(buf, sizeof(buf), ZEND_LONG_FMT, lval); res = redis_cmd_append_sstr_key(dst, buf, len, redis_sock, slot); return res; } /* Append an array key to a redis smart string command. This function * handles the boilerplate conditionals around string or integer keys */ int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx) { char *arg, kbuf[128]; int len; if (kstr) { len = ZSTR_LEN(kstr); arg = ZSTR_VAL(kstr); } else { len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); arg = (char*)kbuf; } return redis_cmd_append_sstr(cmd, arg, len); } PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; double ret; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } ret = atof(response); efree(response); if (IS_ATOMIC(redis_sock)) { RETVAL_DOUBLE(ret); } else { add_next_index_double(z_tab, ret); } return SUCCESS; } PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; long l; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } if (strncmp(response, "+string", 7) == 0) { l = REDIS_STRING; } else if (strncmp(response, "+set", 4) == 0){ l = REDIS_SET; } else if (strncmp(response, "+list", 5) == 0){ l = REDIS_LIST; } else if (strncmp(response, "+zset", 5) == 0){ l = REDIS_ZSET; } else if (strncmp(response, "+hash", 5) == 0){ l = REDIS_HASH; } else if (strncmp(response, "+stream", 7) == 0) { l = REDIS_STREAM; } else { l = REDIS_NOT_FOUND; } efree(response); if (IS_ATOMIC(redis_sock)) { RETVAL_LONG(l); } else { add_next_index_long(z_tab, l); } return SUCCESS; } PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { FailableResultCallback cb = ctx; ZEND_ASSERT(cb == redis_boolean_response || cb == redis_mbulk_reply_zipped_raw); return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); } PHP_REDIS_API int redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { FailableResultCallback cb; /* Whether or not we have WITHSCORES */ ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); cb = ctx ? redis_mbulk_reply_zipped_keys_dbl : redis_sock_read_multibulk_reply; return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); } PHP_REDIS_API int redis_srandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { FailableResultCallback cb; /* Whether or not we have a COUNT argument */ ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); cb = ctx ? redis_sock_read_multibulk_reply : redis_string_response; return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); } PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; zval z_ret; /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { RETVAL_FALSE; return FAILURE; } /* Parse it into a zval array */ ZVAL_UNDEF(&z_ret); redis_parse_info_response(response, &z_ret); /* Free source response */ efree(response); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } return SUCCESS; } PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret) { char *p1, *s1 = NULL; ZVAL_FALSE(z_ret); if ((p1 = php_strtok_r(response, _NL, &s1)) != NULL) { array_init(z_ret); do { if (*p1 == '#') continue; char *p; zend_uchar type; zend_long lval; double dval; if ((p = strchr(p1, ':')) != NULL) { type = is_numeric_string(p + 1, strlen(p + 1), &lval, &dval, 0); switch (type) { case IS_LONG: add_assoc_long_ex(z_ret, p1, p - p1, lval); break; case IS_DOUBLE: add_assoc_double_ex(z_ret, p1, p - p1, dval); break; default: add_assoc_string_ex(z_ret, p1, p - p1, p + 1); } } else { add_next_index_string(z_ret, p1); } } while ((p1 = php_strtok_r(NULL, _NL, &s1)) != NULL); } } static void redis_parse_client_info(char *info, zval *z_ret) { char *p1, *s1 = NULL; ZVAL_FALSE(z_ret); if ((p1 = php_strtok_r(info, " ", &s1)) != NULL) { array_init(z_ret); do { char *p; zend_uchar type; zend_long lval; double dval; if ((p = strchr(p1, '=')) != NULL) { type = is_numeric_string(p + 1, strlen(p + 1), &lval, &dval, 0); switch (type) { case IS_LONG: add_assoc_long_ex(z_ret, p1, p - p1, lval); break; case IS_DOUBLE: add_assoc_double_ex(z_ret, p1, p - p1, dval); break; default: add_assoc_string_ex(z_ret, p1, p - p1, p + 1); } } else { add_next_index_string(z_ret, p1); } } while ((p1 = php_strtok_r(NULL, " ", &s1)) != NULL); } } static int redis_client_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *resp; int resp_len; zval z_ret; /* Make sure we can read the bulk response from Redis */ if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) { RETVAL_FALSE; return FAILURE; } /* Parse it out */ redis_parse_client_info(resp, &z_ret); /* Free our response */ efree(resp); /* Return or append depending if we're atomic */ if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } return SUCCESS; } /* * Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code * to handle. */ PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *resp; int resp_len; zval z_ret; /* Make sure we can read the bulk response from Redis */ if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) { RETVAL_FALSE; return FAILURE; } else if (resp_len > 0) { /* Parse it out */ redis_parse_client_list_response(resp, &z_ret); } else { array_init(&z_ret); } /* Free our response */ efree(resp); /* Return or append depending if we're atomic */ if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } return SUCCESS; } PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret) { char *p, *s = NULL; ZVAL_FALSE(z_ret); if ((p = php_strtok_r(response, _NL, &s)) != NULL) { array_init(z_ret); do { zval z_sub; redis_parse_client_info(p, &z_sub); add_next_index_zval(z_ret, &z_sub); } while ((p = php_strtok_r(NULL, _NL, &s)) != NULL); } } PHP_REDIS_API int redis_zadd_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { FailableResultCallback cb; ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); cb = ctx ? redis_bulk_double_response : redis_long_response; return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { return redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } PHP_REDIS_API int redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); if (ctx == PHPREDIS_CTX_PTR) { return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } } PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx) { char inbuf[4096]; size_t len; int i; if (ctx == NULL) { if (reply_type != TYPE_INT && reply_type != TYPE_BULK) return FAILURE; if (elements > -1) { ZVAL_LONG(zdst, elements); } else { REDIS_ZVAL_NULL(redis_sock, zdst); } } else if (ctx == PHPREDIS_CTX_PTR) { if (reply_type != TYPE_MULTIBULK) return FAILURE; array_init(zdst); for (i = 0; i < elements; ++i) { if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) { zval_dtor(zdst); return FAILURE; } add_next_index_long(zdst, atol(inbuf + 1)); } } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } return SUCCESS; } PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024] = {0}; int res = SUCCESS; zval zdst = {0}; size_t len; /* Attempt to read the LPOS response */ if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0 || redis_read_lpos_response(&zdst, redis_sock, *inbuf, atoll(inbuf+1), ctx) < 0) { ZVAL_FALSE(&zdst); res = FAILURE; } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&zdst, 0, 0); } else { add_next_index_zval(z_tab, &zdst); } return res; } PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL) < 0) return FAILURE; redis_sock->dbNumber = (long)(uintptr_t)ctx; return SUCCESS; } PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) { char *response; int response_len; zend_bool ret = 0; if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) { ret = (*response == '+'); efree(response); } if (ret && success_callback != NULL) { success_callback(redis_sock); } if (IS_ATOMIC(redis_sock)) { RETVAL_BOOL(ret); } else { add_next_index_bool(z_tab, ret); } return ret ? SUCCESS : FAILURE; } PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, NULL); } PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * z_tab, void *ctx) { char *response; int response_len; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL || *response != TYPE_INT) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } if (response) efree(response); return FAILURE; } int64_t ret = phpredis_atoi64(response + 1); if (IS_ATOMIC(redis_sock)) { if (ret > LONG_MAX) { /* overflow */ RETVAL_STRINGL(response + 1, response_len - 1); } else { RETVAL_LONG((long)ret); } } else { if (ret > LONG_MAX) { /* overflow */ add_next_index_stringl(z_tab, response + 1, response_len - 1); } else { add_next_index_long(z_tab, (long)ret); } } efree(response); return SUCCESS; } /* Helper method to convert [key, value, key, value] into [key => value, * key => value] when returning data to the caller. Depending on our decode * flag we'll convert the value data types */ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int decode) { zval z_ret, z_sub; HashTable *keytable; array_init(&z_ret); keytable = Z_ARRVAL_P(z_tab); for(zend_hash_internal_pointer_reset(keytable); zend_hash_has_more_elements(keytable) == SUCCESS; zend_hash_move_forward(keytable)) { zval *z_key_p, *z_value_p; if ((z_key_p = zend_hash_get_current_data(keytable)) == NULL) { continue; /* this should never happen, according to the PHP people. */ } /* get current value, a key */ zend_string *hkey = zval_get_string(z_key_p); /* move forward */ zend_hash_move_forward(keytable); /* fetch again */ if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) { zend_string_release(hkey); continue; /* this should never happen, according to the PHP people. */ } /* get current value, a hash value now. */ char *hval = Z_STRVAL_P(z_value_p); /* Decode the score depending on flag */ if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) { add_assoc_long_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atoi(hval+1)); } else if (decode == SCORE_DECODE_DOUBLE) { add_assoc_double_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atof(hval)); } else { ZVAL_ZVAL(&z_sub, z_value_p, 1, 0); add_assoc_zval_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), &z_sub); } zend_string_release(hkey); } /* replace */ zval_dtor(z_tab); ZVAL_ZVAL(z_tab, &z_ret, 0, 0); } static int array_zip_values_recursive(zval *z_tab) { zend_string *zkey; zval z_ret, z_sub, *zv; array_init(&z_ret); for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(z_tab)); zend_hash_has_more_elements(Z_ARRVAL_P(z_tab)) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(z_tab)) ) { if ((zv = zend_hash_get_current_data(Z_ARRVAL_P(z_tab))) == NULL) { zval_dtor(&z_ret); return FAILURE; } if (Z_TYPE_P(zv) == IS_STRING) { zkey = zval_get_string(zv); zend_hash_move_forward(Z_ARRVAL_P(z_tab)); if ((zv = zend_hash_get_current_data(Z_ARRVAL_P(z_tab))) == NULL) { zend_string_release(zkey); zval_dtor(&z_ret); return FAILURE; } if (Z_TYPE_P(zv) == IS_ARRAY && array_zip_values_recursive(zv) != SUCCESS) { zend_string_release(zkey); zval_dtor(&z_ret); return FAILURE; } ZVAL_ZVAL(&z_sub, zv, 1, 0); add_assoc_zval_ex(&z_ret, ZSTR_VAL(zkey), ZSTR_LEN(zkey), &z_sub); zend_string_release(zkey); } else { if (Z_TYPE_P(zv) == IS_ARRAY && array_zip_values_recursive(zv) != SUCCESS) { zval_dtor(&z_ret); return FAILURE; } ZVAL_ZVAL(&z_sub, zv, 1, 0); add_next_index_zval(&z_ret, &z_sub); } } zval_dtor(z_tab); ZVAL_ZVAL(z_tab, &z_ret, 0, 0); return SUCCESS; } static int redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int unserialize, int decode) { int numElems; if (read_mbulk_header(redis_sock, &numElems) < 0) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } zval z_multi_result; array_init(&z_multi_result); /* pre-allocate array for multi's results. */ /* Grab our key, value, key, value array */ redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize); /* Zip keys and values */ array_zip_values_and_scores(redis_sock, &z_multi_result, decode); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_multi_result, 0, 1); } else { add_next_index_zval(z_tab, &z_multi_result); } return 0; } static int geosearch_cast(zval *zv) { if (Z_TYPE_P(zv) == IS_ARRAY) { zend_hash_apply(Z_ARRVAL_P(zv), geosearch_cast); } else if (Z_TYPE_P(zv) == IS_STRING) { convert_to_double(zv); } return SUCCESS; } PHP_REDIS_API int redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, void *ctx) { int subele, keylen; zval zele = {0}; char *key; ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); if (elements < 0) { REDIS_ZVAL_NULL(redis_sock, zdst); return SUCCESS; } /* Invariant: We should have two elements */ ZEND_ASSERT(elements == 2); array_init(zdst); /* Key name and number of entries */ if ((key = redis_sock_read(redis_sock, &keylen)) == NULL || read_mbulk_header(redis_sock, &elements) < 0 || elements < 0) { if (key) efree(key); goto fail; } add_next_index_stringl(zdst, key, keylen); efree(key); array_init_size(&zele, elements); if (ctx == PHPREDIS_CTX_PTR) { int i; for (i = 0; i < elements; i++) { if (read_mbulk_header(redis_sock, &subele) < 0 || subele != 2) { zval_dtor(&zele); goto fail; } redis_mbulk_reply_loop(redis_sock, &zele, subele, UNSERIALIZE_KEYS); } array_zip_values_and_scores(redis_sock, &zele, SCORE_DECODE_DOUBLE); } else { redis_mbulk_reply_loop(redis_sock, &zele, elements, UNSERIALIZE_ALL); } add_next_index_zval(zdst, &zele); return SUCCESS; fail: zval_dtor(zdst); ZVAL_FALSE(zdst); return FAILURE; } PHP_REDIS_API int redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { int elements, res = SUCCESS; zval zret = {0}; if (read_mbulk_header(redis_sock, &elements) == FAILURE || redis_read_mpop_response(redis_sock, &zret, elements, ctx) == FAILURE) { res = FAILURE; ZVAL_FALSE(&zret); } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&zret, 0, 0); } else { add_next_index_zval(z_tab, &zret); } return res; } #if PHP_VERSION_ID < 80200 static HashTable *zend_array_to_list(HashTable *arr) { zval zret = {0}, *zv; array_init_size(&zret, zend_hash_num_elements(arr)); ZEND_HASH_FOREACH_VAL(arr, zv) { Z_TRY_ADDREF_P(zv); add_next_index_zval(&zret, zv); } ZEND_HASH_FOREACH_END(); return Z_ARRVAL(zret); } #endif PHP_REDIS_API int redis_read_geosearch_response(zval *zdst, RedisSock *redis_sock, long long elements, int with_aux_data) { zval z_multi_result, z_sub, *z_ele, *zv; zend_string *zkey; /* Handle the trivial "empty" result first */ if (elements < 0 && redis_sock->null_mbulk_as_null) { ZVAL_NULL(zdst); return SUCCESS; } array_init(zdst); if (with_aux_data == 0) { redis_mbulk_reply_loop(redis_sock, zdst, elements, UNSERIALIZE_NONE); } else { array_init(&z_multi_result); redis_read_multibulk_recursive(redis_sock, elements, 0, &z_multi_result); ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_multi_result), z_ele) { // The first item in the sub-array is always the name of the returned item zv = zend_hash_index_find(Z_ARRVAL_P(z_ele), 0); zkey = zval_get_string(zv); zend_hash_index_del(Z_ARRVAL_P(z_ele), 0); // The other information is returned in the following order as successive // elements of the sub-array: distance, geohash, coordinates zend_hash_apply(Z_ARRVAL_P(z_ele), geosearch_cast); // Reindex elements so they start at zero */ ZVAL_ARR(&z_sub, zend_array_to_list(Z_ARRVAL_P(z_ele))); add_assoc_zval_ex(zdst, ZSTR_VAL(zkey), ZSTR_LEN(zkey), &z_sub); zend_string_release(zkey); } ZEND_HASH_FOREACH_END(); // Cleanup zval_dtor(&z_multi_result); } return SUCCESS; } PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { zval zret = {0}; int elements; if (read_mbulk_header(redis_sock, &elements) < 0 || redis_read_geosearch_response(&zret, redis_sock, elements, ctx != NULL) < 0) { ZVAL_FALSE(&zret); } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&zret, 0, 1); } else { add_next_index_zval(z_tab, &zret); } return SUCCESS; } static int redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { int numElems; zval z_ret; if (read_mbulk_header(redis_sock, &numElems) < 0) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } array_init(&z_ret); redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret); array_zip_values_and_scores(redis_sock, &z_ret, 0); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } return SUCCESS; } PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_client_info_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 2) { return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 3) { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 4) { return redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } static int redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { int numElems; zval z_ret; if (read_mbulk_header(redis_sock, &numElems) < 0) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } array_init(&z_ret); redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret); array_zip_values_recursive(&z_ret); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } return SUCCESS; } PHP_REDIS_API int redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { return redis_function_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } static int redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { int numElems; zval z_ret; if (read_mbulk_header(redis_sock, &numElems) < 0) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } array_init(&z_ret); redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } return SUCCESS; } PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_command_info_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } /* Helper function to consume Redis stream message data. This is useful for * multiple stream callers (e.g. XREAD[GROUP], and X[REV]RANGE handlers). */ PHP_REDIS_API int redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret ) { zval z_message; int i, mhdr, fields; char *id = NULL; int idlen; /* Iterate over each message */ for (i = 0; i < count; i++) { /* Consume inner multi-bulk header, message ID itself and finally * the multi-bulk header for field and values */ if ((read_mbulk_header(redis_sock, &mhdr) < 0 || mhdr != 2) || ((id = redis_sock_read(redis_sock, &idlen)) == NULL) || (read_mbulk_header(redis_sock, &fields) < 0 || (fields > 0 && fields % 2 != 0))) { if (id) efree(id); return -1; } if (fields < 0) { add_assoc_null_ex(z_ret, id, idlen); } else { array_init(&z_message); redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS); array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE); add_assoc_zval_ex(z_ret, id, idlen, &z_message); } efree(id); } return 0; } PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { zval z_messages; int messages; array_init(&z_messages); if (read_mbulk_header(redis_sock, &messages) < 0 || redis_read_stream_messages(redis_sock, messages, &z_messages) < 0) { zval_dtor(&z_messages); if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return -1; } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_messages, 0, 1); } else { add_next_index_zval(z_tab, &z_messages); } return 0; } PHP_REDIS_API int redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_streams ) { zval z_messages; int i, shdr, messages; char *id = NULL; int idlen; for (i = 0; i < count; i++) { if ((read_mbulk_header(redis_sock, &shdr) < 0 || shdr != 2) || (id = redis_sock_read(redis_sock, &idlen)) == NULL || read_mbulk_header(redis_sock, &messages) < 0) { if (id) efree(id); return -1; } array_init(&z_messages); if (redis_read_stream_messages(redis_sock, messages, &z_messages) < 0) goto failure; add_assoc_zval_ex(z_streams, id, idlen, &z_messages); efree(id); } return 0; failure: efree(id); zval_dtor(&z_messages); return -1; } PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { zval z_rv; int streams; if (read_mbulk_header(redis_sock, &streams) < 0) goto failure; if (streams == -1 && redis_sock->null_mbulk_as_null) { ZVAL_NULL(&z_rv); } else { array_init(&z_rv); if (redis_read_stream_messages_multi(redis_sock, streams, &z_rv) < 0) goto cleanup; } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_rv, 0, 1); } else { add_next_index_zval(z_tab, &z_rv); } return 0; cleanup: zval_dtor(&z_rv); failure: if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return -1; } /* A helper method to read X[AUTO]CLAIM messages into an array. */ static int redis_read_xclaim_ids(RedisSock *redis_sock, int count, zval *rv) { zval z_msg; REDIS_REPLY_TYPE type; char *id = NULL; int i, fields, idlen; long li; for (i = 0; i < count; i++) { id = NULL; /* Consume inner reply type */ if (redis_read_reply_type(redis_sock, &type, &li) < 0 || (type != TYPE_BULK && type != TYPE_MULTIBULK) || (type == TYPE_BULK && li <= 0)) return -1; /* TYPE_BULK is the JUSTID variant, otherwise it's standard xclaim response */ if (type == TYPE_BULK) { if ((id = redis_sock_read_bulk_reply(redis_sock, (size_t)li)) == NULL) return -1; add_next_index_stringl(rv, id, li); efree(id); } else { if ((li != 2 || (id = redis_sock_read(redis_sock, &idlen)) == NULL) || (read_mbulk_header(redis_sock, &fields) < 0 || fields % 2 != 0)) { if (id) efree(id); return -1; } array_init(&z_msg); redis_mbulk_reply_loop(redis_sock, &z_msg, fields, UNSERIALIZE_VALS); array_zip_values_and_scores(redis_sock, &z_msg, SCORE_DECODE_NONE); add_assoc_zval_ex(rv, id, idlen, &z_msg); efree(id); } } return 0; } /* Read an X[AUTO]CLAIM reply having already consumed the reply-type byte. */ PHP_REDIS_API int redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zval *rv) { REDIS_REPLY_TYPE type; zval z_msgs = {0}; char *id = NULL; long id_len = 0; int messages; ZEND_ASSERT(!is_xautoclaim || count == 3); ZVAL_UNDEF(rv); /* If this is XAUTOCLAIM consume the BULK ID and then the actual number of IDs. * Otherwise, our 'count' argument is the number of IDs. */ if (is_xautoclaim) { if (redis_read_reply_type(redis_sock, &type, &id_len) < 0 || type != TYPE_BULK) goto failure; if ((id = redis_sock_read_bulk_reply(redis_sock, id_len)) == NULL) goto failure; if (read_mbulk_header(redis_sock, &messages) < 0) goto failure; } else { messages = count; } array_init(&z_msgs); if (redis_read_xclaim_ids(redis_sock, messages, &z_msgs) < 0) goto failure; /* If XAUTOCLAIM we now need to consume the final array of message IDs */ if (is_xautoclaim) { zval z_deleted = {0}; if (redis_sock_read_multibulk_reply_zval(redis_sock, &z_deleted) == NULL) goto failure; array_init(rv); // Package up ID, message, and deleted messages in our reply add_next_index_stringl(rv, id, id_len); add_next_index_zval(rv, &z_msgs); add_next_index_zval(rv, &z_deleted); efree(id); } else { // We just want the messages ZVAL_COPY_VALUE(rv, &z_msgs); } return 0; failure: zval_dtor(&z_msgs); zval_dtor(rv); if (id) efree(id); return -1; } PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { zval z_ret = {0}; int count; ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); if (read_mbulk_header(redis_sock, &count) < 0) goto failure; if (redis_read_xclaim_reply(redis_sock, count, ctx == PHPREDIS_CTX_PTR, &z_ret) < 0) goto failure; if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } return 0; failure: if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return -1; } PHP_REDIS_API int redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements) { zval zv; int i, len = 0; char *key = NULL, *data; REDIS_REPLY_TYPE type; long li; for (i = 0; i < elements; ++i) { if (redis_read_reply_type(redis_sock, &type, &li) < 0) { goto failure; } switch (type) { case TYPE_BULK: if ((data = redis_sock_read_bulk_reply(redis_sock, li)) == NULL) { if (!key) goto failure; add_assoc_null_ex(z_ret, key, len); efree(key); key = NULL; } else if (key) { add_assoc_stringl_ex(z_ret, key, len, data, li); efree(data); efree(key); key = NULL; } else { key = data; len = li; } break; case TYPE_INT: if (key) { add_assoc_long_ex(z_ret, key, len, li); efree(key); key = NULL; } else { len = spprintf(&key, 0, "%ld", li); } break; case TYPE_MULTIBULK: array_init(&zv); if (redis_read_xinfo_response(redis_sock, &zv, li) != SUCCESS) { zval_dtor(&zv); goto failure; } if (key) { add_assoc_zval_ex(z_ret, key, len, &zv); efree(key); key = NULL; } else { add_next_index_zval(z_ret, &zv); } break; default: goto failure; } } return SUCCESS; failure: if (key) efree(key); return FAILURE; } PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { zval z_ret; int elements; if (read_mbulk_header(redis_sock, &elements) == SUCCESS) { array_init(&z_ret); if (redis_read_xinfo_response(redis_sock, &z_ret, elements) == SUCCESS) { if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } return SUCCESS; } zval_dtor(&z_ret); } if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } PHP_REDIS_API int redis_acl_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { if (ctx == NULL) { return redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 1) { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 2) { return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 3) { return redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR + 4) { return redis_acl_log_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } PHP_REDIS_API int redis_read_acl_log_reply(RedisSock *redis_sock, zval *zret, long count) { zval zsub; int i, nsub; for (i = 0; i < count; i++) { if (read_mbulk_header(redis_sock, &nsub) < 0 || nsub % 2 != 0) return FAILURE; array_init(&zsub); if (redis_mbulk_reply_zipped_raw_variant(redis_sock, &zsub, nsub) == FAILURE) return FAILURE; add_next_index_zval(zret, &zsub); } return SUCCESS; } PHP_REDIS_API int redis_read_acl_getuser_reply(RedisSock *redis_sock, zval *zret, long count) { REDIS_REPLY_TYPE type; zval zv; char *key, *val; long vlen; int klen, i; for (i = 0; i < count; i += 2) { if (!(key = redis_sock_read(redis_sock, &klen)) || redis_read_reply_type(redis_sock, &type, &vlen) < 0 || (type != TYPE_BULK && type != TYPE_MULTIBULK) || vlen > INT_MAX) { if (key) efree(key); return FAILURE; } if (type == TYPE_BULK) { if (!(val = redis_sock_read_bulk_reply(redis_sock, (int)vlen))) return FAILURE; add_assoc_stringl_ex(zret, key, klen, val, vlen); efree(val); } else { array_init(&zv); redis_mbulk_reply_loop(redis_sock, &zv, (int)vlen, UNSERIALIZE_NONE); add_assoc_zval_ex(zret, key, klen, &zv); } efree(key); } return SUCCESS; } int redis_acl_custom_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, int (*cb)(RedisSock*, zval*, long)) { REDIS_REPLY_TYPE type; int res = FAILURE; zval zret; long len; if (redis_read_reply_type(redis_sock, &type, &len) == 0 && type == TYPE_MULTIBULK) { array_init(&zret); res = cb(redis_sock, &zret, len); if (res == FAILURE) { zval_dtor(&zret); ZVAL_FALSE(&zret); } } else { ZVAL_FALSE(&zret); } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&zret, 0, 0); } else { add_next_index_zval(z_tab, &zret); } return res; } int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_acl_custom_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_read_acl_getuser_reply); } int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_acl_custom_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_read_acl_log_reply); } /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */ PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, UNSERIALIZE_NONE, SCORE_DECODE_NONE); } /* Zipped key => value reply unserializing keys and decoding the score as an integer (PUBSUB) */ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_INT); } /* Zipped key => value reply unserializing keys and decoding the score as a double (ZSET commands) */ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_DOUBLE); } /* Zipped key => value reply where only the values are unserialized (e.g. HMGET) */ PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } PHP_REDIS_API int redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; zend_bool ret = 0; if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) { ret = (response[1] == '1'); efree(response); } if (IS_ATOMIC(redis_sock)) { RETVAL_BOOL(ret); } else { add_next_index_bool(z_tab, ret); } return ret ? SUCCESS : FAILURE; } PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } if (IS_ATOMIC(redis_sock)) { if (!redis_unpack(redis_sock, response, response_len, return_value)) { RETVAL_STRINGL(response, response_len); } } else { zval z_unpacked; if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) { add_next_index_zval(z_tab, &z_unpacked); } else { add_next_index_stringl(z_tab, response, response_len); } } efree(response); return SUCCESS; } /* like string response, but never unserialized. */ PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } if (IS_ATOMIC(redis_sock)) { RETVAL_STRINGL(response, response_len); } else { add_next_index_stringl(z_tab, response, response_len); } efree(response); return SUCCESS; } /* Response for DEBUG object which is a formatted single line reply */ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *resp, *p, *p2, *p3, *p4; int is_numeric, resp_len; /* Add or return false if we can't read from the socket */ if((resp = redis_sock_read(redis_sock, &resp_len))==NULL) { if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } add_next_index_bool(z_tab, 0); return; } zval z_result; array_init(&z_result); /* Skip the '+' */ p = resp + 1; /* : ... */ while((p2 = strchr(p, ':'))!=NULL) { /* Null terminate at the ':' */ *p2++ = '\0'; /* Null terminate at the space if we have one */ if((p3 = strchr(p2, ' '))!=NULL) { *p3++ = '\0'; } else { p3 = resp + resp_len; } is_numeric = 1; for(p4=p2; *p4; ++p4) { if(*p4 < '0' || *p4 > '9') { is_numeric = 0; break; } } /* Add our value */ if(is_numeric) { add_assoc_long(&z_result, p, atol(p2)); } else { add_assoc_string(&z_result, p, p2); } p = p3; } efree(resp); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_result, 0, 1); } else { add_next_index_zval(z_tab, &z_result); } } PHP_REDIS_API int redis_sock_configure(RedisSock *redis_sock, HashTable *opts) { zend_string *zkey; zval *val; ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zkey, val) { if (zkey == NULL) { continue; } ZVAL_DEREF(val); if (zend_string_equals_literal_ci(zkey, "host")) { if (Z_TYPE_P(val) != IS_STRING) { REDIS_VALUE_EXCEPTION("Invalid host"); return FAILURE; } if (redis_sock->host) zend_string_release(redis_sock->host); redis_sock->host = zval_get_string(val); } else if (zend_string_equals_literal_ci(zkey, "port")) { if (Z_TYPE_P(val) != IS_LONG) { REDIS_VALUE_EXCEPTION("Invalid port"); return FAILURE; } redis_sock->port = zval_get_long(val); } else if (zend_string_equals_literal_ci(zkey, "connectTimeout")) { if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { REDIS_VALUE_EXCEPTION("Invalid connect timeout"); return FAILURE; } redis_sock->timeout = zval_get_double(val); } else if (zend_string_equals_literal_ci(zkey, "readTimeout")) { if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { REDIS_VALUE_EXCEPTION("Invalid read timeout"); return FAILURE; } redis_sock->read_timeout = zval_get_double(val); } else if (zend_string_equals_literal_ci(zkey, "persistent")) { if (Z_TYPE_P(val) == IS_STRING) { if (redis_sock->persistent_id) zend_string_release(redis_sock->persistent_id); redis_sock->persistent_id = zval_get_string(val); redis_sock->persistent = 1; } else { redis_sock->persistent = zval_is_true(val); } } else if (zend_string_equals_literal_ci(zkey, "retryInterval")) { if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { REDIS_VALUE_EXCEPTION("Invalid retry interval"); return FAILURE; } redis_sock->retry_interval = zval_get_long(val); } else if (zend_string_equals_literal_ci(zkey, "ssl")) { if (redis_sock_set_stream_context(redis_sock, val) != SUCCESS) { REDIS_VALUE_EXCEPTION("Invalid SSL context options"); return FAILURE; } } else if (zend_string_equals_literal_ci(zkey, "auth")) { if (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY) { REDIS_VALUE_EXCEPTION("Invalid auth credentials"); return FAILURE; } redis_sock_set_auth_zval(redis_sock, val); } else if (zend_string_equals_literal_ci(zkey, "backoff")) { if (redis_sock_set_backoff(redis_sock, val) != SUCCESS) { REDIS_VALUE_EXCEPTION("Invalid backoff options"); return FAILURE; } } else { php_error_docref(NULL, E_WARNING, "Skip unknown option '%s'", ZSTR_VAL(zkey)); } } ZEND_HASH_FOREACH_END(); return SUCCESS; } /** * redis_sock_create */ PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, int port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval) { RedisSock *redis_sock; redis_sock = ecalloc(1, sizeof(RedisSock)); redis_sock->host = zend_string_init(host, host_len, 0); redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->retry_interval = retry_interval * 1000; redis_sock->max_retries = 10; redis_initialize_backoff(&redis_sock->backoff, retry_interval); redis_sock->persistent = persistent; if (persistent && persistent_id != NULL) { redis_sock->persistent_id = zend_string_init(persistent_id, strlen(persistent_id), 0); } redis_sock->port = port; redis_sock->timeout = timeout; redis_sock->read_timeout = read_timeout; redis_sock->serializer = REDIS_SERIALIZER_NONE; redis_sock->compression = REDIS_COMPRESSION_NONE; redis_sock->mode = ATOMIC; return redis_sock; } static int redis_uniqid(char *buf, size_t buflen) { struct timeval tv; gettimeofday(&tv, NULL); return snprintf(buf, buflen, "phpredis:%08lx%05lx:%08lx", (long)tv.tv_sec, (long)tv.tv_usec, (long)php_rand()); } static int redis_stream_liveness_check(php_stream *stream) { return php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK ? SUCCESS : FAILURE; } /* Try to get the underlying socket FD for use with poll/select. * Returns -1 on failure. */ static php_socket_t redis_stream_fd_for_select(php_stream *stream) { php_socket_t fd; int flags; flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL; if (php_stream_cast(stream, flags, (void*)&fd, 1) == FAILURE) return -1; return fd; } static int redis_detect_dirty_config(void) { int val = INI_INT("redis.pconnect.pool_detect_dirty"); if (val >= 0 && val <= 2) return val; else if (val > 2) return 2; else return 0; } static int redis_pool_poll_timeout(void) { int val = INI_INT("redis.pconnect.pool_poll_timeout"); if (val >= 0) return val; return 0; } #define REDIS_POLL_FD_SET(_pfd, _fd, _events) \ (_pfd).fd = _fd; (_pfd).events = _events; (_pfd).revents = 0 /* Try to determine if the socket is out of sync (has unconsumed replies) */ static int redis_stream_detect_dirty(php_stream *stream) { php_socket_t fd; php_pollfd pfd; int rv, action; /* Short circuit if this is disabled */ if ((action = redis_detect_dirty_config()) == 0) return SUCCESS; /* Seek past unconsumed bytes if we detect them */ if (stream->readpos < stream->writepos) { redisDbgFmt("%s on unconsumed buffer (%ld < %ld)", action > 1 ? "Aborting" : "Seeking", (long)stream->readpos, (long)stream->writepos); /* Abort if we are configured to immediately fail */ if (action == 1) return FAILURE; /* Seek to the end of buffered data */ zend_off_t offset = stream->writepos - stream->readpos; if (php_stream_seek(stream, offset, SEEK_CUR) == FAILURE) return FAILURE; } /* Get the underlying FD */ if ((fd = redis_stream_fd_for_select(stream)) == -1) return FAILURE; /* We want to detect a readable socket (it shouln't be) */ REDIS_POLL_FD_SET(pfd, fd, PHP_POLLREADABLE); rv = php_poll2(&pfd, 1, redis_pool_poll_timeout()); /* If we detect the socket is readable, it's dirty which is * a failure. Otherwise as best we can tell it's good. * TODO: We could attempt to consume up to N bytes */ redisDbgFmt("Detected %s socket", rv > 0 ? "readable" : "unreadable"); return rv == 0 ? SUCCESS : FAILURE; } static int redis_sock_check_liveness(RedisSock *redis_sock) { char id[64], inbuf[4096]; int idlen, auth; smart_string cmd = {0}; size_t len; /* Short circuit if PHP detects the stream isn't live */ if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS) goto failure; /* Short circuit if we detect the stream is "dirty", can't or are configured not to try and fix it */ if (redis_stream_detect_dirty(redis_sock->stream) != SUCCESS) goto failure; redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; if (!INI_INT("redis.pconnect.echo_check_liveness")) { return SUCCESS; } /* AUTH (if we need it) */ auth = redis_sock_append_auth(redis_sock, &cmd); /* ECHO challenge/response */ idlen = redis_uniqid(id, sizeof(id)); REDIS_CMD_INIT_SSTR_STATIC(&cmd, 1, "ECHO"); redis_cmd_append_sstr(&cmd, id, idlen); /* Send command(s) and make sure we can consume reply(ies) */ if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) { smart_string_free(&cmd); goto failure; } smart_string_free(&cmd); if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { goto failure; } if (auth) { if (strncmp(inbuf, "+OK", 3) == 0 || strncmp(inbuf, "-ERR Client sent AUTH", 21) == 0) { /* successfully authenticated or authentication isn't required */ if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { goto failure; } } else if (strncmp(inbuf, "-NOAUTH", 7) == 0) { /* connection is fine but authentication failed, next command must fails too */ if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "-NOAUTH", 7) != 0) { goto failure; } return SUCCESS; } else { goto failure; } redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; } else { if (strncmp(inbuf, "-NOAUTH", 7) == 0) { /* connection is fine but authentication required */ return SUCCESS; } } /* check echo response */ if ((redis_sock->sentinel && ( strncmp(inbuf, "-ERR unknown command", 20) != 0 || strstr(inbuf, id) == NULL )) || *inbuf != TYPE_BULK || atoi(inbuf + 1) != idlen || redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, id, idlen) != 0 ) { goto failure; } return SUCCESS; failure: redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; if (redis_sock->stream) { php_stream_pclose(redis_sock->stream); redis_sock->stream = NULL; } return FAILURE; } /** * redis_sock_connect */ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) { struct timeval tv, read_tv, *tv_ptr = NULL; zend_string *persistent_id = NULL, *estr = NULL; char host[1024], scheme[8], *pos, *address; const char *fmtstr = "%s://%s:%d"; int host_len, usocket = 0, err = 0, tcp_flag = 1; ConnectionPool *p = NULL; if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock, 0, 1); } address = ZSTR_VAL(redis_sock->host); if ((pos = strstr(address, "://")) == NULL) { strcpy(scheme, redis_sock->stream_ctx ? "ssl" : "tcp"); } else { snprintf(scheme, sizeof(scheme), "%.*s", (int)(pos - address), address); address = pos + sizeof("://") - 1; } if (address[0] == '/' && redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", address); usocket = 1; } else { if(redis_sock->port == 0) redis_sock->port = 6379; #ifdef HAVE_IPV6 /* If we've got IPv6 and find a colon in our address, convert to proper * IPv6 [host]:port format */ if (strchr(address, ':') != NULL && strchr(address, '[') == NULL) { fmtstr = "%s://[%s]:%d"; } #endif host_len = snprintf(host, sizeof(host), fmtstr, scheme, address, redis_sock->port); } if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { p = redis_sock_get_connection_pool(redis_sock); if (zend_llist_count(&p->list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list); zend_llist_remove_tail(&p->list); if (redis_sock_check_liveness(redis_sock) == SUCCESS) { return SUCCESS; } p->nb_active--; } int limit = INI_INT("redis.pconnect.connection_limit"); if (limit > 0 && p->nb_active >= limit) { redis_sock_set_err(redis_sock, "Connection limit reached", sizeof("Connection limit reached") - 1); return FAILURE; } gettimeofday(&tv, NULL); persistent_id = strpprintf(0, "phpredis_%ld%ld", tv.tv_sec, tv.tv_usec); } else { if (redis_sock->persistent_id) { persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id)); } else { persistent_id = strpprintf(0, "phpredis:%s:%f", host, redis_sock->timeout); } } } tv.tv_sec = (time_t)redis_sock->timeout; tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); if (tv.tv_sec != 0 || tv.tv_usec != 0) { tv_ptr = &tv; } redis_sock->stream = php_stream_xport_create(host, host_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id ? ZSTR_VAL(persistent_id) : NULL, tv_ptr, redis_sock->stream_ctx, &estr, &err); if (persistent_id) { zend_string_release(persistent_id); } if (!redis_sock->stream) { if (estr) { redis_sock_set_err(redis_sock, ZSTR_VAL(estr), ZSTR_LEN(estr)); zend_string_release(estr); } return FAILURE; } if (p) p->nb_active++; /* Attempt to set TCP_NODELAY/TCP_KEEPALIVE if we're not using a unix socket. */ if (!usocket) { php_netstream_data_t *sock = (php_netstream_data_t*)redis_sock->stream->abstract; err = setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char*) &tcp_flag, sizeof(tcp_flag)); PHPREDIS_NOTUSED(err); err = setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char*) &redis_sock->tcp_keepalive, sizeof(redis_sock->tcp_keepalive)); PHPREDIS_NOTUSED(err); } php_stream_auto_cleanup(redis_sock->stream); read_tv.tv_sec = (time_t)redis_sock->read_timeout; read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); if (read_tv.tv_sec != 0 || read_tv.tv_usec != 0) { php_stream_set_option(redis_sock->stream,PHP_STREAM_OPTION_READ_TIMEOUT, 0, &read_tv); } php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; return SUCCESS; } /** * redis_sock_server_open */ PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock) { if (redis_sock) { switch (redis_sock->status) { case REDIS_SOCK_STATUS_DISCONNECTED: if (redis_sock_connect(redis_sock) != SUCCESS) { break; } redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; // fall through case REDIS_SOCK_STATUS_CONNECTED: if (redis_sock_auth(redis_sock) != SUCCESS) { break; } redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; // fall through case REDIS_SOCK_STATUS_AUTHENTICATED: if (redis_sock->dbNumber && reselect_db(redis_sock) != SUCCESS) { break; } redis_sock->status = REDIS_SOCK_STATUS_READY; // fall through case REDIS_SOCK_STATUS_READY: return SUCCESS; default: return FAILURE; } } return FAILURE; } /** * redis_sock_disconnect */ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force, int is_reset_mode) { if (redis_sock == NULL) { return FAILURE; } else if (redis_sock->stream) { if (redis_sock->persistent) { ConnectionPool *p = NULL; if (INI_INT("redis.pconnect.pooling_enabled")) { p = redis_sock_get_connection_pool(redis_sock); } if (force || !IS_ATOMIC(redis_sock)) { php_stream_pclose(redis_sock->stream); free_reply_callbacks(redis_sock); if (p) p->nb_active--; } else if (p) { zend_llist_prepend_element(&p->list, &redis_sock->stream); } } else { php_stream_close(redis_sock->stream); } redis_sock->stream = NULL; } if (is_reset_mode) { redis_sock->mode = ATOMIC; } redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; return SUCCESS; } /** * redis_sock_set_err */ PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { // Free our last error if (redis_sock->err != NULL) { zend_string_release(redis_sock->err); redis_sock->err = NULL; } if (msg != NULL && msg_len > 0) { // Copy in our new error message redis_sock->err = zend_string_init(msg, msg_len, 0); } } PHP_REDIS_API int redis_sock_set_stream_context(RedisSock *redis_sock, zval *options) { zend_string *zkey; zval *z_ele; if (!redis_sock || Z_TYPE_P(options) != IS_ARRAY) return FAILURE; if (!redis_sock->stream_ctx) redis_sock->stream_ctx = php_stream_context_alloc(); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), zkey, z_ele) { if (zkey != NULL) { php_stream_context_set_option(redis_sock->stream_ctx, "ssl", ZSTR_VAL(zkey), z_ele); } } ZEND_HASH_FOREACH_END(); return SUCCESS; } PHP_REDIS_API int redis_sock_set_backoff(RedisSock *redis_sock, zval *options) { zend_string *zkey; zend_long val; zval *z_ele; if (!redis_sock || Z_TYPE_P(options) != IS_ARRAY) { return FAILURE; } ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "algorithm")) { if ((val = zval_get_long(z_ele)) < 0 || val >= REDIS_BACKOFF_ALGORITHMS) { return FAILURE; } redis_sock->backoff.algorithm = val; } else if (zend_string_equals_literal_ci(zkey, "base")) { if ((val = zval_get_long(z_ele)) < 0) { return FAILURE; } redis_sock->backoff.base = val * 1000; } else if (zend_string_equals_literal_ci(zkey, "cap")) { if ((val = zval_get_long(z_ele)) < 0) { return FAILURE; } redis_sock->backoff.cap = val * 1000; } else { php_error_docref(NULL, E_WARNING, "Skip unknown backoff option '%s'", ZSTR_VAL(zkey)); } } } ZEND_HASH_FOREACH_END(); return SUCCESS; } /** * redis_sock_read_multibulk_reply */ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { zval z_multi_result; int numElems; if (read_mbulk_header(redis_sock, &numElems) < 0) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } if (numElems == -1 && redis_sock->null_mbulk_as_null) { ZVAL_NULL(&z_multi_result); } else { array_init(&z_multi_result); redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL); } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_multi_result, 0, 1); } else { add_next_index_zval(z_tab, &z_multi_result); } return 0; } /* Like multibulk reply, but don't touch the values, they won't be unserialized * (this is used by HKEYS). */ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { int numElems; if (read_mbulk_header(redis_sock, &numElems) < 0) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } zval z_multi_result; array_init(&z_multi_result); /* pre-allocate array for multi's results. */ redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_multi_result, 0, 1); } else { add_next_index_zval(z_tab, &z_multi_result); } return SUCCESS; } PHP_REDIS_API int redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *line; int i, numElems, len; zval z_multi_result; if (read_mbulk_header(redis_sock, &numElems) < 0) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } return FAILURE; } array_init(&z_multi_result); for (i = 0; i < numElems; ++i) { if ((line = redis_sock_read(redis_sock, &len)) == NULL) { add_next_index_bool(&z_multi_result, 0); continue; } add_next_index_double(&z_multi_result, atof(line)); efree(line); } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_multi_result, 0, 1); } else { add_next_index_zval(z_tab, &z_multi_result); } return SUCCESS; } PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize) { zval z_unpacked; char *line; int i, len; for (i = 0; i < count; ++i) { if ((line = redis_sock_read(redis_sock, &len)) == NULL) { add_next_index_bool(z_tab, 0); continue; } /* We will attempt unserialization, if we're unserializing everything, * or if we're unserializing keys and we're on a key, or we're * unserializing values and we're on a value! */ int unwrap = ( (unserialize == UNSERIALIZE_ALL) || (unserialize == UNSERIALIZE_KEYS && i % 2 == 0) || (unserialize == UNSERIALIZE_VALS && i % 2 != 0) ); if (unwrap && redis_unpack(redis_sock, line, len, &z_unpacked)) { add_next_index_zval(z_tab, &z_unpacked); } else { add_next_index_stringl(z_tab, line, len); } efree(line); } } static int redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int count) { REDIS_REPLY_TYPE type; char *key, *val; int keylen, i; zend_long lval; double dval; long vallen; for (i = 0; i < count; i+= 2) { /* Keys should always be bulk strings */ if ((key = redis_sock_read(redis_sock, &keylen)) == NULL) return FAILURE; /* This can vary */ if (redis_read_reply_type(redis_sock, &type, &vallen) < 0) { efree(key); return FAILURE; } if (type == TYPE_BULK) { if (vallen > INT_MAX || (val = redis_sock_read_bulk_reply(redis_sock, (int)vallen)) == NULL) { efree(key); return FAILURE; } /* Possibly overkill, but provides really nice types */ switch (is_numeric_string(val, vallen, &lval, &dval, 0)) { case IS_LONG: add_assoc_long_ex(zret, key, keylen, lval); break; case IS_DOUBLE: add_assoc_double_ex(zret, key, keylen, dval); break; default: add_assoc_stringl_ex(zret, key, keylen, val, vallen); } efree(val); } else if (type == TYPE_INT) { add_assoc_long_ex(zret, key, keylen, (zend_long)vallen); } else { add_assoc_null_ex(zret, key, keylen); } efree(key); } return SUCCESS; } /* Specialized multibulk processing for HMGET where we need to pair requested * keys with their returned values */ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; int i, numElems; zval *z_keys = ctx; if (read_mbulk_header(redis_sock, &numElems) < 0) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } goto failure; } zval z_multi_result; array_init(&z_multi_result); /* pre-allocate array for multi's results. */ for(i = 0; i < numElems; ++i) { zend_string *zstr = zval_get_string(&z_keys[i]); response = redis_sock_read(redis_sock, &response_len); if(response != NULL) { zval z_unpacked; if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) { add_assoc_zval_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked); } else { add_assoc_stringl_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), response, response_len); } efree(response); } else { add_assoc_bool_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0); } zend_string_release(zstr); zval_dtor(&z_keys[i]); } efree(z_keys); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_multi_result, 0, 1); } else { add_next_index_zval(z_tab, &z_multi_result); } return SUCCESS; failure: if (z_keys != NULL) { for (i = 0; Z_TYPE(z_keys[i]) != IS_NULL; ++i) { zval_dtor(&z_keys[i]); } efree(z_keys); } return FAILURE; } /** * redis_sock_write */ PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz) { if (redis_check_eof(redis_sock, 0, 0) == 0 && redis_sock_write_raw(redis_sock, cmd, sz) == sz) { return sz; } return -1; } void free_reply_callbacks(RedisSock *redis_sock) { fold_item *fi; while (redis_sock->head != NULL) { fi = redis_sock->head->next; free(redis_sock->head); redis_sock->head = fi; } redis_sock->current = NULL; } /** * redis_free_socket */ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) { int i; if (redis_sock->prefix) { zend_string_release(redis_sock->prefix); } if (redis_sock->pipeline_cmd) { zend_string_release(redis_sock->pipeline_cmd); } if (redis_sock->err) { zend_string_release(redis_sock->err); } if (redis_sock->persistent_id) { zend_string_release(redis_sock->persistent_id); } if (redis_sock->host) { zend_string_release(redis_sock->host); } for (i = 0; i < REDIS_SUBS_BUCKETS; ++i) { if (redis_sock->subs[i]) { zend_hash_destroy(redis_sock->subs[i]); efree(redis_sock->subs[i]); redis_sock->subs[i] = NULL; } } redis_sock_free_auth(redis_sock); free_reply_callbacks(redis_sock); efree(redis_sock); } #ifdef HAVE_REDIS_LZ4 /* Implementation of CRC8 for our LZ4 checksum value */ static uint8_t crc8(unsigned char *input, size_t len) { size_t i; uint8_t crc = 0xFF; while (len--) { crc ^= *input++; for (i = 0; i < 8; i++) { if (crc & 0x80) crc = (uint8_t)(crc << 1) ^ 0x31; else crc <<= 1; } } return crc; } #endif PHP_REDIS_API int redis_compress(RedisSock *redis_sock, char **dst, size_t *dstlen, char *buf, size_t len) { switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: #ifdef HAVE_REDIS_LZF { char *data; uint32_t res; double size; /* preserve compatibility with PECL lzf_compress margin (greater of 4% and LZF_MARGIN) */ size = len + MIN(UINT_MAX - len, MAX(LZF_MARGIN, len / 25)); data = emalloc(size); if ((res = lzf_compress(buf, len, data, size)) > 0) { *dst = data; *dstlen = res; return 1; } efree(data); } #endif break; case REDIS_COMPRESSION_ZSTD: #ifdef HAVE_REDIS_ZSTD { char *data; size_t size; int level; if (redis_sock->compression_level < 1) { #ifdef ZSTD_CLEVEL_DEFAULT level = ZSTD_CLEVEL_DEFAULT; #else level = 3; #endif } else if (redis_sock->compression_level > ZSTD_maxCLevel()) { level = ZSTD_maxCLevel(); } else { level = redis_sock->compression_level; } size = ZSTD_compressBound(len); data = emalloc(size); size = ZSTD_compress(data, size, buf, len, level); if (!ZSTD_isError(size)) { *dst = erealloc(data, size); *dstlen = size; return 1; } efree(data); } #endif break; case REDIS_COMPRESSION_LZ4: #ifdef HAVE_REDIS_LZ4 { /* Compressing empty data is pointless */ if (len < 1) break; /* Compressing more than INT_MAX bytes would require multiple blocks */ if (len > INT_MAX) { php_error_docref(NULL, E_WARNING, "LZ4: compressing > %d bytes not supported", INT_MAX); break; } int old_len = len, lz4len, lz4bound; uint8_t crc = crc8((unsigned char*)&old_len, sizeof(old_len)); char *lz4buf, *lz4pos; lz4bound = LZ4_compressBound(len); lz4buf = emalloc(REDIS_LZ4_HDR_SIZE + lz4bound); lz4pos = lz4buf; /* Copy and move past crc8 length checksum */ memcpy(lz4pos, &crc, sizeof(crc)); lz4pos += sizeof(crc); /* Copy and advance past length */ memcpy(lz4pos, &old_len, sizeof(old_len)); lz4pos += sizeof(old_len); if (redis_sock->compression_level <= 0 || redis_sock->compression_level > REDIS_LZ4_MAX_CLEVEL) { lz4len = LZ4_compress_default(buf, lz4pos, old_len, lz4bound); } else { lz4len = LZ4_compress_HC(buf, lz4pos, old_len, lz4bound, redis_sock->compression_level); } if (lz4len <= 0) { efree(lz4buf); break; } *dst = lz4buf; *dstlen = lz4len + REDIS_LZ4_HDR_SIZE; return 1; } #endif break; } *dst = buf; *dstlen = len; return 0; } PHP_REDIS_API int redis_uncompress(RedisSock *redis_sock, char **dst, size_t *dstlen, const char *src, size_t len) { switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: #ifdef HAVE_REDIS_LZF { char *data = NULL; uint32_t res; int i; if (len == 0) break; /* Grow our buffer until we succeed or get a non E2BIG error */ errno = E2BIG; for (i = 2; errno == E2BIG; i *= 2) { data = erealloc(data, len * i); if ((res = lzf_decompress(src, len, data, len * i)) > 0) { *dst = data; *dstlen = res; return 1; } } efree(data); break; } #endif break; case REDIS_COMPRESSION_ZSTD: #ifdef HAVE_REDIS_ZSTD { char *data; unsigned long long zlen; zlen = ZSTD_getFrameContentSize(src, len); if (zlen == ZSTD_CONTENTSIZE_ERROR || zlen == ZSTD_CONTENTSIZE_UNKNOWN || zlen > INT_MAX) break; data = emalloc(zlen); *dstlen = ZSTD_decompress(data, zlen, src, len); if (ZSTD_isError(*dstlen) || *dstlen != zlen) { efree(data); break; } *dst = data; return 1; } #endif break; case REDIS_COMPRESSION_LZ4: #ifdef HAVE_REDIS_LZ4 { char *data; int datalen; uint8_t lz4crc; /* We must have at least enough bytes for our header, and can't have more than * INT_MAX + our header size. */ if (len < REDIS_LZ4_HDR_SIZE || len > INT_MAX + REDIS_LZ4_HDR_SIZE) break; /* Operate on copies in case our CRC fails */ const char *copy = src; size_t copylen = len; /* Read in our header bytes */ memcpy(&lz4crc, copy, sizeof(uint8_t)); copy += sizeof(uint8_t); copylen -= sizeof(uint8_t); memcpy(&datalen, copy, sizeof(int)); copy += sizeof(int); copylen -= sizeof(int); /* Make sure our CRC matches (TODO: Maybe issue a docref error?) */ if (crc8((unsigned char*)&datalen, sizeof(datalen)) != lz4crc) break; /* Finally attempt decompression */ data = emalloc(datalen); if (LZ4_decompress_safe(copy, data, copylen, datalen) > 0) { *dst = data; *dstlen = datalen; return 1; } efree(data); } #endif break; } *dst = (char*)src; *dstlen = len; return 0; } PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) { size_t tmplen; int tmpfree; char *tmp; /* First serialize */ tmpfree = redis_serialize(redis_sock, z, &tmp, &tmplen); /* Now attempt compression */ if (redis_compress(redis_sock, val, val_len, tmp, tmplen)) { if (tmpfree) efree(tmp); return 1; } return tmpfree; } PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) { size_t len; char *buf; /* Uncompress, then unserialize */ if (redis_uncompress(redis_sock, &buf, &len, src, srclen)) { if (!redis_unserialize(redis_sock, buf, len, zdst)) { ZVAL_STRINGL(zdst, buf, len); } efree(buf); return 1; } return redis_unserialize(redis_sock, buf, len, zdst); } PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) { php_serialize_data_t ht; smart_str sstr = {0}; #ifdef HAVE_REDIS_IGBINARY size_t sz; uint8_t *val8; #endif *val = ""; *val_len = 0; switch(redis_sock->serializer) { case REDIS_SERIALIZER_NONE: switch(Z_TYPE_P(z)) { case IS_STRING: *val = Z_STRVAL_P(z); *val_len = Z_STRLEN_P(z); break; case IS_OBJECT: *val = "Object"; *val_len = 6; break; case IS_ARRAY: *val = "Array"; *val_len = 5; break; default: { /* copy */ zend_string *zstr = zval_get_string(z); *val = estrndup(ZSTR_VAL(zstr), ZSTR_LEN(zstr)); *val_len = ZSTR_LEN(zstr); zend_string_release(zstr); return 1; } } break; case REDIS_SERIALIZER_PHP: PHP_VAR_SERIALIZE_INIT(ht); php_var_serialize(&sstr, z, &ht); *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); *val_len = ZSTR_LEN(sstr.s); smart_str_free(&sstr); PHP_VAR_SERIALIZE_DESTROY(ht); return 1; case REDIS_SERIALIZER_MSGPACK: #ifdef HAVE_REDIS_MSGPACK php_msgpack_serialize(&sstr, z); *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); *val_len = ZSTR_LEN(sstr.s); smart_str_free(&sstr); return 1; #endif break; case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY if(igbinary_serialize(&val8, (size_t *)&sz, z) == 0) { *val = (char*)val8; *val_len = sz; return 1; } #endif break; case REDIS_SERIALIZER_JSON: #ifdef HAVE_REDIS_JSON php_json_encode(&sstr, z, PHP_JSON_OBJECT_AS_ARRAY); *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); *val_len = ZSTR_LEN(sstr.s); smart_str_free(&sstr); return 1; #endif break; EMPTY_SWITCH_DEFAULT_CASE() } return 0; } PHP_REDIS_API int redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, zval *z_ret) { php_unserialize_data_t var_hash; int ret = 0; switch(redis_sock->serializer) { case REDIS_SERIALIZER_NONE: /* Nothing to do */ break; case REDIS_SERIALIZER_PHP: PHP_VAR_UNSERIALIZE_INIT(var_hash); ret = php_var_unserialize(z_ret, (const unsigned char **)&val, (const unsigned char *)val + val_len, &var_hash); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); break; case REDIS_SERIALIZER_MSGPACK: #ifdef HAVE_REDIS_MSGPACK ret = !php_msgpack_unserialize(z_ret, (char *)val, (size_t)val_len); #endif break; case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY /* * Check if the given string starts with an igbinary header. * * A modern igbinary string consists of the following format: * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- * | header (4) | type (1) | ... (n) | NUL (1) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- * * With header being three zero bytes and one non-zero version * specifier. At the time of this comment, there is only version * 0x01 and 0x02, but newer versions will use subsequent * values. * * Not all versions contain a trailing \x00 so don't check for that. */ if (val_len < 5 || memcmp(val, "\x00\x00\x00", 3) || val[3] < '\x01' || val[3] > '\x04') { /* This is most definitely not an igbinary string, so do not try to unserialize this as one. */ break; } ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, z_ret); #endif break; case REDIS_SERIALIZER_JSON: #ifdef HAVE_REDIS_JSON #if (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION < 1) JSON_G(error_code) = PHP_JSON_ERROR_NONE; php_json_decode(z_ret, (char*)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); ret = JSON_G(error_code) == PHP_JSON_ERROR_NONE; #else ret = !php_json_decode(z_ret, (char *)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); #endif #endif break; EMPTY_SWITCH_DEFAULT_CASE() } return ret; } PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len) { int ret_len; char *ret; if (redis_sock->prefix == NULL) { return 0; } ret_len = ZSTR_LEN(redis_sock->prefix) + *key_len; ret = ecalloc(1 + ret_len, 1); memcpy(ret, ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix)); memcpy(ret + ZSTR_LEN(redis_sock->prefix), *key, *key_len); *key = ret; *key_len = ret_len; return 1; } /* This is very similar to PHP >= 7.4 zend_string_concat2 only we are taking * two zend_string arguments rather than two char*, size_t pairs */ static zend_string *redis_zstr_concat(zend_string *prefix, zend_string *suffix) { zend_string *res; size_t len; ZEND_ASSERT(prefix != NULL && suffix != NULL); len = ZSTR_LEN(prefix) + ZSTR_LEN(suffix); res = zend_string_alloc(len, 0); memcpy(ZSTR_VAL(res), ZSTR_VAL(prefix), ZSTR_LEN(prefix)); memcpy(ZSTR_VAL(res) + ZSTR_LEN(prefix), ZSTR_VAL(suffix), ZSTR_LEN(suffix)); ZSTR_VAL(res)[len] = '\0'; return res; } PHP_REDIS_API zend_string * redis_key_prefix_zval(RedisSock *redis_sock, zval *zv) { zend_string *zstr, *dup; zstr = zval_get_string(zv); if (redis_sock->prefix == NULL) return zstr; dup = redis_zstr_concat(redis_sock->prefix, zstr); zend_string_release(zstr); return dup; } PHP_REDIS_API zend_string * redis_key_prefix_zstr(RedisSock *redis_sock, zend_string *key) { if (redis_sock->prefix == NULL) return zend_string_copy(key); return redis_zstr_concat(redis_sock->prefix, key); } /* * Processing for variant reply types (think EVAL) */ PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size) { // Handle EOF if(-1 == redis_check_eof(redis_sock, 1, 0)) { return -1; } if(redis_sock_get_line(redis_sock, buf, buf_size, line_size) == NULL) { if (redis_sock->port < 0) { snprintf(buf, buf_size, "read error on connection to %s", ZSTR_VAL(redis_sock->host)); } else { snprintf(buf, buf_size, "read error on connection to %s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); } // Close our socket redis_sock_disconnect(redis_sock, 1, 1); // Throw a read error exception REDIS_THROW_EXCEPTION(buf, 0); return FAILURE; } /* We don't need \r\n */ *line_size-=2; buf[*line_size]='\0'; /* Success! */ return 0; } PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info) { size_t nread; // Make sure we haven't lost the connection, even trying to reconnect if(-1 == redis_check_eof(redis_sock, 1, 0)) { // Failure *reply_type = EOF; return -1; } // Attempt to read the reply-type byte if((*reply_type = redis_sock_getc(redis_sock)) == EOF) { REDIS_THROW_EXCEPTION( "socket error on read socket", 0); return -1; } // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can // extract the value or size info here if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { // Buffer to hold size information char inbuf[255]; /* Read up to our newline */ if (redis_sock_get_line(redis_sock, inbuf, sizeof(inbuf), &nread) == NULL) { return -1; } /* Set our size response */ *reply_info = atol(inbuf); } /* Success! */ return 0; } /* * Read a single line response, having already consumed the reply-type byte */ static int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, int as_string, zval *z_ret) { // Buffer to read our single line reply char inbuf[4096]; size_t len; /* Attempt to read our single line reply */ if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) { return -1; } /* Throw exception on SYNC error otherwise just set error string */ if(reply_type == TYPE_ERR) { redis_sock_set_err(redis_sock, inbuf, len); redis_error_throw(redis_sock); ZVAL_FALSE(z_ret); } else if (as_string) { ZVAL_STRINGL(z_ret, inbuf, len); } else { ZVAL_TRUE(z_ret); } return 0; } PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret ) { // Attempt to read the bulk reply char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size); /* Set our reply to FALSE on failure, and the string on success */ if(bulk_resp == NULL) { ZVAL_FALSE(z_ret); return -1; } ZVAL_STRINGL(z_ret, bulk_resp, size); efree(bulk_resp); return 0; } PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, long long elements, int status_strings, zval *z_ret) { long reply_info; REDIS_REPLY_TYPE reply_type; zval z_subelem; // Iterate while we have elements while(elements > 0) { // Attempt to read our reply type if(redis_read_reply_type(redis_sock, &reply_type, &reply_info ) < 0) { zend_throw_exception_ex(redis_exception_ce, 0, "protocol error, couldn't parse MULTI-BULK response\n"); return FAILURE; } // Switch on our reply-type byte switch(reply_type) { case TYPE_ERR: case TYPE_LINE: redis_read_variant_line(redis_sock, reply_type, status_strings, &z_subelem); add_next_index_zval(z_ret, &z_subelem); break; case TYPE_INT: // Add our long value add_next_index_long(z_ret, reply_info); break; case TYPE_BULK: // Init a zval for our bulk response, read and add it redis_read_variant_bulk(redis_sock, reply_info, &z_subelem); add_next_index_zval(z_ret, &z_subelem); break; case TYPE_MULTIBULK: if (reply_info < 0 && redis_sock->null_mbulk_as_null) { add_next_index_null(z_ret); } else { array_init(&z_subelem); if (reply_info > 0) { redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, &z_subelem); } add_next_index_zval(z_ret, &z_subelem); } break; default: // Stop the compiler from whinging break; } /* Decrement our element counter */ elements--; } return 0; } static int variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int status_strings, int null_mbulk_as_null, zval *z_tab, void *ctx) { // Reply type, and reply size vars REDIS_REPLY_TYPE reply_type; long reply_info; zval z_ret; // Attempt to read our header if(redis_read_reply_type(redis_sock,&reply_type,&reply_info) < 0) { return -1; } /* Switch based on our top level reply type */ switch(reply_type) { case TYPE_ERR: case TYPE_LINE: redis_read_variant_line(redis_sock, reply_type, status_strings, &z_ret); break; case TYPE_INT: ZVAL_LONG(&z_ret, reply_info); break; case TYPE_BULK: redis_read_variant_bulk(redis_sock, reply_info, &z_ret); break; case TYPE_MULTIBULK: if (reply_info > -1) { array_init(&z_ret); redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, &z_ret); } else { if (null_mbulk_as_null) { ZVAL_NULL(&z_ret); } else { array_init(&z_ret); } } break; default: zend_throw_exception_ex(redis_exception_ce, 0, "protocol error, got '%c' as reply-type byte\n", reply_type); return FAILURE; } if (IS_ATOMIC(redis_sock)) { /* Set our return value */ RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } /* Success */ return 0; } PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, redis_sock->reply_literal, redis_sock->null_mbulk_as_null, z_tab, ctx); } PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 0, redis_sock->null_mbulk_as_null, z_tab, ctx); } PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, 0, z_tab, ctx); } PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) { zval *zv; HashTable *ht; int num; /* The user may wish to send us something like [NULL, 'password'] or * [false, 'password'] so don't convert NULL or FALSE into "". */ #define TRY_SET_AUTH_ARG(zv, ppzstr) \ do { \ if (Z_TYPE_P(zv) != IS_NULL && Z_TYPE_P(zv) != IS_FALSE) { \ *(ppzstr) = zval_get_string(zv); \ } \ } while (0) /* Null out user and password */ *user = *pass = NULL; /* User passed nothing */ if (ztest == NULL) return FAILURE; /* Handle a non-array first */ if (Z_TYPE_P(ztest) != IS_ARRAY) { TRY_SET_AUTH_ARG(ztest, pass); return SUCCESS; } /* Handle the array case */ ht = Z_ARRVAL_P(ztest); num = zend_hash_num_elements(ht); /* Something other than one or two entries makes no sense */ if (num != 1 && num != 2) { php_error_docref(NULL, E_WARNING, "When passing an array as auth it must have one or two elements!"); return FAILURE; } if (num == 2) { if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "user")) || (zv = zend_hash_index_find(ht, 0))) { TRY_SET_AUTH_ARG(zv, user); } if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "pass")) || (zv = zend_hash_index_find(ht, 1))) { TRY_SET_AUTH_ARG(zv, pass); } } else if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "pass")) || (zv = zend_hash_index_find(ht, 0))) { TRY_SET_AUTH_ARG(zv, pass); } /* If we at least have a password, we're good */ if (*pass != NULL) return SUCCESS; /* Failure, clean everything up so caller doesn't need to care */ if (*user) zend_string_release(*user); *user = NULL; return FAILURE; } /* Helper methods to extract configuration settings from a hash table */ zval *redis_hash_str_find_type(HashTable *ht, const char *key, int keylen, int type) { zval *zv = zend_hash_str_find(ht, key, keylen); if (zv == NULL || Z_TYPE_P(zv) != type) return NULL; return zv; } void redis_conf_double(HashTable *ht, const char *key, int keylen, double *dval) { zval *zv = zend_hash_str_find(ht, key, keylen); if (zv == NULL) return; *dval = zval_get_double(zv); } void redis_conf_bool(HashTable *ht, const char *key, int keylen, int *ival) { zend_string *zstr = NULL; redis_conf_string(ht, key, keylen, &zstr); if (zstr == NULL) return; *ival = zend_string_equals_literal_ci(zstr, "true") || zend_string_equals_literal_ci(zstr, "yes") || zend_string_equals_literal_ci(zstr, "1"); zend_string_release(zstr); } void redis_conf_zend_bool(HashTable *ht, const char *key, int keylen, zend_bool *bval) { zval *zv = zend_hash_str_find(ht, key, keylen); if (zv == NULL) return; *bval = zend_is_true(zv); } void redis_conf_long(HashTable *ht, const char *key, int keylen, zend_long *lval) { zval *zv = zend_hash_str_find(ht, key, keylen); if (zv == NULL) return; *lval = zval_get_long(zv); } void redis_conf_int(HashTable *ht, const char *key, int keylen, int *ival) { zval *zv = zend_hash_str_find(ht, key, keylen); if (zv == NULL) return; *ival = zval_get_long(zv); } void redis_conf_string(HashTable *ht, const char *key, size_t keylen, zend_string **sval) { zval *zv = zend_hash_str_find(ht, key, keylen); if (zv == NULL) return; *sval = zval_get_string(zv); } void redis_conf_zval(HashTable *ht, const char *key, size_t keylen, zval *zret, int copy, int dtor) { zval *zv = zend_hash_str_find(ht, key, keylen); if (zv == NULL) return; ZVAL_ZVAL(zret, zv, copy, dtor); } void redis_conf_auth(HashTable *ht, const char *key, size_t keylen, zend_string **user, zend_string **pass) { zval *zv = zend_hash_str_find(ht, key, keylen); if (zv == NULL) return; redis_extract_auth_info(zv, user, pass); } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ redis-6.0.2/library.h0000644000175000000120000004000014515245367015227 0ustar pyatsukhnenkowheel#ifndef REDIS_LIBRARY_H #define REDIS_LIBRARY_H /* Non cluster command helper */ #define REDIS_SPPRINTF(ret, kw, fmt, ...) \ redis_spprintf(redis_sock, NULL, ret, kw, fmt, ##__VA_ARGS__) #define REDIS_CMD_APPEND_SSTR_STATIC(sstr, str) \ redis_cmd_append_sstr(sstr, str, sizeof(str)-1); #define REDIS_CMD_APPEND_SSTR_OPT_STATIC(sstr, opt, str) \ if (opt) REDIS_CMD_APPEND_SSTR_STATIC(sstr, str); #define REDIS_CMD_INIT_SSTR_STATIC(sstr, argc, keyword) \ redis_cmd_init_sstr(sstr, argc, keyword, sizeof(keyword)-1); #define REDIS_THROW_EXCEPTION(msg, code) \ zend_throw_exception(redis_exception_ce, (msg), code) #define CLUSTER_THROW_EXCEPTION(msg, code) \ zend_throw_exception(redis_cluster_exception_ce, (msg), code) #define redis_sock_write_sstr(redis_sock, sstr) \ redis_sock_write(redis_sock, (sstr)->c, (sstr)->len) #if PHP_VERSION_ID < 80000 #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(ZSTR_VAL((zstr)), ZSTR_LEN((zstr))) /* use RedisException when ValueError not available */ #define REDIS_VALUE_EXCEPTION(m) REDIS_THROW_EXCEPTION(m, 0) #define RETURN_THROWS() RETURN_FALSE #else #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(zstr) #define REDIS_VALUE_EXCEPTION(m) zend_value_error(m) #endif void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id); void free_reply_callbacks(RedisSock *redis_sock); PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass); int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); int redis_cmd_append_sstr_int(smart_string *str, int append); int redis_cmd_append_sstr_long(smart_string *str, long append); int redis_cmd_append_sstr_i64(smart_string *str, int64_t append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr); int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock); int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_key_zstr(smart_string *str, zend_string *key, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_key_long(smart_string *dst, zend_long lval, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx); PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...); PHP_REDIS_API zend_string *redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...); PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len); PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len); PHP_REDIS_API int redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx); typedef void (*SuccessCallback)(RedisSock *redis_sock); PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, int port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); PHP_REDIS_API int redis_sock_configure(RedisSock *redis_sock, HashTable *opts); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock); PHP_REDIS_API char *redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen); PHP_REDIS_API void redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass); PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv); PHP_REDIS_API void redis_sock_free_auth(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force, int is_reset_mode); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(RedisSock *redis_sock, zval *z_tab); PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen, size_t *linelen, int set_err); PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes); PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize); PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, zend_long *iter); PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_xclaim_reply( RedisSock *redis_sock, int count, int is_xautoclaim, zval *rv); PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_pubsub_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz); PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw); PHP_REDIS_API RedisSock *redis_sock_get(zval *id, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); PHP_REDIS_API int redis_sock_set_stream_context(RedisSock *redis_sock, zval *options); PHP_REDIS_API int redis_sock_set_backoff(RedisSock *redis_sock, zval *options); PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len); PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len); PHP_REDIS_API zend_string* redis_key_prefix_zval(RedisSock *redis_sock, zval *zv); PHP_REDIS_API zend_string * redis_key_prefix_zstr(RedisSock *redis_sock, zend_string *key); PHP_REDIS_API int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret); PHP_REDIS_API int redis_compress(RedisSock *redis_sock, char **dst, size_t *dstlen, char *buf, size_t len); PHP_REDIS_API int redis_uncompress(RedisSock *redis_sock, char **dst, size_t *dstlen, const char *src, size_t len); PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len); PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret); PHP_REDIS_API int redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret); PHP_REDIS_API int redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_ret); PHP_REDIS_API int redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements); PHP_REDIS_API int redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, void *ctx); /* Specialized ACL reply handlers */ PHP_REDIS_API int redis_acl_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_acl_getuser_reply(RedisSock *redis_sock, zval *zret, long len); PHP_REDIS_API int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_acl_log_reply(RedisSock *redis_sock, zval *zret, long count); PHP_REDIS_API int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); /* * Variant Read methods, mostly to implement eval */ PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info); PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret); PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, long long elements, int status_strings, zval *z_ret); PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_zadd_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_geosearch_response(zval *zdst, RedisSock *redis_sock, long long elements, int with_aux_data); PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_srandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx); PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); /* Helper methods to get configuration values from a HashTable. */ #define REDIS_HASH_STR_FIND_STATIC(ht, sstr) \ zend_hash_str_find(ht, sstr, sizeof(sstr) - 1) #define REDIS_HASH_STR_FIND_TYPE_STATIC(ht, sstr, type) \ redis_hash_str_find_type(ht, sstr, sizeof(sstr) - 1, type) #define REDIS_CONF_DOUBLE_STATIC(ht, sstr, dval) \ redis_conf_double(ht, sstr, sizeof(sstr) - 1, dval) #define REDIS_CONF_BOOL_STATIC(ht, sstr, rval) \ redis_conf_bool(ht, sstr, sizeof(sstr) - 1, rval) #define REDIS_CONF_ZEND_BOOL_STATIC(ht, sstr, bval) \ redis_conf_zend_bool(ht, sstr, sizeof(sstr) - 1, bval) #define REDIS_CONF_LONG_STATIC(ht, sstr, lval) \ redis_conf_long(ht, sstr, sizeof(sstr) - 1, lval) #define REDIS_CONF_INT_STATIC(ht, sstr, ival) \ redis_conf_int(ht, sstr, sizeof(sstr) - 1, ival) #define REDIS_CONF_STRING_STATIC(ht, sstr, sval) \ redis_conf_string(ht, sstr, sizeof(sstr) - 1, sval) #define REDIS_CONF_ZVAL_STATIC(ht, sstr, zret, copy, dtor) \ redis_conf_zval(ht, sstr, sizeof(sstr) - 1, zret, copy, dtor) #define REDIS_CONF_AUTH_STATIC(ht, sstr, user, pass) \ redis_conf_auth(ht, sstr, sizeof(sstr) - 1, user, pass) zval *redis_hash_str_find_type(HashTable *ht, const char *key, int keylen, int type); void redis_conf_double(HashTable *ht, const char *key, int keylen, double *dval); void redis_conf_bool(HashTable *ht, const char *key, int keylen, int *bval); void redis_conf_zend_bool(HashTable *ht, const char *key, int keylen, zend_bool *bval); void redis_conf_long(HashTable *ht, const char *key, int keylen, zend_long *lval); void redis_conf_int(HashTable *ht, const char *key, int keylen, int *ival); void redis_conf_string(HashTable *ht, const char *key, size_t keylen, zend_string **sval); void redis_conf_zval(HashTable *ht, const char *key, size_t keylen, zval *zret, int copy, int dtor); void redis_conf_auth(HashTable *ht, const char *key, size_t keylen, zend_string **user, zend_string **pass); static inline char *redis_sock_get_line(RedisSock *redis_sock, char *buf, size_t buf_size, size_t *nread) { char *res; res = php_stream_get_line(redis_sock->stream, buf, buf_size, nread); if (res != NULL) redis_sock->rxBytes += *nread; return res; } static inline char redis_sock_getc(RedisSock *redis_sock) { char res; res = php_stream_getc(redis_sock->stream); if (res != EOF) redis_sock->rxBytes++; return res; } static inline ssize_t redis_sock_read_raw(RedisSock *redis_sock, char *buf, size_t buf_size) { ssize_t nread; nread = php_stream_read(redis_sock->stream, buf, buf_size); if (nread > 0) redis_sock->rxBytes += nread; return nread; } static inline ssize_t redis_sock_write_raw(RedisSock *redis_sock, const char *buf, size_t buf_size) { ssize_t nwritten; nwritten = php_stream_write(redis_sock->stream, buf, buf_size); if (nwritten > 0) redis_sock->txBytes += nwritten; return nwritten; } static inline char *redis_sock_gets_raw(RedisSock *redis_sock, char *buf, size_t buf_size) { size_t nread; return redis_sock_get_line(redis_sock, buf, buf_size, &nread); } #endif redis-6.0.2/php_redis.h0000644000175000000120000000455414515245367015556 0ustar pyatsukhnenkowheel/* +----------------------------------------------------------------------+ | 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 | | Maintainer: Nicolas Favre-Felix | | Maintainer: Michael Grunder | | Maintainer: Nasreddine Bouafif | +----------------------------------------------------------------------+ */ #include "common.h" #ifndef PHP_REDIS_H #define PHP_REDIS_H /* phpredis version */ #define PHP_REDIS_VERSION "6.0.2" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ #define REDIS_SALT_BYTES 32 #define REDIS_SALT_SIZE ((2 * REDIS_SALT_BYTES) + 1) ZEND_BEGIN_MODULE_GLOBALS(redis) char salt[REDIS_SALT_SIZE]; ZEND_END_MODULE_GLOBALS(redis) ZEND_EXTERN_MODULE_GLOBALS(redis) #define REDIS_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(redis, v) #ifdef ZTS #include "TSRM.h" #endif PHP_MINIT_FUNCTION(redis); PHP_MSHUTDOWN_FUNCTION(redis); PHP_MINFO_FUNCTION(redis); PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); extern zend_module_entry redis_module_entry; #define redis_module_ptr &redis_module_entry #define phpext_redis_ptr redis_module_ptr #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ redis-6.0.2/redis.c0000644000175000000120000024543414515245367014706 0ustar pyatsukhnenkowheel/* -*- 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 | | Maintainer: Nicolas Favre-Felix | | Maintainer: Nasreddine Bouafif | | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ #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 #include #include #include #include #ifdef PHP_SESSION #include #endif #include "library.h" #ifdef HAVE_REDIS_ZSTD #include #endif #ifdef HAVE_REDIS_LZ4 #include #endif #ifdef PHP_SESSION extern ps_module ps_mod_redis; extern ps_module ps_mod_redis_cluster; #endif zend_class_entry *redis_ce; zend_class_entry *redis_exception_ce; #if PHP_VERSION_ID < 80000 #include "redis_legacy_arginfo.h" #else #include "zend_attributes.h" #include "redis_arginfo.h" #endif extern const zend_function_entry *redis_get_methods(void) { return class_Redis_methods; } extern int le_cluster_slot_cache; int le_redis_pconnect; PHP_INI_BEGIN() /* redis arrays */ PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.auth", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.autorehash", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.connecttimeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.index", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.lazyconnect", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.pconnect", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.readtimeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.retryinterval", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.consistent", "0", PHP_INI_ALL, NULL) /* redis cluster */ PHP_INI_ENTRY("redis.clusters.cache_slots", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.auth", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.read_timeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL) /* redis pconnect */ PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.pconnect.echo_check_liveness", "1", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.pconnect.pool_detect_dirty", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.pconnect.pool_poll_timeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.pconnect.pool_pattern", "", PHP_INI_ALL, NULL) /* redis session */ PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_retries", "100", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.early_refresh", "0", PHP_INI_ALL, NULL) PHP_INI_END() static const zend_module_dep redis_deps[] = { #ifdef HAVE_REDIS_IGBINARY ZEND_MOD_REQUIRED("igbinary") #endif #ifdef HAVE_REDIS_MSGPACK ZEND_MOD_REQUIRED("msgpack") #endif #ifdef HAVE_REDIS_JSON ZEND_MOD_REQUIRED("json") #endif #ifdef PHP_SESSION ZEND_MOD_REQUIRED("session") #endif ZEND_MOD_END }; ZEND_DECLARE_MODULE_GLOBALS(redis) zend_module_entry redis_module_entry = { STANDARD_MODULE_HEADER_EX, NULL, redis_deps, "redis", NULL, PHP_MINIT(redis), NULL, NULL, NULL, PHP_MINFO(redis), PHP_REDIS_VERSION, PHP_MODULE_GLOBALS(redis), NULL, NULL, NULL, STANDARD_MODULE_PROPERTIES_EX }; #ifdef COMPILE_DL_REDIS ZEND_GET_MODULE(redis) #endif zend_object_handlers redis_object_handlers; /* Send a static DISCARD in case we're in MULTI mode. */ static int redis_send_discard(RedisSock *redis_sock) { char *resp; int resp_len, result = FAILURE; /* send our DISCARD command */ if (redis_sock_write(redis_sock, ZEND_STRL(RESP_DISCARD_CMD)) >= 0 && (resp = redis_sock_read(redis_sock,&resp_len)) != NULL) { /* success if we get OK */ result = (resp_len == 3 && strncmp(resp,"+OK", 3) == 0) ? SUCCESS:FAILURE; /* free our response */ efree(resp); } /* return success/failure */ return result; } /* Passthru for destroying cluster cache */ static void cluster_cache_dtor(zend_resource *rsrc) { if (rsrc->ptr) { cluster_cache_free(rsrc->ptr); } } void free_redis_object(zend_object *object) { redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object); zend_object_std_dtor(&redis->std); if (redis->sock) { redis_sock_disconnect(redis->sock, 0, 1); redis_free_socket(redis->sock); } } zend_object * create_redis_object(zend_class_entry *ce) { redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(ce)); redis->sock = NULL; zend_object_std_init(&redis->std, ce); object_properties_init(&redis->std, ce); memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers)); redis_object_handlers.offset = XtOffsetOf(redis_object, std); redis_object_handlers.free_obj = free_redis_object; redis->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; } static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) { if (res->ptr) { ConnectionPool *p = res->ptr; zend_llist_destroy(&p->list); pefree(res->ptr, 1); } } static void redis_random_hex_bytes(char *dst, size_t dstsize) { char chunk[9], *ptr = dst; ssize_t rem = dstsize, len, clen; size_t bytes; /* We need two characters per hex byte */ bytes = dstsize / 2; zend_string *s = zend_string_alloc(bytes, 0); /* First try to have PHP generate the bytes */ if (php_random_bytes_silent(ZSTR_VAL(s), bytes) == SUCCESS) { php_hash_bin2hex(dst, (unsigned char *)ZSTR_VAL(s), bytes); zend_string_release(s); return; } /* PHP shouldn't have failed, but generate manually if it did. */ while (rem > 0) { clen = snprintf(chunk, sizeof(chunk), "%08x", rand()); len = rem >= clen ? clen : rem; memcpy(ptr, chunk, len); ptr += len; rem -= len; } zend_string_release(s); } /** * PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(redis) { struct timeval tv; /* Seed random generator (for RedisCluster failover) */ gettimeofday(&tv, NULL); srand(tv.tv_usec * tv.tv_sec); /* Generate our random salt */ redis_random_hex_bytes(REDIS_G(salt), sizeof(REDIS_G(salt)) - 1); REDIS_G(salt)[sizeof(REDIS_G(salt)) - 1] = '\0'; REGISTER_INI_ENTRIES(); /* Redis class */ redis_ce = register_class_Redis(); redis_ce->create_object = create_redis_object; /* RedisArray class */ ZEND_MINIT(redis_array)(INIT_FUNC_ARGS_PASSTHRU); /* RedisCluster class */ ZEND_MINIT(redis_cluster)(INIT_FUNC_ARGS_PASSTHRU); /* RedisSentinel class */ ZEND_MINIT(redis_sentinel)(INIT_FUNC_ARGS_PASSTHRU); /* Register our cluster cache list item */ le_cluster_slot_cache = zend_register_list_destructors_ex(NULL, cluster_cache_dtor, "Redis cluster slot cache", module_number); /* RedisException class */ redis_exception_ce = register_class_RedisException(spl_ce_RuntimeException); #ifdef PHP_SESSION php_session_register_module(&ps_mod_redis); php_session_register_module(&ps_mod_redis_cluster); #endif /* Register resource destructors */ le_redis_pconnect = zend_register_list_destructors_ex(NULL, redis_connections_pool_dtor, "phpredis persistent connections pool", module_number); return SUCCESS; } static const char * get_available_serializers(void) { #ifdef HAVE_REDIS_JSON #ifdef HAVE_REDIS_IGBINARY #ifdef HAVE_REDIS_MSGPACK return "php, json, igbinary, msgpack"; #else return "php, json, igbinary"; #endif #else #ifdef HAVE_REDIS_MSGPACK return "php, json, msgpack"; #else return "php, json"; #endif #endif #else #ifdef HAVE_REDIS_IGBINARY #ifdef HAVE_REDIS_MSGPACK return "php, igbinary, msgpack"; #else return "php, igbinary"; #endif #else #ifdef HAVE_REDIS_MSGPACK return "php, msgpack"; #else return "php"; #endif #endif #endif } /** * PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(redis) { smart_str names = {0,}; php_info_print_table_start(); php_info_print_table_header(2, "Redis Support", "enabled"); php_info_print_table_row(2, "Redis Version", PHP_REDIS_VERSION); php_info_print_table_row(2, "Redis Sentinel Version", PHP_REDIS_SENTINEL_VERSION); #ifdef GIT_REVISION php_info_print_table_row(2, "Git revision", "$Id: " GIT_REVISION " $"); #endif php_info_print_table_row(2, "Available serializers", get_available_serializers()); #ifdef HAVE_REDIS_LZF smart_str_appends(&names, "lzf"); #endif #ifdef HAVE_REDIS_ZSTD if (names.s) { smart_str_appends(&names, ", "); } smart_str_appends(&names, "zstd"); #endif #ifdef HAVE_REDIS_LZ4 if (names.s) { smart_str_appends(&names, ", "); } smart_str_appends(&names, "lz4"); #endif if (names.s) { smart_str_0(&names); php_info_print_table_row(2, "Available compression", ZSTR_VAL(names.s)); } smart_str_free(&names); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* {{{ proto Redis Redis::__construct(array $options = null) Public constructor */ PHP_METHOD(Redis, __construct) { HashTable *opts = NULL; redis_object *redis; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(opts) ZEND_PARSE_PARAMETERS_END_EX(RETURN_THROWS()); redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, getThis()); redis->sock = redis_sock_create(ZEND_STRL("127.0.0.1"), 6379, 0, 0, 0, NULL, 0); if (opts != NULL && redis_sock_configure(redis->sock, opts) != SUCCESS) { RETURN_THROWS(); } } /* }}} */ /* {{{ proto Redis Redis::__destruct() Public Destructor */ PHP_METHOD(Redis,__destruct) { if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } // Grab our socket RedisSock *redis_sock; if ((redis_sock = redis_sock_get_instance(getThis(), 1)) == NULL) { RETURN_FALSE; } // If we think we're in MULTI mode, send a discard if (IS_MULTI(redis_sock)) { if (!IS_PIPELINE(redis_sock) && redis_sock->stream) { // 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; int af_unix; #ifdef ZTS /* not sure how in threaded mode this works so disabled persistence at * first */ persistent = 0; #endif if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|lds!lda", &object, redis_ce, &host, &host_len, &port, &timeout, &persistent_id, &persistent_id_len, &retry_interval, &read_timeout, &context) == FAILURE) { return FAILURE; } /* Disregard persistent_id if we're not opening a persistent connection */ if (!persistent) { persistent_id = NULL; } if (timeout > INT_MAX) { REDIS_VALUE_EXCEPTION("Invalid connect timeout"); return FAILURE; } if (read_timeout > INT_MAX) { REDIS_VALUE_EXCEPTION("Invalid read timeout"); return FAILURE; } if (retry_interval < 0L || retry_interval > INT_MAX) { REDIS_VALUE_EXCEPTION("Invalid retry interval"); return FAILURE; } /* Does the host look like a unix socket */ af_unix = (host_len > 0 && host[0] == '/') || (host_len > 6 && !strncasecmp(host, "unix://", sizeof("unix://") - 1)) || (host_len > 6 && !strncasecmp(host, "file://", sizeof("file://") - 1)); /* If it's not a unix socket, set to default */ if (port == -1 && !af_unix) { port = 6379; } redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, object); /* if there is a redis sock already we have to remove it */ if (redis->sock) { redis_sock_disconnect(redis->sock, 0, 1); redis_free_socket(redis->sock); } redis->sock = redis_sock_create(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_connect(redis->sock) != SUCCESS) { if (redis->sock->err) { REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0); } redis_free_socket(redis->sock); redis->sock = NULL; return FAILURE; } return SUCCESS; } /* {{{ proto long Redis::bitop(string op, string key, ...) */ PHP_METHOD(Redis, bitop) { REDIS_PROCESS_CMD(bitop, redis_long_response); } /* }}} */ /* {{{ proto long Redis::bitcount(string key, [int start], [int end]) */ PHP_METHOD(Redis, bitcount) { REDIS_PROCESS_CMD(bitcount, redis_long_response); } /* }}} */ /* {{{ proto integer Redis::bitpos(string key, int bit, [int start, int end]) */ PHP_METHOD(Redis, bitpos) { REDIS_PROCESS_CMD(bitpos, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::close() */ PHP_METHOD(Redis, close) { RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); if (redis_sock_disconnect(redis_sock, 1, 1) == SUCCESS) { RETURN_TRUE; } RETURN_FALSE; } /* }}} */ /* {{{ proto boolean Redis::set(string key, mixed val, long timeout, * [array opt) */ PHP_METHOD(Redis, set) { REDIS_PROCESS_CMD(set, redis_set_response); } /* {{{ proto boolean Redis::setex(string key, long expire, string value) */ PHP_METHOD(Redis, setex) { REDIS_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, redis_boolean_response); } /* {{{ proto boolean Redis::psetex(string key, long expire, string value) */ PHP_METHOD(Redis, psetex) { REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, redis_boolean_response); } /* {{{ proto boolean Redis::setnx(string key, string value) */ PHP_METHOD(Redis, setnx) { REDIS_PROCESS_KW_CMD("SETNX", redis_kv_cmd, redis_1_response); } /* }}} */ /* {{{ proto string Redis::getSet(string key, string value) */ PHP_METHOD(Redis, getset) { REDIS_PROCESS_KW_CMD("GETSET", redis_kv_cmd, redis_string_response); } /* }}} */ /* {{{ proto string Redis::randomKey() */ PHP_METHOD(Redis, randomKey) { REDIS_PROCESS_KW_CMD("RANDOMKEY", redis_empty_cmd, redis_ping_response); } /* }}} */ /* {{{ proto string Redis::echo(string msg) */ PHP_METHOD(Redis, echo) { REDIS_PROCESS_KW_CMD("ECHO", redis_str_cmd, redis_string_response); } /* }}} */ /* {{{ proto string Redis::rename(string key_src, string key_dst) */ PHP_METHOD(Redis, rename) { REDIS_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto string Redis::renameNx(string key_src, string key_dst) */ PHP_METHOD(Redis, renameNx) { REDIS_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, redis_1_response); } /* }}} */ /** {{{ proto bool Redis::reset() */ PHP_METHOD(Redis, reset) { char *response; int response_len; RedisSock *redis_sock; smart_string cmd = {0}; zend_bool ret = 0; if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) { RETURN_FALSE; } if (IS_PIPELINE(redis_sock)) { php_error_docref(NULL, E_ERROR, "Reset ins't allowed in pipeline mode!"); RETURN_FALSE; } redis_cmd_init_sstr(&cmd, 0, "RESET", 5); REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) { ret = REDIS_STRCMP_STATIC(response, response_len, "+RESET"); efree(response); } if (!ret) { if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } REDIS_THROW_EXCEPTION("Reset failed in multi mode!", 0); RETURN_ZVAL(getThis(), 1, 0); } free_reply_callbacks(redis_sock); redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; redis_sock->mode = ATOMIC; redis_sock->dbNumber = 0; redis_sock->watching = 0; RETURN_TRUE; } /* }}} */ /* {{{ proto string Redis::get(string key) */ PHP_METHOD(Redis, get) { REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response); } /* }}} */ /* {{{ proto string Redis::getDel(string key) */ PHP_METHOD(Redis, getDel) { REDIS_PROCESS_KW_CMD("GETDEL", redis_key_cmd, redis_string_response); } /* }}} */ /* {{{ proto string Redis::getEx(string key [, array $options = []]) */ PHP_METHOD(Redis, getEx) { REDIS_PROCESS_CMD(getex, redis_string_response); } /* }}} */ /* {{{ proto string Redis::ping() */ PHP_METHOD(Redis, ping) { REDIS_PROCESS_KW_CMD("PING", redis_opt_str_cmd, redis_read_variant_reply); } /* }}} */ /* {{{ proto boolean Redis::incr(string key [,int value]) */ PHP_METHOD(Redis, incr){ REDIS_PROCESS_CMD(incr, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::incrBy(string key ,int value) */ PHP_METHOD(Redis, incrBy){ REDIS_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, redis_long_response); } /* }}} */ /* {{{ proto float Redis::incrByFloat(string key, float value) */ PHP_METHOD(Redis, incrByFloat) { REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, redis_bulk_double_response); } /* }}} */ /* {{{ proto boolean Redis::decr(string key) */ PHP_METHOD(Redis, decr) { REDIS_PROCESS_CMD(decr, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::decrBy(string key ,int value) */ PHP_METHOD(Redis, decrBy){ REDIS_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, redis_long_response); } /* }}} */ /* {{{ proto array Redis::mget(array keys) */ PHP_METHOD(Redis, mget) { REDIS_PROCESS_CMD(mget, redis_sock_read_multibulk_reply); } /* {{{ proto boolean Redis::exists(string $key, string ...$more_keys) */ PHP_METHOD(Redis, exists) { REDIS_PROCESS_KW_CMD("EXISTS", redis_varkey_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::touch(string $key, string ...$more_keys) */ PHP_METHOD(Redis, touch) { REDIS_PROCESS_KW_CMD("TOUCH", redis_varkey_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::del(string key) */ PHP_METHOD(Redis, del) { REDIS_PROCESS_KW_CMD("DEL", redis_varkey_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}} * {{{ proto long Redis::unlink(array $keys) */ PHP_METHOD(Redis, unlink) { REDIS_PROCESS_KW_CMD("UNLINK", redis_varkey_cmd, redis_long_response); } PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock) { redis_sock->watching = 1; } PHP_REDIS_API int redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_set_watch); } /* {{{ proto boolean Redis::watch(string key1, string key2...) */ PHP_METHOD(Redis, watch) { REDIS_PROCESS_KW_CMD("WATCH", redis_varkey_cmd, redis_watch_response); } /* }}} */ PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock) { redis_sock->watching = 0; } PHP_REDIS_API int redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_clear_watch); } /* {{{ proto boolean Redis::unwatch() */ PHP_METHOD(Redis, unwatch) { REDIS_PROCESS_KW_CMD("UNWATCH", redis_empty_cmd, redis_unwatch_response); } /* }}} */ /* {{{ proto array Redis::keys(string pattern) */ PHP_METHOD(Redis, keys) { REDIS_PROCESS_KW_CMD("KEYS", redis_key_cmd, redis_mbulk_reply_raw); } /* }}} */ /* {{{ proto int Redis::type(string key) */ PHP_METHOD(Redis, type) { REDIS_PROCESS_KW_CMD("TYPE", redis_key_cmd, redis_type_response); } /* }}} */ /* {{{ proto mixed Redis::acl(string $op, ...) }}} */ PHP_METHOD(Redis, acl) { REDIS_PROCESS_CMD(acl, redis_acl_response); } /* {{{ proto long Redis::append(string key, string val) */ PHP_METHOD(Redis, append) { REDIS_PROCESS_KW_CMD("APPEND", redis_kv_cmd, redis_long_response); } /* }}} */ /* {{{ proto string Redis::GetRange(string key, long start, long end) */ PHP_METHOD(Redis, getRange) { REDIS_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd, redis_string_response); } /* }}} */ /* {{{ proto mixed Redis::lcs(string $key1, string $key2, ?array $options = NULL); */ PHP_METHOD(Redis, lcs) { REDIS_PROCESS_CMD(lcs, redis_read_variant_reply); } /* }}} */ /* {{{ proto string Redis::setRange(string key, long start, string value) */ PHP_METHOD(Redis, setRange) { REDIS_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::getbit(string key, long idx) */ PHP_METHOD(Redis, getBit) { REDIS_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::setbit(string key, long idx, bool|int value) */ PHP_METHOD(Redis, setBit) { REDIS_PROCESS_CMD(setbit, redis_long_response); } /* }}} */ /* {{{ proto long Redis::strlen(string key) */ PHP_METHOD(Redis, strlen) { REDIS_PROCESS_KW_CMD("STRLEN", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::lPush(string key , string value) */ PHP_METHOD(Redis, lPush) { REDIS_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::rPush(string key , string value) */ PHP_METHOD(Redis, rPush) { REDIS_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, redis_long_response); } /* }}} */ PHP_METHOD(Redis, lInsert) { REDIS_PROCESS_CMD(linsert, redis_long_response); } /* {{{ proto long Redis::lPushx(string key, mixed value) */ PHP_METHOD(Redis, lPushx) { REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::rPushx(string key, mixed value) */ PHP_METHOD(Redis, rPushx) { REDIS_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, redis_long_response); } /* }}} */ /* {{{ proto string Redis::lPop(string key, [int count = 0]) */ PHP_METHOD(Redis, lPop) { REDIS_PROCESS_KW_CMD("LPOP", redis_pop_cmd, redis_pop_response); } /* }}} */ /* {{{ proto string Redis::lPos(string key, mixed value, [array options = null]) */ PHP_METHOD(Redis, lPos) { REDIS_PROCESS_CMD(lpos, redis_lpos_response); } /* }}} */ /* {{{ proto string Redis::rPop(string key, [int count = 0]) */ PHP_METHOD(Redis, rPop) { REDIS_PROCESS_KW_CMD("RPOP", redis_pop_cmd, redis_pop_response); } /* }}} */ /* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout) */ PHP_METHOD(Redis, blPop) { REDIS_PROCESS_KW_CMD("BLPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout) */ PHP_METHOD(Redis, brPop) { REDIS_PROCESS_KW_CMD("BRPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto int Redis::lLen(string key) */ PHP_METHOD(Redis, lLen) { REDIS_PROCESS_KW_CMD("LLEN", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto string Redis::blMove(string source, string destination, string wherefrom, string whereto, double $timeout) */ PHP_METHOD(Redis, blmove) { REDIS_PROCESS_KW_CMD("BLMOVE", redis_lmove_cmd, redis_string_response); } /* {{{ proto string Redis::lMove(string source, string destination, string wherefrom, string whereto) */ PHP_METHOD(Redis, lMove) { REDIS_PROCESS_KW_CMD("LMOVE", redis_lmove_cmd, redis_string_response); } /* {{{ proto boolean Redis::lrem(string list, string value, int count = 0) */ PHP_METHOD(Redis, lrem) { REDIS_PROCESS_CMD(lrem, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::ltrim(string key , int start , int end) */ PHP_METHOD(Redis, ltrim) { REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto string Redis::lindex(string key , int index) */ PHP_METHOD(Redis, lindex) { REDIS_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, redis_string_response); } /* }}} */ /* {{{ proto array Redis::lrange(string key, int start , int end) */ PHP_METHOD(Redis, lrange) { REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto long Redis::sAdd(string key , mixed value) */ PHP_METHOD(Redis, sAdd) { REDIS_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::sAddArray(string key, array $values) */ PHP_METHOD(Redis, sAddArray) { REDIS_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, redis_long_response); } /* }}} */ /* {{{ proto int Redis::scard(string key) */ PHP_METHOD(Redis, scard) { REDIS_PROCESS_KW_CMD("SCARD", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::srem(string set, string value) */ PHP_METHOD(Redis, srem) { REDIS_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::sMove(string src, string dst, mixed value) */ PHP_METHOD(Redis, sMove) { REDIS_PROCESS_CMD(smove, redis_1_response); } /* }}} */ /* {{{ proto string Redis::sPop(string key) */ PHP_METHOD(Redis, sPop) { if (ZEND_NUM_ARGS() == 1) { REDIS_PROCESS_KW_CMD("SPOP", redis_key_cmd, redis_string_response); } else if (ZEND_NUM_ARGS() == 2) { REDIS_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, redis_sock_read_multibulk_reply); } else { ZEND_WRONG_PARAM_COUNT(); } } /* }}} */ /* {{{ proto string Redis::sRandMember(string key [int count]) */ PHP_METHOD(Redis, sRandMember) { REDIS_PROCESS_CMD(srandmember, redis_srandmember_response); } /* }}} */ /* {{{ proto boolean Redis::sismember(string set, string value) */ PHP_METHOD(Redis, sismember) { REDIS_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, redis_1_response); } /* }}} */ /* {{{ proto array Redis::sMembers(string set) */ PHP_METHOD(Redis, sMembers) { REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, redis_sock_read_multibulk_reply); } /* {{{ proto array Redis::sMisMember(string key, string member0, ...memberN) */ PHP_METHOD(Redis, sMisMember) { REDIS_PROCESS_KW_CMD("SMISMEMBER", redis_key_varval_cmd, redis_read_variant_reply); } /* }}} */ /* {{{ proto array Redis::sInter(string key0, ... string keyN) */ PHP_METHOD(Redis, sInter) { REDIS_PROCESS_KW_CMD("SINTER", redis_varkey_cmd, redis_sock_read_multibulk_reply); } /* }}} */ PHP_METHOD(Redis, sintercard) { REDIS_PROCESS_KW_CMD("SINTERCARD", redis_intercard_cmd, redis_long_response); } /* {{{ proto array Redis::sInterStore(string dst, string key0,...string keyN) */ PHP_METHOD(Redis, sInterStore) { REDIS_PROCESS_KW_CMD("SINTERSTORE", redis_varkey_cmd, redis_long_response); } /* }}} */ /* {{{ proto array Redis::sUnion(string key0, ... string keyN) */ PHP_METHOD(Redis, sUnion) { REDIS_PROCESS_KW_CMD("SUNION", redis_varkey_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto array Redis::sUnionStore(array|string $key, string ...$srckeys) */ PHP_METHOD(Redis, sUnionStore) { REDIS_PROCESS_KW_CMD("SUNIONSTORE", redis_varkey_cmd, redis_long_response); } /* }}} */ /* {{{ proto array Redis::sDiff(string key0, ... string keyN) */ PHP_METHOD(Redis, sDiff) { REDIS_PROCESS_KW_CMD("SDIFF", redis_varkey_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto array Redis::sDiffStore(string dst, string key0, ... keyN) */ PHP_METHOD(Redis, sDiffStore) { REDIS_PROCESS_KW_CMD("SDIFFSTORE", redis_varkey_cmd, redis_long_response); } /* }}} */ /* {{{ proto array Redis::sort(string key, array options) */ PHP_METHOD(Redis, sort) { REDIS_PROCESS_KW_CMD("SORT", redis_sort_cmd, redis_read_variant_reply); } /* {{{ proto array Redis::sort(string key, array options) */ PHP_METHOD(Redis, sort_ro) { REDIS_PROCESS_KW_CMD("SORT_RO", redis_sort_cmd, redis_read_variant_reply); } static 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_expire_cmd, redis_1_response); } /* }}} */ /* {{{ proto bool Redis::pexpire(string key, long ms) */ PHP_METHOD(Redis, pexpire) { REDIS_PROCESS_KW_CMD("PEXPIRE", redis_expire_cmd, redis_1_response); } /* }}} */ /* {{{ proto array Redis::expireAt(string key, int timestamp) */ PHP_METHOD(Redis, expireAt) { REDIS_PROCESS_KW_CMD("EXPIREAT", redis_expire_cmd, redis_1_response); } /* }}} */ /* {{{ proto array Redis::pexpireAt(string key, int timestamp) */ PHP_METHOD(Redis, pexpireAt) { REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_expire_cmd, redis_1_response); } /* }}} */ /* {{{ proto Redis::expiretime(string $key): int */ PHP_METHOD(Redis, expiretime) { REDIS_PROCESS_KW_CMD("EXPIRETIME", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto Redis::expiretime(string $key): int */ PHP_METHOD(Redis, pexpiretime) { REDIS_PROCESS_KW_CMD("PEXPIRETIME", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto array Redis::lSet(string key, int index, string value) */ PHP_METHOD(Redis, lSet) { REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto string Redis::save() */ PHP_METHOD(Redis, save) { REDIS_PROCESS_KW_CMD("SAVE", redis_empty_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto string Redis::bgSave() */ PHP_METHOD(Redis, bgSave) { REDIS_PROCESS_KW_CMD("BGSAVE", redis_empty_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto integer Redis::lastSave() */ PHP_METHOD(Redis, lastSave) { REDIS_PROCESS_KW_CMD("LASTSAVE", redis_empty_cmd, redis_long_response); } /* }}} */ /* {{{ proto bool Redis::failover([array to [,bool abort [,int timeout]]] ) */ PHP_METHOD(Redis, failover) { REDIS_PROCESS_CMD(failover, redis_boolean_response); } /* }}} */ /* {{{ proto bool Redis::flushDB([bool async]) */ PHP_METHOD(Redis, flushDB) { REDIS_PROCESS_KW_CMD("FLUSHDB", redis_flush_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto bool Redis::flushAll([bool async]) */ PHP_METHOD(Redis, flushAll) { REDIS_PROCESS_KW_CMD("FLUSHALL", redis_flush_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto mixed Redis::function(string op, mixed ...args) */ PHP_METHOD(Redis, function) { REDIS_PROCESS_CMD(function, redis_function_response) } /* {{{ proto int Redis::dbSize() */ PHP_METHOD(Redis, dbSize) { REDIS_PROCESS_KW_CMD("DBSIZE", redis_empty_cmd, redis_long_response); } /* }}} */ /* {{{ proto bool Redis::auth(string passwd) */ PHP_METHOD(Redis, auth) { REDIS_PROCESS_CMD(auth, redis_boolean_response); } /* }}} */ /* {{{ proto long Redis::persist(string key) */ PHP_METHOD(Redis, persist) { REDIS_PROCESS_KW_CMD("PERSIST", redis_key_cmd, redis_1_response); } /* }}} */ /* {{{ proto long Redis::ttl(string key) */ PHP_METHOD(Redis, ttl) { REDIS_PROCESS_KW_CMD("TTL", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::pttl(string key) */ PHP_METHOD(Redis, pttl) { REDIS_PROCESS_KW_CMD("PTTL", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto array Redis::info() */ PHP_METHOD(Redis, info) { REDIS_PROCESS_CMD(info, redis_info_response); } /* }}} */ /* {{{ proto bool Redis::select(long dbNumber) */ PHP_METHOD(Redis, select) { REDIS_PROCESS_CMD(select, redis_select_response); } /* }}} */ /* {{{ proto bool Redis::swapdb(long srcdb, long dstdb) */ PHP_METHOD(Redis, swapdb) { REDIS_PROCESS_KW_CMD("SWAPDB", redis_long_long_cmd, redis_boolean_response); } /* {{{ proto bool Redis::move(string key, long dbindex) */ PHP_METHOD(Redis, move) { REDIS_PROCESS_KW_CMD("MOVE", redis_key_long_cmd, redis_1_response); } /* }}} */ /* {{{ proto bool Redis::mset(array (key => value, ...)) */ PHP_METHOD(Redis, mset) { REDIS_PROCESS_KW_CMD("MSET", redis_mset_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto bool Redis::msetnx(array (key => value, ...)) */ PHP_METHOD(Redis, msetnx) { REDIS_PROCESS_KW_CMD("MSETNX", redis_mset_cmd, redis_1_response); } /* }}} */ /* {{{ proto string Redis::rpoplpush(string srckey, string dstkey) */ PHP_METHOD(Redis, rpoplpush) { REDIS_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, redis_string_response); } /* }}} */ /* {{{ proto string Redis::brpoplpush(string src, string dst, int timeout) */ PHP_METHOD(Redis, brpoplpush) { REDIS_PROCESS_CMD(brpoplpush, redis_string_response); } /* }}} */ /* {{{ proto long Redis::zAdd(string key, int score, string value) */ PHP_METHOD(Redis, zAdd) { REDIS_PROCESS_CMD(zadd, redis_zadd_response); } /* }}} */ /* {{{ proto array Redis::zRandMember(string key, array options) */ PHP_METHOD(Redis, zRandMember) { REDIS_PROCESS_CMD(zrandmember, redis_zrandmember_response); } /* }}} */ /* {{{ proto array Redis::zRange(string key,int start,int end,bool scores = 0) */ PHP_METHOD(Redis, zRange) { REDIS_PROCESS_KW_CMD("ZRANGE", redis_zrange_cmd, redis_zrange_response); } /* }}} */ PHP_METHOD(Redis, zrangestore) { REDIS_PROCESS_KW_CMD("ZRANGESTORE", redis_zrange_cmd, redis_long_response); } /* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores = 0) */ PHP_METHOD(Redis, zRevRange) { REDIS_PROCESS_KW_CMD("ZREVRANGE", redis_zrange_cmd, redis_zrange_response); } /* }}} */ /* {{{ proto array Redis::zRangeByScore(string k,string s,string e,array opt) */ PHP_METHOD(Redis, zRangeByScore) { REDIS_PROCESS_KW_CMD("ZRANGEBYSCORE", redis_zrange_cmd, redis_zrange_response); } /* }}} */ /* {{{ proto array Redis::zRevRangeByScore(string key, string start, string end, * array options) */ PHP_METHOD(Redis, zRevRangeByScore) { REDIS_PROCESS_KW_CMD("ZREVRANGEBYSCORE", redis_zrange_cmd, redis_zrange_response); } /* }}} */ /* {{{ proto array Redis::zRangeByLex(string key, string min, string max, [ * offset, limit]) */ PHP_METHOD(Redis, zRangeByLex) { REDIS_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, redis_sock_read_multibulk_reply); } /* }}} */ PHP_METHOD(Redis, zRevRangeByLex) { REDIS_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto long Redis::zLexCount(string key, string min, string max) */ PHP_METHOD(Redis, zLexCount) { REDIS_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::zRemRangeByLex(string key, string min, string max) */ PHP_METHOD(Redis, zRemRangeByLex) { REDIS_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::zRem(string key, string member) */ PHP_METHOD(Redis, zRem) { REDIS_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::zRemRangeByScore(string k, string s, string e) */ PHP_METHOD(Redis, zRemRangeByScore) { REDIS_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::zRemRangeByRank(string key, long start, long end) */ PHP_METHOD(Redis, zRemRangeByRank) { REDIS_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd, redis_long_response); } /* }}} */ /* {{{ proto array Redis::zCount(string key, string start , string end) */ PHP_METHOD(Redis, zCount) { REDIS_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::zCard(string key) */ PHP_METHOD(Redis, zCard) { REDIS_PROCESS_KW_CMD("ZCARD", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto double Redis::zScore(string key, mixed member) */ PHP_METHOD(Redis, zScore) { REDIS_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, redis_bulk_double_response); } /* }}} */ /* {{{ proto array Redis::zMscore(string key, string member0, ...memberN) */ PHP_METHOD(Redis, zMscore) { REDIS_PROCESS_KW_CMD("ZMSCORE", redis_key_varval_cmd, redis_mbulk_reply_double); } /* }}} */ /* {{{ proto long Redis::zRank(string key, string member) */ PHP_METHOD(Redis, zRank) { REDIS_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::zRevRank(string key, string member) */ PHP_METHOD(Redis, zRevRank) { REDIS_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, redis_long_response); } /* }}} */ /* {{{ proto double Redis::zIncrBy(string key, double value, mixed member) */ PHP_METHOD(Redis, zIncrBy) { REDIS_PROCESS_CMD(zincrby, redis_bulk_double_response); } /* }}} */ /* {{{ proto array Redis::zdiff(array keys, array options) */ PHP_METHOD(Redis, zdiff) { REDIS_PROCESS_CMD(zdiff, redis_zdiff_response); } /* }}} */ /* {{{ proto array Redis::zinter(array keys, array|null weights, array options) */ PHP_METHOD(Redis, zinter) { REDIS_PROCESS_KW_CMD("ZINTER", redis_zinterunion_cmd, redis_zdiff_response); } /* }}} */ PHP_METHOD(Redis, zintercard) { REDIS_PROCESS_KW_CMD("ZINTERCARD", redis_intercard_cmd, redis_long_response); } /* {{{ proto array Redis::zunion(array keys, array|null weights, array options) */ PHP_METHOD(Redis, zunion) { REDIS_PROCESS_KW_CMD("ZUNION", redis_zinterunion_cmd, redis_zdiff_response); } /* }}} */ /* {{{ proto array Redis::zdiffstore(string destination, array keys) */ PHP_METHOD(Redis, zdiffstore) { REDIS_PROCESS_CMD(zdiffstore, redis_long_response); } /* }}} */ /* zinterstore */ PHP_METHOD(Redis, zinterstore) { REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinterunionstore_cmd, redis_long_response); } /* zunionstore */ PHP_METHOD(Redis, zunionstore) { REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinterunionstore_cmd, redis_long_response); } /* {{{ proto array Redis::zPopMax(string key) */ PHP_METHOD(Redis, zPopMax) { if (ZEND_NUM_ARGS() == 1) { REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl); } else if (ZEND_NUM_ARGS() == 2) { REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl); } else { ZEND_WRONG_PARAM_COUNT(); } } /* }}} */ /* {{{ proto array Redis::zPopMin(string key) */ PHP_METHOD(Redis, zPopMin) { if (ZEND_NUM_ARGS() == 1) { REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl); } else if (ZEND_NUM_ARGS() == 2) { REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl); } else { ZEND_WRONG_PARAM_COUNT(); } } /* }}} */ /* {{{ proto Redis::bzPopMax(Array(keys) [, timeout]): Array */ PHP_METHOD(Redis, bzPopMax) { REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto Redis::bzPopMin(Array(keys) [, timeout]): Array */ PHP_METHOD(Redis, bzPopMin) { REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto Redis|array|false Redis::lmpop(array $keys, string $from, int $count = 1) */ PHP_METHOD(Redis, lmpop) { REDIS_PROCESS_KW_CMD("LMPOP", redis_mpop_cmd, redis_mpop_response); } /* }}} */ /* {{{ proto Redis|array|false Redis::blmpop(double $timeout, array $keys, string $from, int $count = 1) */ PHP_METHOD(Redis, blmpop) { REDIS_PROCESS_KW_CMD("BLMPOP", redis_mpop_cmd, redis_mpop_response); } /* }}} */ /* {{{ proto Redis|array|false Redis::zmpop(array $keys, string $from, int $count = 1) */ PHP_METHOD(Redis, zmpop) { REDIS_PROCESS_KW_CMD("ZMPOP", redis_mpop_cmd, redis_mpop_response); } /* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, string $from, int $count = 1) */ PHP_METHOD(Redis, bzmpop) { REDIS_PROCESS_KW_CMD("BZMPOP", redis_mpop_cmd, redis_mpop_response); } /* }}} */ /* hashes */ /* {{{ proto long Redis::hset(string key, string mem, string val) */ PHP_METHOD(Redis, hSet) { REDIS_PROCESS_CMD(hset, redis_long_response); } /* }}} */ /* {{{ proto bool Redis::hSetNx(string key, string mem, string val) */ PHP_METHOD(Redis, hSetNx) { REDIS_PROCESS_CMD(hsetnx, redis_1_response); } /* }}} */ /* {{{ proto string Redis::hget(string key, string mem) */ PHP_METHOD(Redis, hGet) { REDIS_PROCESS_KW_CMD("HGET", redis_key_str_cmd, redis_string_response); } /* }}} */ /* {{{ proto 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 bool Redis::hRandField(string key, [array $options]) */ PHP_METHOD(Redis, hRandField) { REDIS_PROCESS_CMD(hrandfield, redis_hrandfield_response); } /* }}} */ /* {{{ proto long Redis::hstrlen(string key, string field) */ PHP_METHOD(Redis, hStrLen) { REDIS_PROCESS_CMD(hstrlen, redis_long_response); } /* }}} */ /* flag : get, set {ATOMIC, MULTI, PIPELINE} */ PHP_METHOD(Redis, multi) { RedisSock *redis_sock; char *resp; int resp_len; zval *object; zend_long multi_value = MULTI; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|l", &object, redis_ce, &multi_value) == FAILURE) { RETURN_FALSE; } /* if the flag is activated, send the command, the reply will be "QUEUED" * or -ERR */ if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } if (multi_value == PIPELINE) { /* Cannot enter pipeline mode in a MULTI block */ if (IS_MULTI(redis_sock)) { php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!"); RETURN_FALSE; } /* Enable PIPELINE if we're not already in one */ if (IS_ATOMIC(redis_sock)) { REDIS_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)) { if (IS_PIPELINE(redis_sock)) { PIPELINE_ENQUEUE_COMMAND(RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD) - 1); REDIS_SAVE_CALLBACK(NULL, NULL); REDIS_ENABLE_MODE(redis_sock, MULTI); } else { SOCKET_WRITE_COMMAND(redis_sock, RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD) - 1) 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; } PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { char inbuf[4096]; size_t len; if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || *inbuf != TYPE_MULTIBULK || atoi(inbuf + 1) < 0 ) { return FAILURE; } array_init(z_tab); return redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab); } /* exec */ PHP_METHOD(Redis, exec) { RedisSock *redis_sock; int ret; zval *object, z_ret; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE || (redis_sock = redis_sock_get(object, 0)) == NULL ) { RETURN_FALSE; } ZVAL_FALSE(&z_ret); if (IS_MULTI(redis_sock)) { if (IS_PIPELINE(redis_sock)) { PIPELINE_ENQUEUE_COMMAND(RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1); REDIS_SAVE_CALLBACK(NULL, NULL); REDIS_DISABLE_MODE(redis_sock, MULTI); RETURN_ZVAL(getThis(), 1, 0); } SOCKET_WRITE_COMMAND(redis_sock, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1) ret = redis_sock_read_multibulk_multi_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret); free_reply_callbacks(redis_sock); REDIS_DISABLE_MODE(redis_sock, MULTI); redis_sock->watching = 0; if (ret < 0) { zval_dtor(&z_ret); ZVAL_FALSE(&z_ret); } } if (IS_PIPELINE(redis_sock)) { if (redis_sock->pipeline_cmd == NULL) { /* Empty array when no command was run. */ array_init(&z_ret); } else { if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd), ZSTR_LEN(redis_sock->pipeline_cmd)) < 0) { ZVAL_FALSE(&z_ret); } else { array_init(&z_ret); if (redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret) != SUCCESS) { zval_dtor(&z_ret); ZVAL_FALSE(&z_ret); } } zend_string_release(redis_sock->pipeline_cmd); redis_sock->pipeline_cmd = NULL; } free_reply_callbacks(redis_sock); REDIS_DISABLE_MODE(redis_sock, PIPELINE); } RETURN_ZVAL(&z_ret, 0, 1); } PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock) { char *resp; int resp_len, ret = FAILURE; if ((resp = redis_sock_read(redis_sock, &resp_len)) != NULL) { if (strncmp(resp, "+QUEUED", 7) == 0) { ret = SUCCESS; } efree(resp); } return ret; } PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { fold_item *fi; 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 || strncmp(inbuf, "+OK", 3) != 0) { return FAILURE; } while ((fi = fi->next) && fi->fun) { if (redis_response_enqueued(redis_sock) != SUCCESS) { return FAILURE; } } if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return FAILURE; } zval z_ret; array_init(&z_ret); add_next_index_zval(z_tab, &z_ret); int num = atol(inbuf + 1); if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) < 0) { return FAILURE; } if (fi) fi = fi->next; } redis_sock->current = fi; return SUCCESS; } PHP_METHOD(Redis, pipeline) { RedisSock *redis_sock; zval *object; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE || (redis_sock = redis_sock_get(object, 0)) == NULL ) { RETURN_FALSE; } /* User cannot enter MULTI mode if already in a pipeline */ if (IS_MULTI(redis_sock)) { php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!"); RETURN_FALSE; } /* Enable pipeline mode unless we're already in that mode in which case this * is just a NO OP */ if (IS_ATOMIC(redis_sock)) { /* 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 */ 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::ssubscribe(Array(shardchannel1, shardchannel2, ... shardchannelN)) */ PHP_METHOD(Redis, ssubscribe) { REDIS_PROCESS_KW_CMD("SSUBSCRIBE", 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); } /** * [ps]unsubscribe channel_0 channel_1 ... channel_n * [ps]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); } PHP_METHOD(Redis, sunsubscribe) { REDIS_PROCESS_KW_CMD("SUNSUBSCRIBE", 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); } /* }}} */ /* {{{ public function slaveof(string $host = NULL, int $port = NULL): Redis|bool }}} */ PHP_METHOD(Redis, slaveof) { REDIS_PROCESS_KW_CMD("SLAVEOF", redis_replicaof_cmd, redis_boolean_response); } /* }}} */ /* {{{ public function replicaof(string $host = NULL, int $port = NULL): Redis|bool }}} */ PHP_METHOD(Redis, replicaof) { REDIS_PROCESS_KW_CMD("REPLICAOF", redis_replicaof_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto string Redis::object(key) */ PHP_METHOD(Redis, object) { REDIS_PROCESS_CMD(object, redis_object_response); } /* }}} */ /* {{{ proto string Redis::getOption($option) */ PHP_METHOD(Redis, getOption) { RedisSock *redis_sock; if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL); } /* }}} */ /* {{{ proto string Redis::setOption(string $option, mixed $value) */ PHP_METHOD(Redis, setOption) { RedisSock *redis_sock; if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL); } /* }}} */ /* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */ /* {{{ proto public function config(string $op, string ...$args) }}} */ // CONFIG SET/GET PHP_METHOD(Redis, config) { REDIS_PROCESS_CMD(config, redis_config_response); } /* }}} */ /* {{{ proto boolean Redis::slowlog(string arg, [int option]) */ PHP_METHOD(Redis, slowlog) { REDIS_PROCESS_CMD(slowlog, redis_read_variant_reply); } /* {{{ proto Redis::wait(int num_slaves, int ms) }}} */ PHP_METHOD(Redis, wait) { REDIS_PROCESS_KW_CMD("WAIT", redis_long_long_cmd, redis_long_response); } /* * {{{ proto Redis::pubsub("channels", pattern); * proto Redis::pubsub("numsub", Array channels); * proto Redis::pubsub("numpat"); }}} */ PHP_METHOD(Redis, pubsub) { REDIS_PROCESS_CMD(pubsub, redis_pubsub_response); } /* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */ PHP_METHOD(Redis, eval) { REDIS_PROCESS_KW_CMD("EVAL", redis_eval_cmd, redis_read_raw_variant_reply); } /* {{{ proto variant Redis::eval_ro(string script, [array keys, long num_keys]) */ PHP_METHOD(Redis, eval_ro) { REDIS_PROCESS_KW_CMD("EVAL_RO", redis_eval_cmd, redis_read_raw_variant_reply); } /* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */ PHP_METHOD(Redis, evalsha) { REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_raw_variant_reply); } /* {{{ proto variant Redis::evalsha_ro(string sha1, [array keys, long num_keys]) */ PHP_METHOD(Redis, evalsha_ro) { REDIS_PROCESS_KW_CMD("EVALSHA_RO", redis_eval_cmd, redis_read_raw_variant_reply); } /* {{{ proto variant Redis::fcall(string fn [, array keys [, array args]]) */ PHP_METHOD(Redis, fcall) { REDIS_PROCESS_KW_CMD("FCALL", redis_fcall_cmd, redis_read_raw_variant_reply); } /* {{{ proto variant Redis::fcall_ro(string fn [, array keys [, array args]]) */ PHP_METHOD(Redis, fcall_ro) { REDIS_PROCESS_KW_CMD("FCALL_RO", redis_fcall_cmd, redis_read_raw_variant_reply); } /* {{{ public function script($args...): mixed }}} */ PHP_METHOD(Redis, script) { REDIS_PROCESS_CMD(script, redis_read_variant_reply); } /* {{{ proto DUMP key */ PHP_METHOD(Redis, dump) { REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_string_response); } /* }}} */ /* {{{ proto Redis::restore(ttl, key, value) */ PHP_METHOD(Redis, restore) { REDIS_PROCESS_CMD(restore, redis_boolean_response); } /* }}} */ /* {{{ proto Redis::debug(string key) */ PHP_METHOD(Redis, debug) { REDIS_PROCESS_KW_CMD("DEBUG", redis_key_cmd, redis_string_response); } /* }}} */ /* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, * bool replace]) */ PHP_METHOD(Redis, migrate) { REDIS_PROCESS_CMD(migrate, redis_boolean_response); } /* {{{ proto Redis::_prefix(key) */ PHP_METHOD(Redis, _prefix) { RedisSock *redis_sock; if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } /* {{{ proto Redis::_serialize(value) */ PHP_METHOD(Redis, _serialize) { RedisSock *redis_sock; // Grab socket if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } /* {{{ proto Redis::_unserialize(value) */ PHP_METHOD(Redis, _unserialize) { RedisSock *redis_sock; // Grab socket if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, redis_exception_ce); } PHP_METHOD(Redis, _compress) { RedisSock *redis_sock; // Grab socket if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_compress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } PHP_METHOD(Redis, _uncompress) { RedisSock *redis_sock; // Grab socket if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_uncompress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, redis_exception_ce); } PHP_METHOD(Redis, _pack) { RedisSock *redis_sock; // Grab socket if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } PHP_METHOD(Redis, _unpack) { RedisSock *redis_sock; // Grab socket if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } redis_unpack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } /* {{{ proto Redis::getLastError() */ PHP_METHOD(Redis, getLastError) { zval *object; RedisSock *redis_sock; // Grab our object if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } // Grab socket if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) { RETURN_FALSE; } /* Return our last error or NULL if we don't have one */ if (redis_sock->err) { RETURN_STRINGL(ZSTR_VAL(redis_sock->err), ZSTR_LEN(redis_sock->err)); } RETURN_NULL(); } /* {{{ proto Redis::clearLastError() */ PHP_METHOD(Redis, clearLastError) { zval *object; RedisSock *redis_sock; // Grab our object if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } // Grab socket if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) { RETURN_FALSE; } // 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) { zval *object; RedisSock *redis_sock; /* Grab our object */ if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } /* Grab socket */ if ((redis_sock = redis_sock_get_instance(object, 1)) == NULL) { RETURN_FALSE; } RETURN_BOOL(redis_sock->status >= REDIS_SOCK_STATUS_CONNECTED); } /* {{{ proto Redis::getHost() */ PHP_METHOD(Redis, getHost) { RedisSock *redis_sock; if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { RETURN_STRINGL(ZSTR_VAL(redis_sock->host), ZSTR_LEN(redis_sock->host)); } else { RETURN_FALSE; } } /* {{{ proto Redis::getPort() */ PHP_METHOD(Redis, getPort) { RedisSock *redis_sock; if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { /* Return our port */ RETURN_LONG(redis_sock->port); } else { RETURN_FALSE; } } /* {{{ proto Redis::getDBNum */ PHP_METHOD(Redis, getDBNum) { RedisSock *redis_sock; if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { /* Return our db number */ RETURN_LONG(redis_sock->dbNumber); } else { RETURN_FALSE; } } PHP_METHOD(Redis, getTransferredBytes) { RedisSock *redis_sock; if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_THROWS(); } array_init_size(return_value, 2); add_next_index_long(return_value, redis_sock->txBytes); add_next_index_long(return_value, redis_sock->rxBytes); } PHP_METHOD(Redis, clearTransferredBytes) { RedisSock *redis_sock; if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_THROWS(); } redis_sock->txBytes = 0; redis_sock->rxBytes = 0; } /* {{{ proto Redis::getTimeout */ PHP_METHOD(Redis, getTimeout) { RedisSock *redis_sock; if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { RETURN_DOUBLE(redis_sock->timeout); } else { RETURN_FALSE; } } /* {{{ proto Redis::getReadTimeout */ PHP_METHOD(Redis, getReadTimeout) { RedisSock *redis_sock; if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { RETURN_DOUBLE(redis_sock->read_timeout); } else { RETURN_FALSE; } } /* {{{ proto Redis::getPersistentID */ PHP_METHOD(Redis, getPersistentID) { RedisSock *redis_sock; if ((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) { RETURN_FALSE; } else if (redis_sock->persistent_id == NULL) { RETURN_NULL(); } RETURN_STRINGL(ZSTR_VAL(redis_sock->persistent_id), ZSTR_LEN(redis_sock->persistent_id)); } /* {{{ proto Redis::getAuth */ PHP_METHOD(Redis, getAuth) { RedisSock *redis_sock; zval zret; if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); if (redis_sock == NULL) RETURN_FALSE; if (redis_sock->user && redis_sock->pass) { array_init(&zret); add_next_index_str(&zret, zend_string_copy(redis_sock->user)); add_next_index_str(&zret, zend_string_copy(redis_sock->pass)); RETURN_ZVAL(&zret, 0, 0); } else if (redis_sock->pass) { RETURN_STR_COPY(redis_sock->pass); } else { RETURN_NULL(); } } /* {{{ proto mixed Redis::client(string $command, [ $arg1 ... $argN]) */ PHP_METHOD(Redis, client) { REDIS_PROCESS_CMD(client, redis_client_response); } /* }}} */ /* {{{ 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_command_response); } /* }}} */ /* {{{ proto array Redis::copy(string $source, string $destination, array $options = null) */ PHP_METHOD(Redis, copy) { REDIS_PROCESS_CMD(copy, redis_1_response) } /* }}} */ /* Helper to format any combination of SCAN arguments */ 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(), "Os!z/|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_CMD(geoadd, redis_long_response); } PHP_METHOD(Redis, geohash) { REDIS_PROCESS_KW_CMD("GEOHASH", redis_key_varval_cmd, redis_mbulk_reply_raw); } PHP_METHOD(Redis, geopos) { REDIS_PROCESS_KW_CMD("GEOPOS", redis_key_varval_cmd, redis_read_variant_reply); } PHP_METHOD(Redis, geodist) { REDIS_PROCESS_CMD(geodist, redis_bulk_double_response); } PHP_METHOD(Redis, georadius) { REDIS_PROCESS_KW_CMD("GEORADIUS", redis_georadius_cmd, redis_read_variant_reply); } PHP_METHOD(Redis, georadius_ro) { REDIS_PROCESS_KW_CMD("GEORADIUS_RO", redis_georadius_cmd, redis_read_variant_reply); } PHP_METHOD(Redis, georadiusbymember) { REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER", redis_georadiusbymember_cmd, redis_read_variant_reply); } PHP_METHOD(Redis, georadiusbymember_ro) { REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, redis_read_variant_reply); } PHP_METHOD(Redis, geosearch) { REDIS_PROCESS_CMD(geosearch, redis_geosearch_response); } PHP_METHOD(Redis, geosearchstore) { REDIS_PROCESS_CMD(geosearchstore, redis_long_response); } /* * Streams */ PHP_METHOD(Redis, xack) { REDIS_PROCESS_CMD(xack, redis_long_response); } PHP_METHOD(Redis, xadd) { REDIS_PROCESS_CMD(xadd, redis_read_variant_reply); } PHP_METHOD(Redis, xautoclaim) { REDIS_PROCESS_CMD(xautoclaim, redis_xclaim_reply); } PHP_METHOD(Redis, xclaim) { REDIS_PROCESS_CMD(xclaim, redis_xclaim_reply); } PHP_METHOD(Redis, xdel) { REDIS_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, redis_long_response); } PHP_METHOD(Redis, 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: */ redis-6.0.2/redis.stub.php0000644000175000000120000054424714515245367016233 0ustar pyatsukhnenkowheel 'localhost', * 'port' => 6379, * 'readTimeout' => 2.5, * 'connectTimeout' => 2.5, * 'persistent' => true, * * // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass'] * 'auth' => ['phpredis', 'phpredis'], * * // See PHP stream options for valid SSL configuration settings. * 'ssl' => ['verify_peer' => false], * * // How quickly to retry a connection after we time out or it closes. * // Note that this setting is overridden by 'backoff' strategies. * 'retryInterval' => 100, * * // Which backoff algorithm to use. 'decorrelated jitter' is * // likely the best one for most solution, but there are many * // to choose from: * // REDIS_BACKOFF_ALGORITHM_DEFAULT * // REDIS_BACKOFF_ALGORITHM_CONSTANT * // REDIS_BACKOFF_ALGORITHM_UNIFORM * // REDIS_BACKOFF_ALGORITHM_EXPONENTIAL * // REDIS_BACKOFF_ALGORITHM_FULL_JITTER * // REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER * // REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER * // 'base', and 'cap' are in milliseconds and represent the first * // delay redis will use when reconnecting, and the maximum delay * // we will reach while retrying. * 'backoff' => [ * 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, * 'base' => 500, * 'cap' => 750, * ] * ]; * * Note: If you do wish to connect via the constructor, only 'host' is * strictly required, which will cause PhpRedis to connect to that * host on Redis' default port (6379). * * * @see Redis::connect() * @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ * @param array $options * * @return Redis */ public function __construct(?array $options = null); public function __destruct(); /** * Compress a value with the currently configured compressor as set with * Redis::setOption(). * * @see Redis::setOption() * * @param string $value The value to be compressed * @return string The compressed result * */ public function _compress(string $value): string; /** * Uncompress the provided argument that has been compressed with the * currently configured compressor as set with Redis::setOption(). * * @see Redis::setOption() * * @param string $value The compressed value to uncompress. * @return string The uncompressed result. * */ public function _uncompress(string $value): string; /** * Prefix the passed argument with the currently set key prefix as set * with Redis::setOption(). * * @param string $key The key/string to prefix * @return string The prefixed string * */ public function _prefix(string $key): string; /** * Serialize the provided value with the currently set serializer as set * with Redis::setOption(). * * @see Redis::setOption() * * @param mixed $value The value to serialize * @return string The serialized result * */ public function _serialize(mixed $value): string; /** * Unserialize the passed argument with the currently set serializer as set * with Redis::setOption(). * * @see Redis::setOption() * * @param string $value The value to unserialize * @return mixed The unserialized result * */ public function _unserialize(string $value): mixed; /** * Pack the provided value with the configured serializer and compressor * as set with Redis::setOption(). * * @param mixed $value The value to pack * @return string The packed result having been serialized and * compressed. */ public function _pack(mixed $value): string; /** * Unpack the provided value with the configured compressor and serializer * as set with Redis::setOption(). * * @param string $value The value which has been serialized and compressed. * @return mixed The uncompressed and eserialized value. * */ public function _unpack(string $value): mixed; public function acl(string $subcmd, string ...$args): mixed; /** * Append data to a Redis STRING key. * * @param string $key The key in question * @param mixed $value The data to append to the key. * * @return Redis|int|false The new string length of the key or false on failure. * * @see https://redis.io/commands/append * * @example * $redis->set('foo', 'hello); * $redis->append('foo', 'world'); */ public function append(string $key, mixed $value): Redis|int|false; /** * Authenticate a Redis connection after its been established. * * $redis->auth('password'); * $redis->auth(['password']); * $redis->auth(['username', 'password']); * * @see https://redis.io/commands/auth * * @param mixed $credentials A string password, or an array with one or two string elements. * @return Redis|bool Whether the AUTH was successful. * */ public function auth(#[\SensitiveParameter] mixed $credentials): Redis|bool; /** * Execute a save of the Redis database in the background. * * @see https://redis.io/commands/bgsave * * @return Redis|bool Whether the command was successful. */ public function bgSave(): Redis|bool; /** * Asynchronously rewrite Redis' append-only file * * @see https://redis.io/commands/bgrewriteaof * * @return Redis|bool Whether the command was successful. */ public function bgrewriteaof(): Redis|bool; /** * Count the number of set bits in a Redis string. * * @see https://redis.io/commands/bitcount/ * * @param string $key The key in question (must be a string key) * @param int $start The index where Redis should start counting. If omitted it * defaults to zero, which means the start of the string. * @param int $end The index where Redis should stop counting. If omitted it * defaults to -1, meaning the very end of the string. * * @param bool $bybit Whether or not Redis should treat $start and $end as bit * positions, rather than bytes. * * @return Redis|int|false The number of bits set in the requested range. * */ public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false; public function bitop(string $operation, string $deskey, string $srckey, string ...$other_keys): Redis|int|false; /** * Return the position of the first bit set to 0 or 1 in a string. * * @see https://redis.io/commands/bitpos/ * * @param string $key The key to check (must be a string) * @param bool $bit Whether to look for an unset (0) or set (1) bit. * @param int $start Where in the string to start looking. * @param int $end Where in the string to stop looking. * @param bool $bybit If true, Redis will treat $start and $end as BIT values and not bytes, so if start * was 0 and end was 2, Redis would only search the first two bits. * * @return Redis|int|false The position of the first set or unset bit. **/ public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false; /** * Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified * timeout. This method may be called in two distinct ways, of which examples are provided below. * * @see https://redis.io/commands/blpop/ * * @param string|array $key_or_keys This can either be a string key or an array of one or more * keys. * @param string|float|int $timeout_or_key If the previous argument was a string key, this can either * be an additional key, or the timeout you wish to send to * the command. * * @return Redis|array|null|false Can return various things depending on command and data in Redis. * * @example * $redis->blPop('list1', 'list2', 'list3', 1.5); * $relay->blPop(['list1', 'list2', 'list3'], 1.5); */ public function blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false; /** * Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout. * The calling convention is identical to Redis::blPop() so see that documentation for more details. * * @see https://redis.io/commands/brpop/ * @see Redis::blPop() * */ public function brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false; /** * Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list, * optionally blocking up to a specified timeout. * * @see https://redis.io/commands/brpoplpush/ * * @param string $src The source list * @param string $dst The destination list * @param int|float $timeout The number of seconds to wait. Note that you must be connected * to Redis >= 6.0.0 to send a floating point timeout. * */ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis|string|false; /** * POP the maximum scoring element off of one or more sorted sets, blocking up to a specified * timeout if no elements are available. * * Following are examples of the two main ways to call this method. * * **NOTE**: We reccomend calling this function with an array and a timeout as the other strategy * may be deprecated in future versions of PhpRedis * * @see https://redis.io/commands/bzpopmax * * @param string|array $key_or_keys Either a string key or an array of one or more keys. * @param string|int $timeout_or_key If the previous argument was an array, this argument * must be a timeout value. Otherwise it could also be * another key. * @param mixed $extra_args Can consist of additional keys, until the last argument * which needs to be a timeout. * * @return Redis|array|false The popped elements. * * @example * $redis->bzPopMax('key1', 'key2', 'key3', 1.5); * $redis->bzPopMax(['key1', 'key2', 'key3'], 1.5); */ public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false; /** * POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout * if no elements are available * * This command is identical in semantics to bzPopMax so please see that method for more information. * * @see https://redis.io/commands/bzpopmin * @see Redis::bzPopMax() * */ public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false; /** * POP one or more elements from one or more sorted sets, blocking up to a specified amount of time * when no elements are available. * * @param float $timeout How long to block if there are no element available * @param array $keys The sorted sets to pop from * @param string $from The string 'MIN' or 'MAX' (case insensitive) telling Redis whether you wish to * pop the lowest or highest scoring members from the set(s). * @param int $count Pop up to how many elements. * * @return Redis|array|null|false This function will return an array of popped elements, or false * depending on whether any elements could be popped within the * specified timeout. * * NOTE: If Redis::OPT_NULL_MULTIBULK_AS_NULL is set to true via Redis::setOption(), this method will * instead return NULL when Redis doesn't pop any elements. */ public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false; /** * POP one or more of the highest or lowest scoring elements from one or more sorted sets. * * @see https://redis.io/commands/zmpop * * @param array $keys One or more sorted sets * @param string $from The string 'MIN' or 'MAX' (case insensitive) telling Redis whether you want to * pop the lowest or highest scoring elements. * @param int $count Pop up to how many elements at once. * * @return Redis|array|null|false An array of popped elements or false if none could be popped. */ public function zmpop(array $keys, string $from, int $count = 1): Redis|array|null|false; /** * Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when * no elements are available. * * @see https://redis.io/commands/blmpop * * @param float $timeout The number of seconds Redis will block when no elements are available. * @param array $keys One or more Redis LISTs to pop from. * @param string $from The string 'LEFT' or 'RIGHT' (case insensitive), telling Redis whether * to pop elements from the beginning or end of the LISTs. * @param int $count Pop up to how many elements at once. * * @return Redis|array|null|false One or more elements popped from the list(s) or false if all LISTs * were empty. */ public function blmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false; /** * Pop one or more elements off of one or more Redis LISTs. * * @see https://redis.io/commands/lmpop * * @param array $keys An array with one or more Redis LIST key names. * @param string $from The string 'LEFT' or 'RIGHT' (case insensitive), telling Redis whether to pop\ * elements from the beginning or end of the LISTs. * @param int $count The maximum number of elements to pop at once. * * @return Redis|array|null|false One or more elements popped from the LIST(s) or false if all the LISTs * were empty. * */ public function lmpop(array $keys, string $from, int $count = 1): Redis|array|null|false; /** * Reset any last error on the connection to NULL * * @see Redis::getLastError() * @return bool This should always return true or throw an exception if we're not connected. * * @example * $redis = new Redis(['host' => 'localhost']); * $redis->set('string', 'this_is_a_string'); * $redis->smembers('string'); * var_dump($redis->getLastError()); * $redis->clearLastError(); * var_dump($redis->getLastError()); */ public function clearLastError(): bool; public function client(string $opt, mixed ...$args): mixed; public function close(): bool; public function command(?string $opt = null, mixed ...$args): mixed; /** * Execute the Redis CONFIG command in a variety of ways. * * What the command does in particular depends on the `$operation` qualifier. * Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET. * * @param string $operation The CONFIG operation to execute (e.g. GET, SET, REWRITE). * @param array|string|null $key_or_settings One or more keys or values. * @param string $value The value if this is a `CONFIG SET` operation. * @see https://redis.io/commands/config * * @example * $redis->config('GET', 'timeout'); * $redis->config('GET', ['timeout', 'databases']); * $redis->config('SET', 'timeout', 30); * $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); */ public function config(string $operation, array|string|null $key_or_settings = null, ?string $value = null): mixed; public function connect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Make a copy of a key. * * $redis = new Redis(['host' => 'localhost']); * * @param string $src The key to copy * @param string $dst The name of the new key created from the source key. * @param array $options An array with modifiers on how COPY should operate. * * $options = [ * 'REPLACE' => true|false # Whether to replace an existing key. * 'DB' => int # Copy key to specific db. * ]; * * * @return Redis|bool True if the copy was completed and false if not. * * @see https://redis.io/commands/copy * * @example * $redis->pipeline() * ->select(1) * ->del('newkey') * ->select(0) * ->del('newkey') * ->mset(['source1' => 'value1', 'exists' => 'old_value']) * ->exec(); * * var_dump($redis->copy('source1', 'newkey')); * var_dump($redis->copy('source1', 'newkey', ['db' => 1])); * var_dump($redis->copy('source1', 'exists')); * var_dump($redis->copy('source1', 'exists', ['REPLACE' => true])); */ public function copy(string $src, string $dst, ?array $options = null): Redis|bool; /** * Return the number of keys in the currently selected Redis database. * * @see https://redis.io/commands/dbsize * * @return Redis|int The number of keys or false on failure. * * @example * $redis = new Redis(['host' => 'localhost']); * $redis->flushdb(); * $redis->set('foo', 'bar'); * var_dump($redis->dbsize()); * $redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']); * var_dump($redis->dbsize()); */ public function dbSize(): Redis|int|false; public function debug(string $key): Redis|string; /** * Decrement a Redis integer by 1 or a provided value. * * @param string $key The key to decrement * @param int $by How much to decrement the key. Note that if this value is * not sent or is set to `1`, PhpRedis will actually invoke * the 'DECR' command. If it is any value other than `1` * PhpRedis will actually send the `DECRBY` command. * * @return Redis|int|false The new value of the key or false on failure. * * @see https://redis.io/commands/decr * @see https://redis.io/commands/decrby * * @example $redis->decr('counter'); * @example $redis->decr('counter', 2); */ public function decr(string $key, int $by = 1): Redis|int|false; /** * Decrement a redis integer by a value * * @param string $key The integer key to decrement. * @param int $value How much to decrement the key. * * @return Redis|int|false The new value of the key or false on failure. * * @see https://redis.io/commands/decrby * * @example $redis->decrby('counter', 1); * @example $redis->decrby('counter', 2); */ public function decrBy(string $key, int $value): Redis|int|false; /** * Delete one or more keys from Redis. * * This method can be called in two distinct ways. The first is to pass a single array * of keys to delete, and the second is to pass N arguments, all names of keys. See * below for an example of both strategies. * * @param array|string $key_or_keys Either an array with one or more key names or a string with * the name of a key. * @param string $other_keys One or more additional keys passed in a variadic fashion. * * @return Redis|int|false The number of keys that were deleted * * @see https://redis.io/commands/del * * @example $redis->del('key:0', 'key:1'); * @example $redis->del(['key:2', 'key:3', 'key:4']); */ public function del(array|string $key, string ...$other_keys): Redis|int|false; /** * @deprecated * @alias Redis::del */ public function delete(array|string $key, string ...$other_keys): Redis|int|false; /** * Discard a transaction currently in progress. * * @return Redis|bool True if we could discard the transaction. * * @example * $redis->getMode(); * $redis->set('foo', 'bar'); * $redis->discard(); * $redis->getMode(); */ public function discard(): Redis|bool; /** * Dump Redis' internal binary representation of a key. * * $redis->zRange('new-zset', 0, -1, true); * * * @param string $key The key to dump. * * @return Redis|string A binary string representing the key's value. * * @see https://redis.io/commands/dump * * @example * $redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two'); * $binary = $redis->dump('zset'); * $redis->restore('new-zset', 0, $binary); */ public function dump(string $key): Redis|string; /** * Have Redis repeat back an arbitrary string to the client. * * @param string $str The string to echo * * @return Redis|string|false The string sent to Redis or false on failure. * * @see https://redis.io/commands/echo * * @example $redis->echo('Hello, World'); */ public function echo(string $str): Redis|string|false; /** * Execute a LUA script on the redis server. * * @see https://redis.io/commands/eval/ * * @param string $script A string containing the LUA script * @param array $args An array of arguments to pass to this script * @param int $num_keys How many of the arguments are keys. This is needed * as redis distinguishes between key name arguments * and other data. * * @return mixed LUA scripts may return arbitrary data so this method can return * strings, arrays, nested arrays, etc. */ public function eval(string $script, array $args = [], int $num_keys = 0): mixed; /** * This is simply the read-only variant of eval, meaning the underlying script * may not modify data in redis. * * @see Redis::eval_ro() */ public function eval_ro(string $script_sha, array $args = [], int $num_keys = 0): mixed; /** * Execute a LUA script on the server but instead of sending the script, send * the SHA1 hash of the script. * * @param string $script_sha The SHA1 hash of the lua code. Note that the script * must already exist on the server, either having been * loaded with `SCRIPT LOAD` or having been executed directly * with `EVAL` first. * @param array $args Arguments to send to the script. * @param int $num_keys The number of arguments that are keys * * @return mixed Returns whatever the specific script does. * * @see https://redis.io/commands/evalsha/ * @see Redis::eval(); * */ public function evalsha(string $sha1, array $args = [], int $num_keys = 0): mixed; /** * This is simply the read-only variant of evalsha, meaning the underlying script * may not modify data in redis. * * @see Redis::evalsha() */ public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): mixed; /** * Execute either a MULTI or PIPELINE block and return the array of replies. * * @return Redis|array|false The array of pipeline'd or multi replies or false on failure. * * @see https://redis.io/commands/exec * @see https://redis.io/commands/multi * @see Redis::pipeline() * @see Redis::multi() * * @example * $res = $redis->multi() * ->set('foo', 'bar') * ->get('foo') * ->del('list') * ->rpush('list', 'one', 'two', 'three') * ->exec(); */ public function exec(): Redis|array|false; /** * Test if one or more keys exist. * * @param mixed $key Either an array of keys or a string key * @param mixed $other_keys If the previous argument was a string, you may send any number of * additional keys to test. * * @return Redis|int|bool The number of keys that do exist and false on failure * * @see https://redis.io/commands/exists * * @example $redis->exists(['k1', 'k2', 'k3']); * @example $redis->exists('k4', 'k5', 'notakey'); */ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; /** * Sets an expiration in seconds on the key in question. If connected to * redis-server >= 7.0.0 you may send an additional "mode" argument which * modifies how the command will execute. * * @param string $key The key to set an expiration on. * @param int $timeout The number of seconds after which key will be automatically deleted. * @param string|null $mode A two character modifier that changes how the * command works. * * NX - Set expiry only if key has no expiry * XX - Set expiry only if key has an expiry * LT - Set expiry only when new expiry is < current expiry * GT - Set expiry only when new expiry is > current expiry * * * @return Redis|bool True if an expiration was set and false otherwise. * @see https://redis.io/commands/expire * */ public function expire(string $key, int $timeout, ?string $mode = null): Redis|bool; /* * Set a key's expiration to a specific Unix timestamp in seconds. * * If connected to Redis >= 7.0.0 you can pass an optional 'mode' argument. * @see Redis::expire() For a description of the mode argument. * * @param string $key The key to set an expiration on. * * @return Redis|bool True if an expiration was set, false if not. * */ /** * Set a key to expire at an exact unix timestamp. * * @param string $key The key to set an expiration on. * @param int $timestamp The unix timestamp to expire at. * @param string|null $mode An option 'mode' that modifies how the command acts (see {@link Redis::expire}). * @return Redis|bool True if an expiration was set, false if not. * * @see https://redis.io/commands/expireat * @see https://redis.io/commands/expire * @see Redis::expire() */ public function expireAt(string $key, int $timestamp, ?string $mode = null): Redis|bool; public function failover(?array $to = null, bool $abort = false, int $timeout = 0): Redis|bool; /** * Get the expiration of a given key as a unix timestamp * * @param string $key The key to check. * * @return Redis|int|false The timestamp when the key expires, or -1 if the key has no expiry * and -2 if the key doesn't exist. * * @see https://redis.io/commands/expiretime * * @example * $redis->setEx('mykey', 60, 'myval'); * $redis->expiretime('mykey'); */ public function expiretime(string $key): Redis|int|false; /** * Get the expriation timestamp of a given Redis key but in milliseconds. * * @see https://redis.io/commands/pexpiretime * @see Redis::expiretime() * * @param string $key The key to check * * @return Redis|int|false The expiration timestamp of this key (in milliseconds) or -1 if the * key has no expiration, and -2 if it does not exist. */ public function pexpiretime(string $key): Redis|int|false; /** * Invoke a function. * * @param string $fn The name of the function * @param array $keys Optional list of keys * @param array $args Optional list of args * * @return mixed Function may return arbitrary data so this method can return * strings, arrays, nested arrays, etc. * * @see https://redis.io/commands/fcall */ public function fcall(string $fn, array $keys = [], array $args = []): mixed; /** * This is a read-only variant of the FCALL command that cannot execute commands that modify data. * * @param string $fn The name of the function * @param array $keys Optional list of keys * @param array $args Optional list of args * * @return mixed Function may return arbitrary data so this method can return * strings, arrays, nested arrays, etc. * * @see https://redis.io/commands/fcall_ro */ public function fcall_ro(string $fn, array $keys = [], array $args = []): mixed; /** * Deletes every key in all Redis databases * * @param bool $sync Whether to perform the task in a blocking or non-blocking way. * @return bool * * @see https://redis.io/commands/flushall */ public function flushAll(?bool $sync = null): Redis|bool; /** * Deletes all the keys of the currently selected database. * * @param bool $sync Whether to perform the task in a blocking or non-blocking way. * @return bool * * @see https://redis.io/commands/flushdb */ public function flushDB(?bool $sync = null): Redis|bool; /** * Functions is an API for managing code to be executed on the server. * * @param string $operation The subcommand you intend to execute. Valid options are as follows * 'LOAD' - Create a new library with the given library name and code. * 'DELETE' - Delete the given library. * 'LIST' - Return general information on all the libraries * 'STATS' - Return information about the current function running * 'KILL' - Kill the current running function * 'FLUSH' - Delete all the libraries * 'DUMP' - Return a serialized payload representing the current libraries * 'RESTORE' - Restore the libraries represented by the given payload * @param member $args Additional arguments * * @return Redis|bool|string|array Depends on subcommand. * * @see https://redis.io/commands/function */ public function function(string $operation, mixed ...$args): Redis|bool|string|array; /** * Add one or more members to a geospacial sorted set * * @param string $key The sorted set to add data to. * @param float $lng The longitude of the first member * @param float $lat The lattitude of the first member. * @param member $other_triples_and_options You can continue to pass longitude, lattitude, and member * arguments to add as many members as you wish. Optionally, the final argument may be * a string with options for the command @see Redis documentation for the options. * * @return Redis|int|false The number of added elements is returned. If the 'CH' option is specified, * the return value is the number of members *changed*. * * @example $redis->geoAdd('cities', -121.8374, 39.7284, 'Chico', -122.03218, 37.322, 'Cupertino'); * @example $redis->geoadd('cities', -121.837478, 39.728494, 'Chico', ['XX', 'CH']); * * @see https://redis.io/commands/geoadd */ public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options): Redis|int|false; /** * Get the distance between two members of a geospacially encoded sorted set. * * @param string $key The Sorted set to query. * @param string $src The first member. * @param string $dst The second member. * @param string $unit Which unit to use when computing distance, defaulting to meters. * * M - meters * KM - kilometers * FT - feet * MI - miles * * * @return Redis|float|false The calculated distance in whichever units were specified or false * if one or both members did not exist. * * @example $redis->geodist('cities', 'Chico', 'Cupertino', 'mi'); * * @see https://redis.io/commands/geodist */ public function geodist(string $key, string $src, string $dst, ?string $unit = null): Redis|float|false; /** * Retrieve one or more GeoHash encoded strings for members of the set. * * @param string $key The key to query * @param string $member The first member to request * @param string $other_members One or more additional members to request. * * @return Redis|array|false An array of GeoHash encoded values. * * @see https://redis.io/commands/geohash * @see https://en.wikipedia.org/wiki/Geohash * * @example $redis->geohash('cities', 'Chico', 'Cupertino'); */ public function geohash(string $key, string $member, string ...$other_members): Redis|array|false; /** * Return the longitude and lattitude for one or more members of a geospacially encoded sorted set. * * @param string $key The set to query. * @param string $member The first member to query. * @param string $other_members One or more members to query. * * @return An array of longitude and lattitude pairs. * * @see https://redis.io/commands/geopos * * @example $redis->geopos('cities', 'Seattle', 'New York'); */ public function geopos(string $key, string $member, string ...$other_members): Redis|array|false; /** * Retrieve members of a geospacially sorted set that are within a certain radius of a location. * * @param string $key The set to query * @param float $lng The longitude of the location to query. * @param float $lat The latitude of the location to query. * @param float $radius The radius of the area to include. * @param string $unit The unit of the provided radius (defaults to 'meters). * See {@link Redis::geodist} for possible units. * @param array $options An array of options that modifies how the command behaves. * * $options = [ * 'WITHCOORD', # Return members and their coordinates. * 'WITHDIST', # Return members and their distances from the center. * 'WITHHASH', # Return members GeoHash string. * 'ASC' | 'DESC', # The sort order of returned members * * # Limit to N returned members. Optionally a two element array may be * # passed as the `LIMIT` argument, and the `ANY` argument. * 'COUNT' => [], or [, ] * * # Instead of returning members, store them in the specified key. * 'STORE' => * * # Store the distances in the specified key * 'STOREDIST' => * ]; * * * @return mixed This command can return various things, depending on the options passed. * * @see https://redis.io/commands/georadius * * @example $redis->georadius('cities', 47.608013, -122.335167, 1000, 'km'); */ public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed; /** * A readonly variant of `GEORADIUS` that may be executed on replicas. * * @see Redis::georadius */ public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed; /** * Similar to `GEORADIUS` except it uses a member as the center of the query. * * @param string $key The key to query. * @param string $member The member to treat as the center of the query. * @param float $radius The radius from the member to include. * @param string $unit The unit of the provided radius * See {@link Redis::geodist} for possible units. * @param array $options An array with various options to modify the command's behavior. * See {@link Redis::georadius} for options. * * @return mixed This command can return various things depending on options. * * @example $redis->georadiusbymember('cities', 'Seattle', 200, 'mi'); */ public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): mixed; /** * This is the read-only variant of `GEORADIUSBYMEMBER` that can be run on replicas. */ public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): mixed; /** * Search a geospacial sorted set for members in various ways. * * @param string $key The set to query. * @param array|string $position Either a two element array with longitude and lattitude, or * a string representing a member of the set. * @param array|int|float $shape Either a number representine the radius of a circle to search, or * a two element array representing the width and height of a box * to search. * @param string $unit The unit of our shape. See {@link Redis::geodist} for possible units. * @param array $options @see {@link Redis::georadius} for options. Note that the `STORE` * options are not allowed for this command. */ public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): array; /** * Search a geospacial sorted set for members within a given area or range, storing the results into * a new set. * * @param string $dst The destination where results will be stored. * @param string $src The key to query. * @param array|string $position Either a two element array with longitude and lattitude, or * a string representing a member of the set. * @param array|int|float $shape Either a number representine the radius of a circle to search, or * a two element array representing the width and height of a box * to search. * @param string $unit The unit of our shape. See {@link Redis::geodist} for possible units. * @param array $options * * $options = [ * 'ASC' | 'DESC', # The sort order of returned members * 'WITHDIST' # Also store distances. * * # Limit to N returned members. Optionally a two element array may be * # passed as the `LIMIT` argument, and the `ANY` argument. * 'COUNT' => [], or [, ] * ]; * */ public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): Redis|array|int|false; /** * Retrieve a string keys value. * * @param string $key The key to query * @return mixed The keys value or false if it did not exist. * * @see https://redis.io/commands/get * * @example $redis->get('foo'); */ public function get(string $key): mixed; /** * Get the authentication information on the connection, if any. * * @return mixed The authentication information used to authenticate the connection. * * @see Redis::auth() */ public function getAuth(): mixed; /** * Get the bit at a given index in a string key. * * @param string $key The key to query. * @param int $idx The Nth bit that we want to query. * * @example $redis->getbit('bitmap', 1337); * * @see https://redis.io/commands/getbit */ public function getBit(string $key, int $idx): Redis|int|false; /** * Get the value of a key and optionally set it's expiration. * * @param string $key The key to query * @param array $options Options to modify how the command works. * * $options = [ * 'EX' => # Expire in N seconds * 'PX' => # Expire in N milliseconds * 'EXAT' => # Expire at a unix timestamp (in seconds) * 'PXAT' => # Expire at a unix timestamp (in milliseconds); * 'PERSIST' # Remove any configured expiration on the key. * ]; * * * @return Redis|string|bool The key's value or false if it didn't exist. * * @see https://redis.io/comands/getex * * @example $redis->getEx('mykey', ['EX' => 60]); */ public function getEx(string $key, array $options = []): Redis|string|bool; /** * Get the database number PhpRedis thinks we're connected to. * * This value is updated internally in PhpRedis each time {@link Redis::select} is called. * * @return The database we're connected to. * * @see Redis::select() * @see https://redis.io/commands/select */ public function getDBNum(): int; /** * Get a key from Redis and delete it in an atomic operation. * * @param string $key The key to get/delete. * @return Redis|string|bool The value of the key or false if it didn't exist. * * @see https://redis.io/commands/getdel * * @example $redis->getdel('token:123'); */ public function getDel(string $key): Redis|string|bool; /** * Return the host or Unix socket we are connected to. * * @return string The host or Unix socket. */ public function getHost(): string; /** * Get the last error returned to us from Redis, if any. * * @return string The error string or NULL if there is none. */ public function getLastError(): ?string; /** * Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode * * @return int The mode we're in. * */ public function getMode(): int; /** * Retrieve the value of a configuration setting as set by Redis::setOption() * * @see Redis::setOption() for a detailed list of options and their values. * * @return mixed The setting itself or false on failure */ public function getOption(int $option): mixed; /** * Get the persistent connection ID, if there is one. * * @return string The ID or NULL if we don't have one. */ public function getPersistentID(): ?string; /** * Get the port we are connected to. This number will be zero if we are connected to a unix socket. * * @return int The port. */ public function getPort(): int; /** * Retrieve a substring of a string by index. * * @param string $key The string to query. * @param int $start The zero-based starting index. * @param int $end The zero-based ending index. * * @return Redis|string|false The substring or false on failure. * * @see https://redis.io/commands/getrange * * @example * $redis->set('silly-word', 'Supercalifragilisticexpialidocious'); * echo $redis->getRange('silly-word', 0, 4) . "\n"; */ public function getRange(string $key, int $start, int $end): Redis|string|false; /** * Get the longest common subsequence between two string keys. * * @param string $key1 The first key to check * @param string $key2 The second key to check * @param array $options An optional array of modifiers for the comand. * * * $options = [ * 'MINMATCHLEN' => int # Exclude matching substrings that are less than this value * * 'WITHMATCHLEN' => bool # Whether each match should also include its length. * * 'LEN' # Return the length of the longest subsequence * * 'IDX' # Each returned match will include the indexes where the * # match occurs in each string. * ]; * * * NOTE: 'LEN' cannot be used with 'IDX'. * * @return Redis|string|array|int|false Various reply types depending on options. * * @see https://redis.io/commands/lcs * * @example * $redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc'); * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc'); * echo $redis->lcs('seq1', 'seq2') . "\n"; */ public function lcs(string $key1, string $key2, ?array $options = null): Redis|string|array|int|false; /** * Get the currently set read timeout on the connection. * * @return float The timeout. */ public function getReadTimeout(): float; /** * Sets a key and returns any previously set value, if the key already existed. * * @param string $key The key to set. * @param mixed $value The value to set the key to. * * @return Redis|string|false The old value of the key or false if it didn't exist. * * @see https://redis.io/commands/getset * * @example * $redis->getset('captain', 'Pike'); * $redis->getset('captain', 'Kirk'); */ public function getset(string $key, mixed $value): Redis|string|false; /** * Retrieve any set connection timeout * * @return float The currently set timeout or false on failure (e.g. we aren't connected). */ public function getTimeout(): float|false; /** * Get the number of bytes sent and received on the socket. * * @return array An array in the form [$sent_bytes, $received_bytes] */ public function getTransferredBytes(): array; /** * Reset the number of bytes sent and received on the socket. * * @return void */ public function clearTransferredBytes(): void; /** * Remove one or more fields from a hash. * * @param string $key The hash key in question. * @param string $field The first field to remove * @param string $other_fields One or more additional fields to remove. * * @return Redis|int|false The number of fields actually removed. * * @see https://redis.io/commands/hdel * * @example $redis->hDel('communication', 'Alice', 'Bob'); */ public function hDel(string $key, string $field, string ...$other_fields): Redis|int|false; /** * Checks whether a field exists in a hash. * * @param string $key The hash to query. * @param string $field The field to check * * @return Redis|bool True if it exists, false if not. * * @see https://redis.io/commands/hexists * * @example $redis->hExists('communication', 'Alice'); */ public function hExists(string $key, string $field): Redis|bool; public function hGet(string $key, string $member): mixed; /** * Read every field and value from a hash. * * @param string $key The hash to query. * @return Redis|array|false All fields and values or false if the key didn't exist. * * @see https://redis.io/commands/hgetall * * @example $redis->hgetall('myhash'); */ public function hGetAll(string $key): Redis|array|false; /** * Increment a hash field's value by an integer * * @param string $key The hash to modify * @param string $field The field to increment * @param int $value How much to increment the value. * * @return Redis|int|false The new value of the field. * * @see https://redis.io/commands/hincrby * * @example * $redis->hMSet('player:1', ['name' => 'Alice', 'score' => 0]); * $redis->hincrby('player:1', 'score', 10); * */ public function hIncrBy(string $key, string $field, int $value): Redis|int|false; /** * Increment a hash field by a floating point value * * @param string $key The hash with the field to increment. * @param string $field The field to increment. * * @return Redis|float|false The field value after incremented. * * @see https://redis.io/commands/hincrbyfloat * * @example * $redis->hincrbyfloat('numbers', 'tau', 2 * 3.1415926); */ public function hIncrByFloat(string $key, string $field, float $value): Redis|float|false; /** * Retrieve all of the fields of a hash. * * @param string $key The hash to query. * * @return Redis|array|false The fields in the hash or false if the hash doesn't exist. * * @see https://redis.io/commands/hkeys * * @example $redis->hkeys('myhash'); */ public function hKeys(string $key): Redis|array|false; /** * Get the number of fields in a hash. * * @see https://redis.io/commands/hlen * * @param string $key The hash to check. * * @return Redis|int|false The number of fields or false if the key didn't exist. * * @example $redis->hlen('myhash'); */ public function hLen(string $key): Redis|int|false; /** * Get one or more fields from a hash. * * @param string $key The hash to query. * @param array $fields One or more fields to query in the hash. * * @return Redis|array|false The fields and values or false if the key didn't exist. * * @see https://redis.io/commands/hmget * * @example $redis->hMGet('player:1', ['name', 'score']); */ public function hMget(string $key, array $fields): Redis|array|false; /** * Add or update one or more hash fields and values * * @param string $key The hash to create/update * @param array $fieldvals An associative array with fields and their values. * * @return Redis|bool True if the operation was successful * * @see https://redis.io/commands/hmset * * @example $redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]); */ public function hMset(string $key, array $fieldvals): Redis|bool; /** * Get one or more random field from a hash. * * @param string $key The hash to query. * @param array $options An array of options to modify how the command behaves. * * * $options = [ * 'COUNT' => int # An optional number of fields to return. * 'WITHVALUES' => bool # Also return the field values. * ]; * * * @return Redis|array|string One or more random fields (and possibly values). * * @see https://redis.io/commands/hrandfield * * @example $redis->hrandfield('settings'); * @example $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]); */ public function hRandField(string $key, ?array $options = null): Redis|string|array; public function hSet(string $key, string $member, mixed $value): Redis|int|false; /** * Set a hash field and value, but only if that field does not exist * * @param string $key The hash to update. * @param string $field The value to set. * * @return Redis|bool True if the field was set and false if not. * * @see https://redis.io/commands/hsetnx * * @example * $redis->hsetnx('player:1', 'lock', 'enabled'); * $redis->hsetnx('player:1', 'lock', 'enabled'); */ public function hSetNx(string $key, string $field, string $value): Redis|bool; /** * Get the string length of a hash field * * @param string $key The hash to query. * @param string $field The field to query. * * @return Redis|int|false The string length of the field or false. * * @example * $redis = new Redis(['host' => 'localhost']); * $redis->del('hash'); * $redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]); * $redis->hstrlen('hash', '50bytes'); * * @see https://redis.io/commands/hstrlen */ public function hStrLen(string $key, string $field): Redis|int|false; /** * Get all of the values from a hash. * * @param string $key The hash to query. * * @return Redis|array|false The values from the hash. * * @see https://redis.io/commands/hvals * * @example $redis->hvals('player:1'); */ public function hVals(string $key): Redis|array|false; /** * Iterate over the fields and values of a hash in an incremental fashion. * * @see https://redis.io/commands/hscan * @see https://redis.io/commands/scan * * @param string $key The hash to query. * @param int $iterator The scan iterator, which should be initialized to NULL before the first call. * This value will be updated after every call to hscan, until it reaches zero * meaning the scan is complete. * @param string $pattern An optional glob-style pattern to filter fields with. * @param int $count An optional hint to Redis about how many fields and values to return per HSCAN. * * @return Redis|array|bool An array with a subset of fields and values. * * @example * $redis = new Redis(['host' => 'localhost']); * * $redis->del('big-hash'); * * for ($i = 0; $i < 1000; $i++) { * $fields["field:$i"] = "value:$i"; * } * * $redis->hmset('big-hash', $fields); * * $it = null; * * do { * // Scan the hash but limit it to fields that match '*:1?3' * $fields = $redis->hscan('big-hash', $it, '*:1?3'); * * foreach ($fields as $field => $value) { * echo "[$field] => $value\n"; * } * } while ($it != 0); */ public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool; /** * Increment a key's value, optionally by a specifc amount. * * @see https://redis.io/commands/incr * @see https://redis.io/commands/incrby * * @param string $key The key to increment * @param int $by An optional amount to increment by. * * @return Redis|int|false The new value of the key after incremented. * * @example $redis->incr('mycounter'); * @example $redis->incr('mycounter', 10); */ public function incr(string $key, int $by = 1): Redis|int|false; /** * Increment a key by a specific integer value * * @see https://redis.io/commands/incrby * * @param string $key The key to increment. * @param int $value The amount to increment. * * @example * $redis->set('primes', 2); * $redis->incrby('primes', 1); * $redis->incrby('primes', 2); * $redis->incrby('primes', 2); * $redis->incrby('primes', 4); */ public function incrBy(string $key, int $value): Redis|int|false; /** * Increment a numeric key by a floating point value. * * @param string $key The key to increment * @param floag $value How much to increment (or decrement) the value. * * @return Redis|float|false The new value of the key or false if the key didn't contain a string. * * @example * $redis->incrbyfloat('tau', 3.1415926); * $redis->incrbyfloat('tau', 3.1415926); */ public function incrByFloat(string $key, float $value): Redis|float|false; /** * Retrieve information about the connected redis-server. If no arguments are passed to * this function, redis will return every info field. Alternatively you may pass a specific * section you want returned (e.g. 'server', or 'memory') to receive only information pertaining * to that section. * * If connected to Redis server >= 7.0.0 you may pass multiple optional sections. * * @see https://redis.io/commands/info/ * * @param string $sections Optional section(s) you wish Redis server to return. * * @return Redis|array|false */ public function info(string ...$sections): Redis|array|false; /** * Check if we are currently connected to a Redis instance. * * @return bool True if we are, false if not */ public function isConnected(): bool; /** @return Redis|array|false */ public function keys(string $pattern); /** * @param mixed $elements * @return Redis|int|false */ public function lInsert(string $key, string $pos, mixed $pivot, mixed $value); /** * Retrieve the lenght of a list. * * @param string $key The list * * @return Redis|int|false The number of elements in the list or false on failure. */ public function lLen(string $key): Redis|int|false; /** * Move an element from one list into another. * * @param string $src The source list. * @param string $dst The destination list * @param string $wherefrom Where in the source list to retrieve the element. This can be either * - `Redis::LEFT`, or `Redis::RIGHT`. * @param string $whereto Where in the destination list to put the element. This can be either * - `Redis::LEFT`, or `Redis::RIGHT`. * @return Redis|string|false The element removed from the source list. * * @example * $redis->rPush('numbers', 'one', 'two', 'three'); * $redis->lMove('numbers', 'odds', Redis::LEFT, Redis::LEFT); */ public function lMove(string $src, string $dst, string $wherefrom, string $whereto): Redis|string|false; /** * Move an element from one list to another, blocking up to a timeout until an element is available. * * @param string $src The source list * @param string $dst The destination list * @param string $wherefrom Where in the source list to extract the element. * - `Redis::LEFT`, or `Redis::RIGHT`. * @param string $whereto Where in the destination list to put the element. * - `Redis::LEFT`, or `Redis::RIGHT`. * @param float $timeout How long to block for an element. * * @return Redis|string|false; * * @example * @redis->lPush('numbers', 'one'); * @redis->blmove('numbers', 'odds', Redis::LEFT, Redis::LEFT 1.0); * // This call will block, if no additional elements are in 'numbers' * @redis->blmove('numbers', 'odds', Redis::LEFT, Redis::LEFT, 1.0); */ public function blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout): Redis|string|false; /** * Pop one or more elements off a list. * * @param string $key The list to pop from. * @param int $count Optional number of elements to remove. By default one element is popped. * @return Redis|null|bool|int|array Will return the element(s) popped from the list or false/NULL * if none was removed. * * @see https://redis.io/commands/lpop * * @example $redis->lpop('mylist'); * @example $redis->lpop('mylist', 4); */ public function lPop(string $key, int $count = 0): Redis|bool|string|array; /** * Retrieve the index of an element in a list. * * @param string $key The list to query. * @param mixed $value The value to search for. * @param array $options Options to configure how the command operates * * $options = [ * # How many matches to return. By default a single match is returned. * # If count is set to zero, it means unlimited. * 'COUNT' => * * # Specify which match you want returned. `RANK` 1 means "the first match" * # 2 means the second, and so on. If passed as a negative number the * # RANK is computed right to left, so a `RANK` of -1 means "the last match". * 'RANK' => * * # This argument allows you to limit how many elements Redis will search before * # returning. This is useful to prevent Redis searching very long lists while * # blocking the client. * 'MAXLEN => * ]; * * * @return Redis|null|bool|int|array Returns one or more of the matching indexes, or null/false if none were found. */ public function lPos(string $key, mixed $value, ?array $options = null): Redis|null|bool|int|array; /** * Prepend one or more elements to a list. * * @param string $key The list to prepend. * @param mixed $elements One or more elements to prepend. * * @return Redis|int The new length of the list after prepending. * * @see https://redis.io/commands/lpush * * @example $redis->lPush('mylist', 'cat', 'bear', 'aligator'); */ public function lPush(string $key, mixed ...$elements): Redis|int|false; /** * Append one or more elements to a list. * * @param string $key The list to append to. * @param mixed $elements one or more elements to append. * * @return Redis|int|false The new length of the list * * @see https://redis.io/commands/rpush * * @example $redis->rPush('mylist', 'xray', 'yankee', 'zebra'); */ public function rPush(string $key, mixed ...$elements): Redis|int|false; /** * Prepend an element to a list but only if the list exists * * @param string $key The key to prepend to. * @param mixed $value The value to prepend. * * @return Redis|int|false The new length of the list. * */ public function lPushx(string $key, mixed $value): Redis|int|false; /** * Append an element to a list but only if the list exists * * @param string $key The key to prepend to. * @param mixed $value The value to prepend. * * @return Redis|int|false The new length of the list. * */ public function rPushx(string $key, mixed $value): Redis|int|false; /** * Set a list element at an index to a specific value. * * @param string $key The list to modify. * @param int $index The position of the element to change. * @param mixed $value The new value. * * @return Redis|bool True if the list was modified. * * @see https://redis.io/commands/lset */ public function lSet(string $key, int $index, mixed $value): Redis|bool; /** * Retrieve the last time Redis' database was persisted to disk. * * @return int The unix timestamp of the last save time * * @see https://redis.io/commands/lastsave */ public function lastSave(): int; /** * Get the element of a list by its index. * * @param string $key The key to query * @param int $index The index to check. * @return mixed The index or NULL/false if the element was not found. */ public function lindex(string $key, int $index): mixed; /** * Retrieve elements from a list. * * @param string $key The list to query. * @param int $start The beginning index to retrieve. This number can be negative * meaning start from the end of the list. * @param int $end The end index to retrieve. This can also be negative to start * from the end of the list. * * @return Redis|array|false The range of elements between the indexes. * * @example $redis->lrange('mylist', 0, -1); // the whole list * @example $redis->lrange('mylist', -2, -1); // the last two elements in the list. */ public function lrange(string $key, int $start , int $end): Redis|array|false; /** * Remove one or more matching elements from a list. * * @param string $key The list to truncate. * @param mixed $value The value to remove. * @param int $count How many elements matching the value to remove. * * @return Redis|int|false The number of elements removed. * * @see https://redis.io/commands/lrem */ public function lrem(string $key, mixed $value, int $count = 0): Redis|int|false; /** * Trim a list to a subrange of elements. * * @param string $key The list to trim * @param int $start The starting index to keep * @param int $end The ending index to keep. * * @return Redis|bool true if the list was trimmed. * * @example $redis->ltrim('mylist', 0, 3); // Keep the first four elements */ public function ltrim(string $key, int $start , int $end): Redis|bool; /** * Get one ore more string keys. * * @param array $keys The keys to retrieve * @return Redis|array an array of keys with their values. * * @example $redis->mget(['key1', 'key2']); */ public function mget(array $keys): Redis|array; public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, #[\SensitiveParameter] mixed $credentials = null): Redis|bool; /** * Move a key to a different database on the same redis instance. * * @param string $key The key to move * @return Redis|bool True if the key was moved */ public function move(string $key, int $index): Redis|bool; /** * Set one ore more string keys. * * @param array $key_values An array with keys and their values. * @return Redis|bool True if the keys could be set. * * @see https://redis.io/commands/mset * * @example $redis->mSet(['foo' => 'bar', 'baz' => 'bop']); */ public function mset(array $key_values): Redis|bool; /** * Set one ore more string keys but only if none of the key exist. * * @param array $key_values An array of keys with their values. * * @return Redis|bool True if the keys were set and false if not. * * @see https://redis.io/commands/msetnx * * @example $redis->msetnx(['foo' => 'bar', 'baz' => 'bop']); */ public function msetnx(array $key_values): Redis|bool; /** * Begin a transaction. * * @param int $value The type of transaction to start. This can either be `Redis::MULTI` or * `Redis::PIPELINE'. * * @return Redis|bool True if the transaction could be started. * * @see https://redis.io/commands/multi * * @example * $redis->multi(); * $redis->set('foo', 'bar'); * $redis->get('foo'); * $redis->exec(); */ public function multi(int $value = Redis::MULTI): bool|Redis; public function object(string $subcommand, string $key): Redis|int|string|false; /** * @deprecated * @alias Redis::connect */ public function open(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; public function pconnect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Remove the expiration from a key. * * @param string $key The key to operate against. * * @return Redis|bool True if a timeout was removed and false if it was not or the key didn't exist. */ public function persist(string $key): Redis|bool; /** * Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 * you can pass an optional mode argument that modifies how the command will execute. * * @see Redis::expire() for a description of the mode argument. * * @param string $key The key to set an expiration on. * @param int $timeout The number of milliseconds after which key will be automatically deleted. * @param string|null $mode A two character modifier that changes how the * command works. * * @return Redis|bool True if an expiry was set on the key, and false otherwise. */ public function pexpire(string $key, int $timeout, ?string $mode = null): bool; /** * Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to * Redis >= 7.0.0 you can pass an optional 'mode' argument. * * @see Redis::expire() For a description of the mode argument. * * @param string $key The key to set an expiration on. * @param int $timestamp The unix timestamp to expire at. * @param string|null $mode A two character modifier that changes how the * command works. * * @return Redis|bool True if an expiration was set on the key, false otherwise. */ public function pexpireAt(string $key, int $timestamp, ?string $mode = null): Redis|bool; /** * Add one or more elements to a Redis HyperLogLog key * * @see https://redis.io/commands/pfadd * * @param string $key The key in question. * * @param array $elements One or more elements to add. * * @return Redis|int Returns 1 if the set was altered, and zero if not. */ public function pfadd(string $key, array $elements): Redis|int; /** * Retrieve the cardinality of a Redis HyperLogLog key. * * @see https://redis.io/commands/pfcount * * @param string $key_or_keys Either one key or an array of keys * * @return Redis|int The estimated cardinality of the set. */ public function pfcount(array|string $key_or_keys): Redis|int|false; /** * Merge one or more source HyperLogLog sets into a destination set. * * @see https://redis.io/commands/pfmerge * * @param string $dst The destination key. * @param array $srckeys One or more source keys. * * @return Redis|bool Always returns true. */ public function pfmerge(string $dst, array $srckeys): Redis|bool; /** * PING the redis server with an optional string argument. * * @see https://redis.io/commands/ping * * @param string $message An optional string message that Redis will reply with, if passed. * * @return Redis|string|false If passed no message, this command will simply return `true`. * If a message is passed, it will return the message. * * @example $redis->ping(); * @example $redis->ping('beep boop'); */ public function ping(?string $message = null): Redis|string|bool; /** * Enter into pipeline mode. * * Pipeline mode is the highest performance way to send many commands to Redis * as they are aggregated into one stream of commands and then all sent at once * when the user calls Redis::exec(). * * NOTE: That this is shorthand for Redis::multi(Redis::PIPELINE) * * @return Redis The redis object is returned, to facilitate method chaining. * * @example * $redis->pipeline() * ->set('foo', 'bar') * ->del('mylist') * ->rpush('mylist', 'a', 'b', 'c') * ->exec(); */ public function pipeline(): bool|Redis; /** * @deprecated * @alias Redis::pconnect */ public function popen(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Set a key with an expiration time in milliseconds * * @param string $key The key to set * @param int $expire The TTL to set, in milliseconds. * @param mixed $value The value to set the key to. * * @return Redis|bool True if the key could be set. * * @example $redis->psetex('mykey', 1000, 'myval'); */ public function psetex(string $key, int $expire, mixed $value): Redis|bool; /** * Subscribe to one or more glob-style patterns * * @param array $patterns One or more patterns to subscribe to. * @param callable $cb A callback with the following prototype: * * * function ($redis, $channel, $message) { } * * * @see https://redis.io/commands/psubscribe * * @return bool True if we were subscribed. */ public function psubscribe(array $patterns, callable $cb): bool; /** * Get a keys time to live in milliseconds. * * @param string $key The key to check. * * @return Redis|int|false The key's TTL or one of two special values if it has none. * * -1 - The key has no TTL. * -2 - The key did not exist. * * * @see https://redis.io/commands/pttl * * @example $redis->pttl('ttl-key'); */ public function pttl(string $key): Redis|int|false; /** * Publish a message to a pubsub channel * * @see https://redis.io/commands/publish * * @param string $channel The channel to publish to. * @param string $message The message itself. * * @return Redis|int The number of subscribed clients to the given channel. */ public function publish(string $channel, string $message): Redis|int|false; public function pubsub(string $command, mixed $arg = null): mixed; /** * Unsubscribe from one or more channels by pattern * * @see https://redis.io/commands/punsubscribe * @see https://redis.io/commands/subscribe * @see Redis::subscribe() * * @param array $patterns One or more glob-style patterns of channel names. * * @return Redis|array|bool The array of subscribed patterns or false on failure. */ public function punsubscribe(array $patterns): Redis|array|bool; /** * Pop one or more elements from the end of a list. * * @param string $key A redis LIST key name. * @param int $count The maximum number of elements to pop at once. * NOTE: The `count` argument requires Redis >= 6.2.0 * * @return Redis|array|string|bool One ore more popped elements or false if all were empty. * * @see https://redis.io/commands/rpop * * @example $redis->rPop('mylist'); * @example $redis->rPop('mylist', 4); */ public function rPop(string $key, int $count = 0): Redis|array|string|bool; /** * Return a random key from the current database * * @see https://redis.io/commands/randomkey * * @return Redis|string|false A random key name or false if no keys exist * */ public function randomKey(): Redis|string|false; /** * Execute any arbitrary Redis command by name. * * @param string $command The command to execute * @param mixed $args One or more arguments to pass to the command. * * @return mixed Can return any number of things depending on command executed. * * @example $redis->rawCommand('del', 'mystring', 'mylist'); * @example $redis->rawCommand('set', 'mystring', 'myvalue'); * @example $redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three'); */ public function rawcommand(string $command, mixed ...$args): mixed; /** * Unconditionally rename a key from $old_name to $new_name * * @see https://redis.io/commands/rename * * @param string $old_name The original name of the key * @param string $new_name The new name for the key * * @return Redis|bool True if the key was renamed or false if not. */ public function rename(string $old_name, string $new_name): Redis|bool; /** * Renames $key_src to $key_dst but only if newkey does not exist. * * @see https://redis.io/commands/renamenx * * @param string $key_src The source key name * @param string $key_dst The destination key name. * * @return Redis|bool True if the key was renamed, false if not. * * @example * $redis->set('src', 'src_key'); * $redis->set('existing-dst', 'i_exist'); * * $redis->renamenx('src', 'dst'); * $redis->renamenx('dst', 'existing-dst'); */ public function renameNx(string $key_src, string $key_dst): Redis|bool; /** * Reset the state of the connection. * * @return Redis|bool Should always return true unless there is an error. */ public function reset(): Redis|bool; /** * Restore a key by the binary payload generated by the DUMP command. * * @param string $key The name of the key you wish to create. * @param int $ttl What Redis should set the key's TTL (in milliseconds) to once it is created. * Zero means no TTL at all. * @param string $value The serialized binary value of the string (generated by DUMP). * @param array $options An array of additional options that modifies how the command operates. * * * $options = [ * 'ABSTTL' # If this is present, the `$ttl` provided by the user should * # be an absolute timestamp, in milliseconds() * * 'REPLACE' # This flag instructs Redis to store the key even if a key with * # that name already exists. * * 'IDLETIME' => int # Tells Redis to set the keys internal 'idletime' value to a * # specific number (see the Redis command OBJECT for more info). * 'FREQ' => int # Tells Redis to set the keys internal 'FREQ' value to a specific * # number (this relates to Redis' LFU eviction algorithm). * ]; * * * @return Redis|bool True if the key was stored, false if not. * * @see https://redis.io/commands/restore * @see https://redis.io/commands/dump * @see Redis::dump() * * @example * $redis->sAdd('captains', 'Janeway', 'Picard', 'Sisko', 'Kirk', 'Archer'); * $serialized = $redis->dump('captains'); * * $redis->restore('captains-backup', 0, $serialized); */ public function restore(string $key, int $ttl, string $value, ?array $options = null): Redis|bool; /** * Query whether the connected instance is a primary or replica * * @return mixed Will return an array with the role of the connected instance unless there is * an error. */ public function role(): mixed; /** * Atomically pop an element off the end of a Redis LIST and push it to the beginning of * another. * * @param string $srckey The source key to pop from. * @param string $dstkey The destination key to push to. * * @return Redis|string|false The popped element or false if the source key was empty. * * @see https://redis.io/commands/rpoplpush * * @example * $redis->pipeline() * ->del('list1', 'list2') * ->rpush('list1', 'list1-1', 'list1-2') * ->rpush('list2', 'list2-1', 'list2-2') * ->exec(); * * $redis->rpoplpush('list2', 'list1'); */ public function rpoplpush(string $srckey, string $dstkey): Redis|string|false; /** * Add one or more values to a Redis SET key. * * @param string $key The key name * @param mixed $member A value to add to the set. * @param mixed $other_members One or more additional values to add * * @return Redis|int|false The number of values added to the set. * * @see https://redis.io/commands/sadd * * @example * $redis->del('myset'); * * $redis->sadd('myset', 'foo', 'bar', 'baz'); * $redis->sadd('myset', 'foo', 'new'); */ public function sAdd(string $key, mixed $value, mixed ...$other_values): Redis|int|false; /** * Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but * instead of being variadic, takes a single array of values. * * @see https://redis.io/commands/sadd * @see Redis::sadd() * * @param string $key The set to add values to. * @param array $values One or more members to add to the set. * @return Redis|int|false The number of members added to the set. * * @example * $redis->del('myset'); * * $redis->sAddArray('myset', ['foo', 'bar', 'baz']); * $redis->sAddArray('myset', ['foo', 'new']); */ public function sAddArray(string $key, array $values): int; /** * Given one or more Redis SETS, this command returns all of the members from the first * set that are not in any subsequent set. * * @param string $key The first set * @param string $other_keys One or more additional sets * * @return Redis|array|false Returns the elements from keys 2..N that don't exist in the * first sorted set, or false on failure. * * @see https://redis.io/commands/sdiff * * @example * $redis->pipeline() * ->del('set1', 'set2', 'set3') * ->sadd('set1', 'apple', 'banana', 'carrot', 'date') * ->sadd('set2', 'carrot') * ->sadd('set3', 'apple', 'carrot', 'eggplant') * ->exec(); * * $redis->sdiff('set1', 'set2', 'set3'); */ public function sDiff(string $key, string ...$other_keys): Redis|array|false; /** * This method performs the same operation as SDIFF except it stores the resulting diff * values in a specified destination key. * * @see https://redis.io/commands/sdiffstore * @see Redis::sdiff() * * @param string $dst The key where to store the result * @param string $key The first key to perform the DIFF on * @param string $other_keys One or more additional keys. * * @return Redis|int|false The number of values stored in the destination set or false on failure. */ public function sDiffStore(string $dst, string $key, string ...$other_keys): Redis|int|false; /** * Given one or more Redis SET keys, this command will return all of the elements that are * in every one. * * @see https://redis.io/commands/sinter * * @param string $key The first SET key to intersect. * @param string $other_keys One or more Redis SET keys. * * @example * $redis->pipeline() * ->del('alice_likes', 'bob_likes', 'bill_likes') * ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato') * ->sadd('bob_likes', 'asparagus', 'carrot', 'potato') * ->sadd('bill_likes', 'broccoli', 'potato') * ->exec(); * * var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes')); * */ public function sInter(array|string $key, string ...$other_keys): Redis|array|false; /** * Compute the intersection of one or more sets and return the cardinality of the result. * * @param array $keys One or more set key names. * @param int $limit A maximum cardinality to return. This is useful to put an upper bound * on the amount of work Redis will do. * * @return Redis|int|false The * * @see https://redis.io/commands/sintercard * * @example * $redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot'); * $redis->sAdd('set2', 'apple', 'banana'); * $redis->sAdd('set3', 'pear', 'banana'); * * $redis->sInterCard(['set1', 'set2', 'set3']); * ?> * */ public function sintercard(array $keys, int $limit = -1): Redis|int|false; /** * Perform the intersection of one or more Redis SETs, storing the result in a destination * key, rather than returning them. * * @param array|string $key_or_keys Either a string key, or an array of keys (with at least two * elements, consisting of the destination key name and one * or more source keys names. * @param string $other_keys If the first argument was a string, subsequent arguments should * be source key names. * * @return Redis|int|false The number of values stored in the destination key or false on failure. * * @see https://redis.io/commands/sinterstore * @see Redis::sinter() * * @example $redis->sInterStore(['dst', 'src1', 'src2', 'src3']); * @example $redis->sInterStore('dst', 'src1', 'src'2', 'src3'); * ?> * */ public function sInterStore(array|string $key, string ...$other_keys): Redis|int|false; /** * Retrieve every member from a set key. * * @param string $key The set name. * * @return Redis|array|false Every element in the set or false on failure. * * @see https://redis.io/commands/smembers * * @example * $redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']); * $redis->sMembers('tng-crew'); */ public function sMembers(string $key): Redis|array|false; /** * Check if one or more values are members of a set. * * @see https://redis.io/commands/smismember * @see https://redis.io/commands/smember * @see Redis::smember() * * @param string $key The set to query. * @param string $member The first value to test if exists in the set. * @param string $other_members Any number of additional values to check. * * @return Redis|array|false An array of integers representing whether each passed value * was a member of the set. * * @example * $redis->sAdd('ds9-crew', ...["Sisko", "Kira", "Dax", "Worf", "Bashir", "O'Brien"]); * $members = $redis->sMIsMember('ds9-crew', ...['Sisko', 'Picard', 'Data', 'Worf']); */ public function sMisMember(string $key, string $member, string ...$other_members): Redis|array|false; /** * Pop a member from one set and push it onto another. This command will create the * destination set if it does not currently exist. * * @see https://redis.io/commands/smove * * @param string $src The source set. * @param string $dst The destination set. * @param mixed $value The member you wish to move. * * @return Redis|bool True if the member was moved, and false if it wasn't in the set. * * @example * $redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four'); * $redis->sMove('numbers', 'evens', 'zero'); * $redis->sMove('numbers', 'evens', 'two'); * $redis->sMove('numbers', 'evens', 'four'); */ public function sMove(string $src, string $dst, mixed $value): Redis|bool; /** * Remove one or more elements from a set. * * @see https://redis.io/commands/spop * * @param string $key The set in question. * @param int $count An optional number of members to pop. This defaults to * removing one element. * * @example * $redis->del('numbers', 'evens'); * $redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four'); * $redis->sPop('numbers'); */ public function sPop(string $key, int $count = 0): Redis|string|array|false; /** * Retrieve one or more random members of a set. * * @param string $key The set to query. * @param int $count An optional count of members to return. * * If this value is positive, Redis will return *up to* the requested * number but with unique elements that will never repeat. This means * you may recieve fewer then `$count` replies. * * If the number is negative, Redis will return the exact number requested * but the result may contain duplicate elements. * * @return Redis|array|string|false One or more random members or false on failure. * * @see https://redis.io/commands/srandmember * * @example $redis->sRandMember('myset'); * @example $redis->sRandMember('myset', 10); * @example $redis->sRandMember('myset', -10); */ public function sRandMember(string $key, int $count = 0): Redis|string|array|false; /** * Returns the union of one or more Redis SET keys. * * @see https://redis.io/commands/sunion * * @param string $key The first SET to do a union with * @param string $other_keys One or more subsequent keys * * @return Redis|array|false The union of the one or more input sets or false on failure. * * @example $redis->sunion('set1', 'set2'); */ public function sUnion(string $key, string ...$other_keys): Redis|array|false; /** * Perform a union of one or more Redis SET keys and store the result in a new set * * @see https://redis.io/commands/sunionstore * @see Redis::sunion() * * @param string $dst The destination key * @param string $key The first source key * @param string $other_keys One or more additional source keys * * @return Redis|int|false The number of elements stored in the destination SET or * false on failure. */ public function sUnionStore(string $dst, string $key, string ...$other_keys): Redis|int|false; /** * Persist the Redis database to disk. This command will block the server until the save is * completed. For a nonblocking alternative, see Redis::bgsave(). * * @see https://redis.io/commands/save * @see Redis::bgsave() * * @return Redis|bool Returns true unless an error occurs. */ public function save(): Redis|bool; /** * Incrementally scan the Redis keyspace, with optional pattern and type matching. * * A note about Redis::SCAN_NORETRY and Redis::SCAN_RETRY. * * For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of * keys with a nonzero iterator. This can happen when matching against a pattern that very few * keys match inside a key space with a great many keys. The following example demonstrates how * to use Redis::scan() with the option disabled and enabled. * * @param int $iterator The cursor returned by Redis for every subsequent call to SCAN. On * the initial invocation of the call, it should be initialized by the * caller to NULL. Each time SCAN is invoked, the iterator will be * updated to a new number, until finally Redis will set the value to * zero, indicating that the scan is complete. * * @param string $pattern An optional glob-style pattern for matching key names. If passed as * NULL, it is the equivalent of sending '*' (match every key). * * @param int $count A hint to redis that tells it how many keys to return in a single * call to SCAN. The larger the number, the longer Redis may block * clients while iterating the key space. * * @param string $type An optional argument to specify which key types to scan (e.g. * 'STRING', 'LIST', 'SET') * * @return array|false An array of keys, or false if no keys were returned for this * invocation of scan. Note that it is possible for Redis to return * zero keys before having scanned the entire key space, so the caller * should instead continue to SCAN until the iterator reference is * returned to zero. * * @see https://redis.io/commands/scan * @see Redis::setOption() * * @example * $redis = new Redis(['host' => 'localhost']); * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); * * $it = null; * * do { * $keys = $redis->scan($it, '*zorg*'); * foreach ($keys as $key) { * echo "KEY: $key\n"; * } * } while ($it != 0); * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); * * $it = null; * * // When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an * // empty array of keys when the iterator is nonzero. * while ($keys = $redis->scan($it, '*zorg*')) { * foreach ($keys as $key) { * echo "KEY: $key\n"; * } * } */ public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, ?string $type = null): array|false; /** * Retrieve the number of members in a Redis set. * * @param string $key The set to get the cardinality of. * * @return Redis|int|false The cardinality of the set or false on failure. * * @see https://redis.io/commands/scard * * @example $redis->scard('set'); * */ public function scard(string $key): Redis|int|false; /** * An administrative command used to interact with LUA scripts stored on the server. * * @see https://redis.io/commands/script * * @param string $command The script suboperation to execute. * @param mixed $args One ore more additional argument * * @return mixed This command returns various things depending on the specific operation executed. * * @example $redis->script('load', 'return 1'); * @example $redis->script('exists', sha1('return 1')); */ public function script(string $command, mixed ...$args): mixed; /** * Select a specific Redis database. * * @param int $db The database to select. Note that by default Redis has 16 databases (0-15). * * @return Redis|bool true on success and false on failure * * @see https://redis.io/commands/select * * @example $redis->select(1); */ public function select(int $db): Redis|bool; /** * Create or set a Redis STRING key to a value. * * @param string $key The key name to set. * @param mixed $value The value to set the key to. * @param array|int $options Either an array with options for how to perform the set or an * integer with an expiration. If an expiration is set PhpRedis * will actually send the `SETEX` command. * * OPTION DESCRIPTION * ------------ -------------------------------------------------------------- * ['EX' => 60] expire 60 seconds. * ['PX' => 6000] expire in 6000 milliseconds. * ['EXAT' => time() + 10] expire in 10 seconds. * ['PXAT' => time()*1000 + 1000] expire in 1 second. * ['KEEPTTL' => true] Redis will not update the key's current TTL. * ['XX'] Only set the key if it already exists. * ['NX'] Only set the key if it doesn't exist. * ['GET'] Instead of returning `+OK` return the previous value of the * key or NULL if the key didn't exist. * * @return Redis|string|bool True if the key was set or false on failure. * * @see https://redis.io/commands/set * @see https://redis.io/commands/setex * * @example $redis->set('key', 'value'); * @example $redis->set('key', 'expires_in_60_seconds', 60); */ public function set(string $key, mixed $value, mixed $options = null): Redis|string|bool; /** * Set a specific bit in a Redis string to zero or one * * @see https://redis.io/commands/setbit * * @param string $key The Redis STRING key to modify * @param bool $value Whether to set the bit to zero or one. * * @return Redis|int|false The original value of the bit or false on failure. * * @example * $redis->set('foo', 'bar'); * $redis->setbit('foo', 7, 1); */ public function setBit(string $key, int $idx, bool $value): Redis|int|false; /** * Update or append to a Redis string at a specific starting index * * @see https://redis.io/commands/setrange * * @param string $key The key to update * @param int $index Where to insert the provided value * @param string $value The value to copy into the string. * * @return Redis|int|false The new length of the string or false on failure * * @example * $redis->set('message', 'Hello World'); * $redis->setRange('message', 6, 'Redis'); */ public function setRange(string $key, int $index, string $value): Redis|int|false; /** * Set a configurable option on the Redis object. * * Following are a list of options you can set: * * | OPTION | TYPE | DESCRIPTION | * | --------------- | ---- | ----------- | * | OPT_MAX_RETRIES | int | The maximum number of times Redis will attempt to reconnect if it gets disconnected, before throwing an exception. | * | OPT_SCAN | enum | Redis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY. Whether PhpRedis should automatically SCAN again when zero keys but a nonzero iterator are returned. | * | OPT_SERIALIZER | enum | Set the automatic data serializer.
`Redis::SERIALIZER_NONE`
`Redis::SERIALIZER_PHP`
`Redis::SERIALIZER_IGBINARY`
`Redis::SERIALIZER_MSGPACK`, `Redis::SERIALIZER_JSON`| * | OPT_PREFIX | string | A string PhpRedis will use to prefix every key we read or write. | * | OPT_READ_TIMEOUT | float | How long PhpRedis will block for a response from Redis before throwing a 'read error on connection' exception. | * | OPT_TCP_KEEPALIVE | bool | Set or disable TCP_KEEPALIVE on the connection. | * | OPT_COMPRESSION | enum | Set the compression algorithm
`Redis::COMPRESSION_NONE`
`Redis::COMPRESSION_LZF`
`Redis::COMPRESSION_LZ4`
`Redis::COMPRESSION_ZSTD` | * | OPT_REPLY_LITERAL | bool | If set to true, PhpRedis will return the literal string Redis returns for LINE replies (e.g. '+OK'), rather than `true`. | * | OPT_COMPRESSION_LEVEL | int | Set a specific compression level if Redis is compressing data. | * | OPT_NULL_MULTIBULK_AS_NULL | bool | Causes PhpRedis to return `NULL` rather than `false` for NULL MULTIBULK replies | * | OPT_BACKOFF_ALGORITHM | enum | The exponential backoff strategy to use. | * | OPT_BACKOFF_BASE | int | The minimum delay between retries when backing off. | * | OPT_BACKOFF_CAP | int | The maximum delay between replies when backing off. | * * @see Redis::getOption() * @see Redis::__construct() for details about backoff strategies. * * @param int $option The option constant. * @param mixed $value The option value. * * @return bool true if the setting was updated, false if not. * */ public function setOption(int $option, mixed $value): bool; /** * Set a Redis STRING key with a specific expiration in seconds. * * @param string $key The name of the key to set. * @param int $expire The key's expiration in seconds. * @param mixed $value The value to set the key. * * @return Redis|bool True on success or false on failure. * * @example $redis->setex('60s-ttl', 60, 'some-value'); */ public function setex(string $key, int $expire, mixed $value); /** * Set a key to a value, but only if that key does not already exist. * * @see https://redis.io/commands/setnx * * @param string $key The key name to set. * @param mixed $value What to set the key to. * * @return Redis|bool Returns true if the key was set and false otherwise. * * @example $redis->setnx('existing-key', 'existing-value'); * @example $redis->setnx('new-key', 'new-value'); */ public function setnx(string $key, mixed $value): Redis|bool; /** * Check whether a given value is the member of a Redis SET. * * @param string $key The redis set to check. * @param mixed $value The value to test. * * @return Redis|bool True if the member exists and false if not. * * @example $redis->sismember('myset', 'mem1', 'mem2'); */ public function sismember(string $key, mixed $value): Redis|bool; /** * Turn a redis instance into a replica of another or promote a replica * to a primary. * * This method and the corresponding command in Redis has been marked deprecated * and users should instead use Redis::replicaof() if connecting to redis-server * >= 5.0.0. * * @deprecated * * @see https://redis.io/commands/slaveof * @see https://redis.io/commands/replicaof * @see Redis::replicaof() */ public function slaveof(?string $host = null, int $port = 6379): Redis|bool; /** * Used to turn a Redis instance into a replica of another, or to remove * replica status promoting the instance to a primary. * * @see https://redis.io/commands/replicaof * @see https://redis.io/commands/slaveof * @see Redis::slaveof() * * @param string $host The host of the primary to start replicating. * @param string $port The port of the primary to start replicating. * * @return Redis|bool Success if we were successfully able to start replicating a primary or * were able to promote teh replicat to a primary. * * @example * $redis = new Redis(['host' => 'localhost']); * * // Attempt to become a replica of a Redis instance at 127.0.0.1:9999 * $redis->replicaof('127.0.0.1', 9999); * * // When passed no arguments, PhpRedis will deliver the command `REPLICAOF NO ONE` * // attempting to promote the instance to a primary. * $redis->replicaof(); */ public function replicaof(?string $host = null, int $port = 6379): Redis|bool; /** * Update one or more keys last modified metadata. * * @see https://redis.io/commands/touch/ * * @param array|string $key Either the first key or if passed as the only argument * an array of keys. * @param string $more_keys One or more keys to send to the command. * * @return Redis|int|false This command returns the number of keys that exist and * had their last modified time reset */ public function touch(array|string $key_or_array, string ...$more_keys): Redis|int|false; /** * Interact with Redis' slowlog functionality in various ways, depending * on the value of 'operation'. * * @category administration * * @param string $operation The operation you wish to perform.  This can * be one of the following values: * 'GET' - Retrieve the Redis slowlog as an array. * 'LEN' - Retrieve the length of the slowlog. * 'RESET' - Remove all slowlog entries. * @param int $length This optional argument can be passed when operation * is 'get' and will specify how many elements to retrieve. * If omitted Redis will send up to a default number of * entries, which is configurable. * * Note: With Redis >= 7.0.0 you can send -1 to mean "all". * * @return mixed * * @see https://redis.io/commands/slowlog/ * * @example $redis->slowlog('get', -1); // Retrieve all slowlog entries. * @example $redis->slowlog('len'); // Retrieve slowlog length. * @example $redis->slowlog('reset'); // Reset the slowlog. */ public function slowlog(string $operation, int $length = 0): mixed; /** * Sort the contents of a Redis key in various ways. * * @see https://redis.io/commands/sort/ * * @param string $key The key you wish to sort * @param array $options Various options controlling how you would like the * data sorted. See blow for a detailed description * of this options array. * * @return mixed This command can either return an array with the sorted data * or the number of elements placed in a destination set when * using the STORE option. * * @example * $options = [ * 'SORT' => 'ASC'|| 'DESC' // Sort in descending or descending order. * 'ALPHA' => true || false // Whether to sort alphanumerically. * 'LIMIT' => [0, 10] // Return a subset of the data at offset, count * 'BY' => 'weight_*' // For each element in the key, read data from the * external key weight_* and sort based on that value. * 'GET' => 'weight_*' // For each element in the source key, retrieve the * data from key weight_* and return that in the result * rather than the source keys' element. This can * be used in combination with 'BY' * ]; */ public function sort(string $key, ?array $options = null): mixed; /** * This is simply a read-only variant of the sort command * * @see Redis::sort() */ public function sort_ro(string $key, ?array $options = null): mixed; /** * @deprecated */ public function sortAsc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array; /** * @deprecated */ public function sortAscAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array; /** * @deprecated */ public function sortDesc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array; /** * @deprecated */ public function sortDescAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array; /** * Remove one or more values from a Redis SET key. * * @see https://redis.io/commands/srem * * @param string $key The Redis SET key in question. * @param mixed $value The first value to remove. * @param mixed $more_values One or more additional values to remove. * * @return Redis|int|false The number of values removed from the set or false on failure. * * @example $redis->sRem('set1', 'mem1', 'mem2', 'not-in-set'); */ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|int|false; /** * Scan the members of a redis SET key. * * @see https://redis.io/commands/sscan * @see https://redis.io/commands/scan * @see Redis::setOption() * * @param string $key The Redis SET key in question. * @param int $iterator A reference to an iterator which should be initialized to NULL that * PhpRedis will update with the value returned from Redis after each * subsequent call to SSCAN. Once this cursor is zero you know all * members have been traversed. * @param string $pattern An optional glob style pattern to match against, so Redis only * returns the subset of members matching this pattern. * @param int $count A hint to Redis as to how many members it should scan in one command * before returning members for that iteration. * * @example * $redis->del('myset'); * for ($i = 0; $i < 10000; $i++) { * $redis->sAdd('myset', "member:$i"); * } * $redis->sadd('myset', 'foofoo'); * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); * * $scanned = 0; * $it = null; * * // Without Redis::SCAN_RETRY we may receive empty results and * // a nonzero iterator. * do { * // Scan members containing '5' * $members = $redis->sscan('myset', $it, '*5*'); * foreach ($members as $member) { * echo "NORETRY: $member\n"; * $scanned++; * } * } while ($it != 0); * echo "TOTAL: $scanned\n"; * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); * * $scanned = 0; * $it = null; * * // With Redis::SCAN_RETRY PhpRedis will never return an empty array * // when the cursor is non-zero * while (($members = $redis->sscan('myset', $it, '*5*'))) { * foreach ($members as $member) { * echo "RETRY: $member\n"; * $scanned++; * } * } */ public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false; /** * Subscribes the client to the specified shard channels. * * @param array $channels One or more channel names. * @param callable $cb The callback PhpRedis will invoke when we receive a message * from one of the subscribed channels. * * @return bool True on success, false on faiilure. Note that this command will block the * client in a subscribe loop, waiting for messages to arrive. * * @see https://redis.io/commands/ssubscribe * * @example * $redis = new Redis(['host' => 'localhost']); * * $redis->ssubscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { * echo "[$channel]: $message\n"; * * // Unsubscribe from the message channel when we read 'quit' * if ($message == 'quit') { * echo "Unsubscribing from '$channel'\n"; * $redis->sunsubscribe([$channel]); * } * }); * * // Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be * // broken and this command will execute. * echo "Subscribe loop ended\n"; */ public function ssubscribe(array $channels, callable $cb): bool; /** * Retrieve the length of a Redis STRING key. * * @param string $key The key we want the length of. * * @return Redis|int|false The length of the string key if it exists, zero if it does not, and * false on failure. * * @see https://redis.io/commands/strlen * * @example $redis->strlen('mykey'); */ public function strlen(string $key): Redis|int|false; /** * Subscribe to one or more Redis pubsub channels. * * @param array $channels One or more channel names. * @param callable $cb The callback PhpRedis will invoke when we receive a message * from one of the subscribed channels. * * @return bool True on success, false on faiilure. Note that this command will block the * client in a subscribe loop, waiting for messages to arrive. * * @see https://redis.io/commands/subscribe * * @example * $redis = new Redis(['host' => 'localhost']); * * $redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { * echo "[$channel]: $message\n"; * * // Unsubscribe from the message channel when we read 'quit' * if ($message == 'quit') { * echo "Unsubscribing from '$channel'\n"; * $redis->unsubscribe([$channel]); * } * }); * * // Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be * // broken and this command will execute. * echo "Subscribe loop ended\n"; */ public function subscribe(array $channels, callable $cb): bool; /** * Unsubscribes the client from the given shard channels, * or from all of them if none is given. * * @param array $channels One or more channels to unsubscribe from. * @return Redis|array|bool The array of unsubscribed channels. * * @see https://redis.io/commands/sunsubscribe * @see Redis::ssubscribe() * * @example * $redis->ssubscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { * if ($message == 'quit') { * echo "$channel => 'quit' detected, unsubscribing!\n"; * $redis->sunsubscribe([$channel]); * } else { * echo "$channel => $message\n"; * } * }); * * echo "We've unsubscribed from both channels, exiting\n"; */ public function sunsubscribe(array $channels): Redis|array|bool; /** * Atomically swap two Redis databases so that all of the keys in the source database will * now be in the destination database and vice-versa. * * Note: This command simply swaps Redis' internal pointer to the database and is therefore * very fast, regardless of the size of the underlying databases. * * @param int $src The source database number * @param int $dst The destination database number * * @return Redis|bool Success if the databases could be swapped and false on failure. * * @see https://redis.io/commands/swapdb * @see Redis::del() * * @example * $redis->select(0); * $redis->set('db0-key', 'db0-value'); * $redis->swapdb(0, 1); * $redis->get('db0-key'); */ public function swapdb(int $src, int $dst): Redis|bool; /** * Retrieve the server time from the connected Redis instance. * * @see https://redis.io/commands/time * * @return A two element array consisting of a Unix Timestamp and the number of microseconds * elapsed since the second. * * @example $redis->time(); */ public function time(): Redis|array; /** * Get the amount of time a Redis key has before it will expire, in seconds. * * @param string $key The Key we want the TTL for. * @return Redis|int|false (a) The number of seconds until the key expires, or -1 if the key has * no expiration, and -2 if the key does not exist. In the event of an * error, this command will return false. * * @see https://redis.io/commands/ttl * * @example $redis->ttl('mykey'); */ public function ttl(string $key): Redis|int|false; /** * Get the type of a given Redis key. * * @see https://redis.io/commands/type * * @param string $key The key to check * @return Redis|int|false The Redis type constant or false on failure. * * The Redis class defines several type constants that correspond with Redis key types. * * Redis::REDIS_NOT_FOUND * Redis::REDIS_STRING * Redis::REDIS_SET * Redis::REDIS_LIST * Redis::REDIS_ZSET * Redis::REDIS_HASH * Redis::REDIS_STREAM * * @example * foreach ($redis->keys('*') as $key) { * echo "$key => " . $redis->type($key) . "\n"; * } */ public function type(string $key): Redis|int|false; /** * Delete one or more keys from the Redis database. Unlike this operation, the actual * deletion is asynchronous, meaning it is safe to delete large keys without fear of * Redis blocking for a long period of time. * * @param array|string $key_or_keys Either an array with one or more keys or a string with * the first key to delete. * @param string $other_keys If the first argument passed to this method was a string * you may pass any number of additional key names. * * @return Redis|int|false The number of keys deleted or false on failure. * * @see https://redis.io/commands/unlink * @see https://redis.io/commands/del * @see Redis::del() * * @example $redis->unlink('key1', 'key2', 'key3'); * @example $redis->unlink(['key1', 'key2', 'key3']); */ public function unlink(array|string $key, string ...$other_keys): Redis|int|false; /** * Unsubscribe from one or more subscribed channels. * * @param array $channels One or more channels to unsubscribe from. * @return Redis|array|bool The array of unsubscribed channels. * * @see https://redis.io/commands/unsubscribe * @see Redis::subscribe() * * @example * $redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { * if ($message == 'quit') { * echo "$channel => 'quit' detected, unsubscribing!\n"; * $redis->unsubscribe([$channel]); * } else { * echo "$channel => $message\n"; * } * }); * * echo "We've unsubscribed from both channels, exiting\n"; */ public function unsubscribe(array $channels): Redis|array|bool; /** * Remove any previously WATCH'ed keys in a transaction. * * @see https://redis.io/commands/unwatch * @see https://redis.io/commands/unwatch * @see Redis::watch() * * @return True on success and false on failure. */ public function unwatch(): Redis|bool; /** * Watch one or more keys for conditional execution of a transaction. * * @param array|string $key_or_keys Either an array with one or more key names, or a string key name * @param string $other_keys If the first argument was passed as a string, any number of additional * string key names may be passed variadically. * @return Redis|bool * * * @see https://redis.io/commands/watch * @see https://redis.io/commands/unwatch * * @example * $redis1 = new Redis(['host' => 'localhost']); * $redis2 = new Redis(['host' => 'localhost']); * * // Start watching 'incr-key' * $redis1->watch('incr-key'); * * // Retrieve its value. * $val = $redis1->get('incr-key'); * * // A second client modifies 'incr-key' after we read it. * $redis2->set('incr-key', 0); * * // Because another client changed the value of 'incr-key' after we read it, this * // is no longer a proper increment operation, but because we are `WATCH`ing the * // key, this transaction will fail and we can try again. * // * // If were to comment out the above `$redis2->set('incr-key', 0)` line the * // transaction would succeed. * $redis1->multi(); * $redis1->set('incr-key', $val + 1); * $res = $redis1->exec(); * * // bool(false) * var_dump($res); */ public function watch(array|string $key, string ...$other_keys): Redis|bool; /** * Block the client up to the provided timeout until a certain number of replicas have confirmed * recieving them. * * @see https://redis.io/commands/wait * * @param int $numreplicas The number of replicas we want to confirm write operaions * @param int $timeout How long to wait (zero meaning forever). * * @return Redis|int|false The number of replicas that have confirmed or false on failure. * */ public function wait(int $numreplicas, int $timeout): int|false; /** * Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but * not yet acknowledged by XACK.) * * @param string $key The stream to query. * @param string $group The consumer group to use. * @param array $ids An array of stream entry IDs. * * @return int|false The number of acknowledged messages * * @see https://redis.io/commands/xack * @see https://redis.io/commands/xreadgroup * @see Redis::xack() * * @example * $redis->xAdd('ships', '*', ['name' => 'Enterprise']); * $redis->xAdd('ships', '*', ['name' => 'Defiant']); * * $redis->xGroup('CREATE', 'ships', 'Federation', '0-0'); * * // Consume a single message with the consumer group 'Federation' * $ship = $redis->xReadGroup('Federation', 'Picard', ['ships' => '>'], 1); * * /* Retrieve the ID of the message we read. * assert(isset($ship['ships'])); * $id = key($ship['ships']); * * // The message we just read is now pending. * $res = $redis->xPending('ships', 'Federation')); * var_dump($res); * * // We can tell Redis we were able to process the message by using XACK * $res = $redis->xAck('ships', 'Federation', [$id]); * assert($res === 1); * * // The message should no longer be pending. * $res = $redis->xPending('ships', 'Federation'); * var_dump($res); */ public function xack(string $key, string $group, array $ids): int|false; /** * Append a message to a stream. * * @param string $key The stream name. * @param string $id The ID for the message we want to add. This can be the special value '*' * which means Redis will generate the ID that appends the message to the * end of the stream. It can also be a value in the form -* which will * generate an ID that appends to the end ot entries with the same value * (if any exist). * @param int $maxlen If specified Redis will append the new message but trim any number of the * oldest messages in the stream until the length is <= $maxlen. * @param bool $approx Used in conjunction with `$maxlen`, this flag tells Redis to trim the stream * but in a more efficient way, meaning the trimming may not be exactly to * `$maxlen` values. * @param bool $nomkstream If passed as `TRUE`, the stream must exist for Redis to append the message. * * @see https://redis.io/commands/xadd * * @example $redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']); * @example $redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']); */ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): Redis|string|false; /** * This command allows a consumer to claim pending messages that have been idle for a specified period of time. * Its purpose is to provide a mechanism for picking up messages that may have had a failed consumer. * * @see https://redis.io/commands/xautoclaim * @see https://redis.io/commands/xclaim * @see https://redis.io/docs/data-types/streams-tutorial/ * * @param string $key The stream to check. * @param string $group The consumer group to query. * @param string $consumer Which consumer to check. * @param int $min_idle The minimum time in milliseconds for the message to have been pending. * @param string $start The minimum message id to check. * @param int $count An optional limit on how many messages are returned. * @param bool $justid If the client only wants message IDs and not all of their data. * * @return Redis|array|bool An array of pending IDs or false if there are none, or on failure. * * @example * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); * * $redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); * * // Consume the ['name' => 'Defiant'] message * $msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1); * * // The "Jem'Hadar" consumer has the message presently * $pending = $redis->xPending('ships', 'combatants'); * var_dump($pending); * * // Asssume control of the pending message with a different consumer. * $res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); * * // Now the 'Sisko' consumer owns the message * $pending = $redis->xPending('ships', 'combatants'); * var_dump($pending); */ public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array; /** * This method allows a consumer to take ownership of pending stream entries, by ID. Another * command that does much the same thing but does not require passing specific IDs is `Redis::xAutoClaim`. * * @see https://redis.io/commands/xclaim * @see https://redis.io/commands/xautoclaim. * * @param string $key The stream we wish to claim messages for. * @param string $group Our consumer group. * @param string $consumer Our consumer. * @param int $min_idle_time The minimum idle-time in milliseconds a message must have for ownership to be transferred. * @param array $options An options array that modifies how the command operates. * * * # Following is an options array describing every option you can pass. Note that * # 'IDLE', and 'TIME' are mutually exclusive. * $options = [ * 'IDLE' => 3 # Set the idle time of the message to a 3. By default * # the idle time is set to zero. * 'TIME' => 1000*time() # Same as IDLE except it takes a unix timestamp in * # milliseconds. * 'RETRYCOUNT' => 0 # Set the retry counter to zero. By default XCLAIM * # doesn't modify the counter. * 'FORCE' # Creates the pending message entry even if IDs are * # not already * # in the PEL with another client. * 'JUSTID' # Return only an array of IDs rather than the messages * # themselves. * ]; * * * @return Redis|array|bool An array of claimed messags or false on failure. * * @example * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); * * $redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); * * // Consume the ['name' => 'Defiant'] message * $msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1); * * // The "Jem'Hadar" consumer has the message presently * $pending = $redis->xPending('ships', 'combatants'); * var_dump($pending); * * assert($pending && isset($pending[1])); * * // Claim the message by ID. * $claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']); * var_dump($claimed); * * // Now the 'Sisko' consumer owns the message * $pending = $redis->xPending('ships', 'combatants'); * var_dump($pending); */ public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): Redis|array|bool; /** * Remove one or more specific IDs from a stream. * * @param string $key The stream to modify. * @param array $ids One or more message IDs to remove. * * @return Redis|int|false The number of messages removed or false on failure. * * @example $redis->xDel('stream', ['1-1', '2-1', '3-1']); */ public function xdel(string $key, array $ids): Redis|int|false; /** * XGROUP * * Perform various operation on consumer groups for a particular Redis STREAM. What the command does * is primarily based on which operation is passed. * * @see https://redis.io/commands/xgroup/ * * @param string $operation The subcommand you intend to execute. Valid options are as follows * 'HELP' - Redis will return information about the command * Requires: none * 'CREATE' - Create a consumer group. * Requires: Key, group, consumer. * 'SETID' - Set the ID of an existing consumer group for the stream. * Requires: Key, group, id. * 'CREATECONSUMER' - Create a new consumer group for the stream. You must * also pass key, group, and the consumer name you wish to * create. * Requires: Key, group, consumer. * 'DELCONSUMER' - Delete a consumer from group attached to the stream. * Requires: Key, group, consumer. * 'DESTROY' - Delete a consumer group from a stream. * Requires: Key, group. * @param string $key The STREAM we're operating on. * @param string $group The consumer group we want to create/modify/delete. * @param string $id_or_consumer The STREAM id (e.g. '$') or consumer group. See the operation section * for information about which to send. * @param bool $mkstream This flag may be sent in combination with the 'CREATE' operation, and * cause Redis to also create the STREAM if it doesn't currently exist. * * @param bool $entriesread Allows you to set Redis' 'entries-read' STREAM value. This argument is * only relevant to the 'CREATE' and 'SETID' operations. * Note: Requires Redis >= 7.0.0. * * @return mixed This command return various results depending on the operation performed. */ public function xgroup(string $operation, ?string $key = null, ?string $group = null, ?string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2): mixed; /** * Retrieve information about a stream key. * * @param string $operation The specific info operation to perform. * @param string $arg1 The first argument (depends on operation) * @param string $arg2 The second argument * @param int $count The COUNT argument to `XINFO STREAM` * * @return mixed This command can return different things depending on the operation being called. * * @see https://redis.io/commands/xinfo * * @example $redis->xInfo('CONSUMERS', 'stream'); * @example $redis->xInfo('GROUPS', 'stream'); * @example $redis->xInfo('STREAM', 'stream'); */ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed; /** * Get the number of messages in a Redis STREAM key. * * @param string $key The Stream to check. * * @return Redis|int|false The number of messages or false on failure. * * @see https://redis.io/commands/xlen * * @example $redis->xLen('stream'); */ public function xlen(string $key): Redis|int|false; /** * Interact with stream messages that have been consumed by a consumer group but not yet * acknowledged with XACK. * * @see https://redis.io/commands/xpending * @see https://redis.io/commands/xreadgroup * * @param string $key The stream to inspect. * @param string $group The user group we want to see pending messages from. * @param string $start The minimum ID to consider. * @param string $string The maximum ID to consider. * @param string $count Optional maximum number of messages to return. * @param string $consumer If provided, limit the returned messages to a specific consumer. * * @return Redis|array|false The pending messages belonging to the stream or false on failure. * */ public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false; /** * Get a range of entries from a STREAM key. * * @param string $key The stream key name to list. * @param string $start The minimum ID to return. * @param string $end The maximum ID to return. * @param int $count An optional maximum number of entries to return. * * @return Redis|array|bool The entries in the stream within the requested range or false on failure. * * @see https://redis.io/commands/xrange * * @example $redis->xRange('stream', '0-1', '0-2'); * @example $redis->xRange('stream', '-', '+'); */ public function xrange(string $key, string $start, string $end, int $count = -1): Redis|array|bool; /** * Consume one or more unconsumed elements in one or more streams. * * @param array $streams An associative array with stream name keys and minimum id values. * @param int $count An optional limit to how many entries are returnd *per stream* * @param int $block An optional maximum number of milliseconds to block the caller if no * data is available on any of the provided streams. * * @return Redis|array|bool An array of read elements or false if there aren't any. * * @see https://redis.io/commands/xread * * @example * $redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']); * $redis->xAdd('s03', '3-2', ['title' => 'The Search, Part II']); * $redis->xAdd('s03', '3-3', ['title' => 'The House Of Quark']); * $redis->xAdd('s04', '4-1', ['title' => 'The Way of the Warrior']); * $redis->xAdd('s04', '4-3', ['title' => 'The Visitor']); * $redis->xAdd('s04', '4-4', ['title' => 'Hippocratic Oath']); * * $redis->xRead(['s03' => '3-2', 's04' => '4-1']); */ public function xread(array $streams, int $count = -1, int $block = -1): Redis|array|bool; /** * Read one or more messages using a consumer group. * * @param string $group The consumer group to use. * @param string $consumer The consumer to use. * @param array $streams An array of stream names and message IDs * @param int $count Optional maximum number of messages to return * @param int $block How long to block if there are no messages available. * * @return Redis|array|bool Zero or more unread messages or false on failure. * * @see https://redis.io/commands/xreadgroup * * @example * $redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true); * * $redis->xAdd('episodes', '1-1', ['title' => 'Emissary: Part 1']); * $redis->xAdd('episodes', '1-2', ['title' => 'A Man Alone']); * * $messages = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']); * * // After having read the two messages, add another * $redis->xAdd('episodes', '1-3', ['title' => 'Emissary: Part 2']); * * // Acknowledge the first two read messages * foreach ($messages as $stream => $stream_messages) { * $ids = array_keys($stream_messages); * $redis->xAck('stream', 'ds9', $ids); * } * * // We can now pick up where we left off, and will only get the final message * $msgs = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']); */ public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): Redis|array|bool; /** * Get a range of entries from a STREAM ke in reverse cronological order. * * @param string $key The stream key to query. * @param string $end The maximum message ID to include. * @param string $start The minimum message ID to include. * @param int $count An optional maximum number of messages to include. * * @return Redis|array|bool The entries within the requested range, from newest to oldest. * * @see https://redis.io/commands/xrevrange * @see https://redis.io/commands/xrange * * @example $redis->xRevRange('stream', '0-2', '0-1'); * @example $redis->xRevRange('stream', '+', '-'); */ public function xrevrange(string $key, string $end, string $start, int $count = -1): Redis|array|bool; /** * Truncate a STREAM key in various ways. * * @param string $key The STREAM key to trim. * @param string $threshold This can either be a maximum length, or a minimum id. * MAXLEN - An integer describing the maximum desired length of the stream after the command. * MINID - An ID that will become the new minimum ID in the stream, as Redis will trim all * messages older than this ID. * @param bool $approx Whether redis is allowed to do an approximate trimming of the stream. This is * more efficient for Redis given how streams are stored internally. * @param bool $minid When set to `true`, users should pass a minimum ID to the `$threshold` argument. * @param int $limit An optional upper bound on how many entries to trim during the command. * * @return Redis|int|false The number of entries deleted from the stream. * * @see https://redis.io/commands/xtrim * * @example $redis->xTrim('stream', 3); * @example $redis->xTrim('stream', '2-1', false, true); */ public function xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1): Redis|int|false; /** * Add one or more elements and scores to a Redis sorted set. * * @param string $key The sorted set in question. * @param array|float $score_or_options Either the score for the first element, or an array of options. * * $options = [ * 'NX', # Only update elements that already exist * 'NX', # Only add new elements but don't update existing ones. * * 'LT' # Only update existing elements if the new score is * # less than the existing one. * 'GT' # Only update existing elements if the new score is * # greater than the existing one. * * 'CH' # Instead of returning the number of elements added, * # Redis will return the number Of elements that were * # changed in the operation. * * 'INCR' # Instead of setting each element to the provide score, * # increment the element by the * # provided score, much like ZINCRBY. When this option * # is passed, you may only send a single score and member. * ]; * * Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis * will send whichever one is last in the options array. * * @param mixed $more_scores_and_mems A variadic number of additional scores and members. * * @return Redis|int|false The return value varies depending on the options passed. * * Following is information about the options that may be passed as the second argument: * * @see https://redis.io/commands/zadd * * @example $redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third'); * @example $redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element'); */ public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|float|false; /** * Return the number of elements in a sorted set. * * @param string $key The sorted set to retreive cardinality from. * * @return Redis|int|false The number of elements in the set or false on failure * * @see https://redis.io/commands/zcard * * @example $redis->zCard('zs'); */ public function zCard(string $key): Redis|int|false; /** * Count the number of members in a sorted set with scores inside a provided range. * * @param string $key The sorted set to check. * @param string $min The minimum score to include in the count * @param string $max The maximum score to include in the count * * NOTE: In addition to a floating point score you may pass the special values of '-inf' and * '+inf' meaning negative and positive infinity, respectively. * * @see https://redis.io/commands/zcount * * @example $redis->zCount('fruit-rankings', '0', '+inf'); * @example $redis->zCount('fruit-rankings', 50, 60); * @example $redis->zCount('fruit-rankings', '-inf', 0); */ public function zCount(string $key, string $start, string $end): Redis|int|false; /** * Create or increment the score of a member in a Redis sorted set * * @param string $key The sorted set in question. * @param float $value How much to increment the score. * * @return Redis|float|false The new score of the member or false on failure. * * @see https://redis.io/commands/zincrby * * @example $redis->zIncrBy('zs', 5.0, 'bananas'); * @example $redis->zIncrBy('zs', 2.0, 'eggplants'); */ public function zIncrBy(string $key, float $value, mixed $member): Redis|float|false; /** * Count the number of elements in a sorted set whos members fall within the provided * lexographical range. * * @param string $key The sorted set to check. * @param string $min The minimum matching lexographical string * @param string $max The maximum matching lexographical string * * @return Redis|int|false The number of members that fall within the range or false on failure. * * @see https://redis.io/commands/zlexcount * * @example * $redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer'); * $redis->zLexCount('captains', '[A', '[S'); */ public function zLexCount(string $key, string $min, string $max): Redis|int|false; /** * Retrieve the score of one or more members in a sorted set. * * @see https://redis.io/commands/zmscore * * @param string $key The sorted set * @param mixed $member The first member to return the score from * @param mixed $other_members One or more additional members to return the scores of. * * @return Redis|array|false An array of the scores of the requested elements. * * @example * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three'); * * $redis->zMScore('zs', 'zero', 'two'); * $redis->zMScore('zs', 'one', 'not-a-member'); */ public function zMscore(string $key, mixed $member, mixed ...$other_members): Redis|array|false; /** * Pop one or more of the highest scoring elements from a sorted set. * * @param string $key The sorted set to pop elements from. * @param int $count An optional count of elements to pop. * * @return Redis|array|false All of the popped elements with scores or false on fialure. * * @see https://redis.io/commands/zpopmax * * @example * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three'); * * $redis->zPopMax('zs'); * $redis->zPopMax('zs', 2);. */ public function zPopMax(string $key, ?int $count = null): Redis|array|false; /** * Pop one or more of the lowest scoring elements from a sorted set. * * @param string $key The sorted set to pop elements from. * @param int $count An optional count of elements to pop. * * @return Redis|array|false The popped elements with their scores or false on failure. * * @see https://redis.io/commands/zpopmin * * @example * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three'); * * $redis->zPopMin('zs'); * $redis->zPopMin('zs', 2); */ public function zPopMin(string $key, ?int $count = null): Redis|array|false; /** * Retrieve a range of elements of a sorted set between a start and end point. * How the command works in particular is greatly affected by the options that * are passed in. * * @param string $key The sorted set in question. * @param mixed $start The starting index we want to return. * @param mixed $end The final index we want to return. * * @param array|bool|null $options This value may either be an array of options to pass to * the command, or for historical purposes a boolean which * controls just the 'WITHSCORES' option. * * $options = [ * 'WITHSCORES' => true, # Return both scores and members. * 'LIMIT' => [10, 10], # Start at offset 10 and return 10 elements. * 'REV' # Return the elements in reverse order * 'BYSCORE', # Treat `start` and `end` as scores instead * 'BYLEX' # Treat `start` and `end` as lexicographical values. * ]; * * * Note: 'BYLEX' and 'BYSCORE' are mutually exclusive. * * * @return Redis|array|false An array with matching elements or false on failure. * * @see https://redis.io/commands/zrange/ * @category zset * * @example $redis->zRange('zset', 0, -1); * @example $redis->zRange('zset', '-inf', 'inf', ['byscore']); */ public function zRange(string $key, string|int $start, string|int $end, array|bool|null $options = null): Redis|array|false; /** * Retrieve a range of elements from a sorted set by legographical range. * * @param string $key The sorted set to retreive elements from * @param string $min The minimum legographical value to return * @param string $max The maximum legographical value to return * @param int $offset An optional offset within the matching values to return * @param int $count An optional count to limit the replies to (used in conjunction with offset) * * @return Redis|array|false An array of matching elements or false on failure. * * @see https://redis.io/commands/zrangebylex * * @example * $redis = new Redis(['host' => 'localhost']); * $redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer'); * * $redis->zRangeByLex('captains', '[A', '[S'); * $redis->zRangeByLex('captains', '[A', '[S', 2, 2); */ public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false; /** * Retrieve a range of members from a sorted set by their score. * * @param string $key The sorted set to query. * @param string $start The minimum score of elements that Redis should return. * @param string $end The maximum score of elements that Redis should return. * @param array $options Options that change how Redis will execute the command. * * OPTION TYPE MEANING * 'WITHSCORES' bool Whether to also return scores. * 'LIMIT' [offset, count] Limit the reply to a subset of elements. * * @return Redis|array|false The number of matching elements or false on failure. * * @see https://redis.io/commands/zrangebyscore * * @example $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]); * @example $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]); */ public function zRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false; /** * This command is similar to ZRANGE except that instead of returning the values directly * it will store them in a destination key provided by the user * * @param string $dstkey The key to store the resulting element(s) * @param string $srckey The source key with element(s) to retrieve * @param string $start The starting index to store * @param string $end The ending index to store * @param array|bool|null $options Our options array that controls how the command will function. * * @return Redis|int|false The number of elements stored in $dstkey or false on failure. * * @see https://redis.io/commands/zrange/ * @see Redis::zRange * @category zset * * See {@link Redis::zRange} for a full description of the possible options. */ public function zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = null): Redis|int|false; /** * Retrieve one or more random members from a Redis sorted set. * * @param string $key The sorted set to pull random members from. * @param array $options One or more options that determine exactly how the command operates. * * OPTION TYPE MEANING * 'COUNT' int The number of random members to return. * 'WITHSCORES' bool Whether to return scores and members instead of * * @return Redis|string|array One ore more random elements. * * @see https://redis.io/commands/zrandmember * * @example $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]); */ public function zRandMember(string $key, ?array $options = null): Redis|string|array; /** * Get the rank of a member of a sorted set, by score. * * @param string $key The sorted set to check. * @param mixed $memeber The member to test. * * @return Redis|int|false The rank of the requested member. * @see https://redis.io/commands/zrank * * @example $redis->zRank('zs', 'zero'); * @example $redis->zRank('zs', 'three'); */ public function zRank(string $key, mixed $member): Redis|int|false; /** * Remove one or more members from a Redis sorted set. * * @param mixed $key The sorted set in question. * @param mixed $member The first member to remove. * @param mixed $other_members One or more members to remove passed in a variadic fashion. * * @return Redis|int|false The number of members that were actually removed or false on failure. * * @see https://redis.io/commands/zrem * * @example $redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9'); */ public function zRem(mixed $key, mixed $member, mixed ...$other_members): Redis|int|false; /** * Remove zero or more elements from a Redis sorted set by legographical range. * * @param string $key The sorted set to remove elements from. * @param string $min The start of the lexographical range to remove. * @param string $max The end of the lexographical range to remove * * @return Redis|int|false The number of elements removed from the set or false on failure. * * @see https://redis.io/commands/zremrangebylex * @see Redis::zrangebylex() * * @example $redis->zRemRangeByLex('zs', '[a', '(b'); * @example $redis->zRemRangeByLex('zs', '(banana', '(eggplant'); */ public function zRemRangeByLex(string $key, string $min, string $max): Redis|int|false; /** * Remove one or more members of a sorted set by their rank. * * @param string $key The sorted set where we wnat to remove members. * @param int $start The rank when we want to start removing members * @param int $end The rank we want to stop removing membersk. * * @return Redis|int|false The number of members removed from the set or false on failure. * * @see https://redis.io/commands/zremrangebyrank * * @example $redis->zRemRangeByRank('zs', 0, 3); */ public function zRemRangeByRank(string $key, int $start, int $end): Redis|int|false; /** * Remove one or more members of a sorted set by their score. * * @param string $key The sorted set where we wnat to remove members. * @param int $start The lowest score to remove. * @param int $end The highest score to remove. * * @return Redis|int|false The number of members removed from the set or false on failure. * * @see https://redis.io/commands/zremrangebyrank * * @example * $redis->zAdd('zs', 2, 'two', 4, 'four', 6, 'six'); * $redis->zRemRangeByScore('zs', 2, 4); */ public function zRemRangeByScore(string $key, string $start, string $end): Redis|int|false; /** * List the members of a Redis sorted set in reverse order * * @param string $key The sorted set in question. * @param int $start The index to start listing elements * @param int $end The index to stop listing elements. * @param mixed $withscores Whether or not Redis should also return each members score. See * the example below demonstrating how it may be used. * * @return Redis|array|false The members (and possibly scores) of the matching elements or false * on failure. * * @see https://redis.io/commands/zrevrange * * @example $redis->zRevRange('zs', 0, -1); * @example $redis->zRevRange('zs', 2, 3); * @example $redis->zRevRange('zs', 0, -1, true); * @example $redis->zRevRange('zs', 0, -1, ['withscores' => true]); */ public function zRevRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false; /** * List members of a Redis sorted set within a legographical range, in reverse order. * * @param string $key The sorted set to list * @param string $min The maximum legographical element to include in the result. * @param string $min The minimum lexographical element to include in the result. * @param string $offset An option offset within the matching elements to start at. * @param string $count An optional count to limit the replies to. * * @return Redis|array|false The matching members or false on failure. * * @see https://redis.io/commands/zrevrangebylex * @see Redis::zrangebylex() * * @example $redis->zRevRangeByLex('captains', '[Q', '[J'); * @example $redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2); */ public function zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1): Redis|array|false; /** * List elements from a Redis sorted set by score, highest to lowest * * @param string $key The sorted set to query. * @param string $max The highest score to include in the results. * @param string $min The lowest score to include in the results. * @param array $options An options array that modifies how the command executes. * * * $options = [ * 'WITHSCORES' => true|false # Whether or not to return scores * 'LIMIT' => [offset, count] # Return a subset of the matching members * ]; * * * NOTE: For legacy reason, you may also simply pass `true` for the * options argument, to mean `WITHSCORES`. * * @return Redis|array|false The matching members in reverse order of score or false on failure. * * @see https://redis.io/commands/zrevrangebyscore * * @example * $redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka', * 119.2658, 'Sarah Knauss', 118.7205, 'Lucile Randon', * 117.7123, 'Nabi Tajima', 117.6301, 'Marie-Louise Meilleur', * 117.5178, 'Violet Brown', 117.3753, 'Emma Morano', * 117.2219, 'Chiyo Miyako', 117.0740, 'Misao Okawa'); * * $redis->zRevRangeByScore('oldest-people', 122, 119); * $redis->zRevRangeByScore('oldest-people', 'inf', 118); * $redis->zRevRangeByScore('oldest-people', '117.5', '-inf', ['LIMIT' => [0, 1]]); */ public function zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []): Redis|array|false; /** * Retrieve a member of a sorted set by reverse rank. * * @param string $key The sorted set to query. * @param mixed $member The member to look up. * * @return Redis|int|false The reverse rank (the rank if counted high to low) of the member or * false on failure. * @see https://redis.io/commands/zrevrank * * @example * $redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo'); * * $redis->zrevrank('ds9-characters', 'Sisko'); * $redis->zrevrank('ds9-characters', 'Garak'); */ public function zRevRank(string $key, mixed $member): Redis|int|false; /** * Get the score of a member of a sorted set. * * @param string $key The sorted set to query. * @param mixed $member The member we wish to query. * * @return The score of the requested element or false if it is not found. * * @see https://redis.io/commands/zscore * * @example * $redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET'); * $redis->zScore('telescopes', 'LBT'); */ public function zScore(string $key, mixed $member): Redis|float|false; /** * Given one or more sorted set key names, return every element that is in the first * set but not any of the others. * * @param array $keys One ore more sorted sets. * @param array $options An array which can contain ['WITHSCORES' => true] if you want Redis to * return members and scores. * * @return Redis|array|false An array of members or false on failure. * * @see https://redis.io/commands/zdiff * * @example * $redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five'); * $redis->zAdd('evens', 2, 'two', 4, 'four'); * $redis->zAdd('mod3', 3, 'three', 6, 'six'); * * $redis->zDiff(['primes', 'evens', 'mod3']); */ public function zdiff(array $keys, ?array $options = null): Redis|array|false; /** * Store the difference of one or more sorted sets in a destination sorted set. * * See {@link Redis::zdiff} for a more detailed description of how the diff operation works. * * @param string $key The destination set name. * @param array $keys One or more source key names * * @return Redis|int|false The number of elements stored in the destination set or false on * failure. * * @see https://redis.io/commands/zdiff * @see Redis::zdiff() */ public function zdiffstore(string $dst, array $keys): Redis|int|false; /** * Compute the intersection of one or more sorted sets and return the members * * @param array $keys One ore more sorted sets. * @param array $weights An optional array of weights to be applied to each set when performing * the intersection. * @param array $options Options for how Redis should combine duplicate elements when performing the * intersection. See Redis::zunion() for details. * * @return Redis|array|false All of the members that exist in every set. * * @see https://redis.io/commands/zinter * * @example * $redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard'); * $redis->zAdd('DS9', 2.5, 'Worf', 3.0, 'Kira', 4.0, 'Sisko'); * * $redis->zInter(['TNG', 'DS9']); * $redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true]); * $redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true, 'aggregate' => 'max']); */ public function zinter(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false; /** * Similar to ZINTER but instead of returning the intersected values, this command returns the * cardinality of the intersected set. * * @see https://redis.io/commands/zintercard * @see https://redis.io/commands/zinter * @see Redis::zinter() * * @param array $keys One ore more sorted set key names. * @param int $limit An optional upper bound on the returned cardinality. If set to a value * greater than zero, Redis will stop processing the intersection once the * resulting cardinality reaches this limit. * * @return Redis|int|false The cardinality of the intersection or false on failure. * * @example * $redis->zAdd('zs1', 1, 'one', 2, 'two', 3, 'three', 4, 'four'); * $redis->zAdd('zs2', 2, 'two', 4, 'four'); * * $redis->zInterCard(['zs1', 'zs2']); */ public function zintercard(array $keys, int $limit = -1): Redis|int|false; /** * Compute the intersection of one ore more sorted sets storing the result in a new sorted set. * * @param string $dst The destination sorted set to store the intersected values. * @param array $keys One ore more sorted set key names. * @param array $weights An optional array of floats to weight each passed input set. * @param string $aggregate An optional aggregation method to use. * * 'SUM' - Store sum of all intersected members (this is the default). * 'MIN' - Store minimum value for each intersected member. * 'MAX' - Store maximum value for each intersected member. * * @return Redis|int|false The total number of members writtern to the destination set or false on failure. * * @see https://redis.io/commands/zinterstore * @see https://redis.io/commands/zinter * * @example * $redis->zAdd('zs1', 3, 'apples', 2, 'pears'); * $redis->zAdd('zs2', 4, 'pears', 3, 'bananas'); * $redis->zAdd('zs3', 2, 'figs', 3, 'pears'); * * $redis->zInterStore('fruit-sum', ['zs1', 'zs2', 'zs3']); * $redis->zInterStore('fruit-max', ['zs1', 'zs2', 'zs3'], NULL, 'MAX'); */ public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false; /** * Scan the members of a sorted set incrementally, using a cursor * * @param string $key The sorted set to scan. * @param int $iterator A reference to an iterator that should be initialized to NULL initially, that * will be updated after each subsequent call to ZSCAN. Once the iterator * has returned to zero the scan is complete * @param string $pattern An optional glob-style pattern that limits which members are returned during * the scanning process. * @param int $count A hint for Redis that tells it how many elements it should test before returning * from the call. The higher the more work Redis may do in any one given call to * ZSCAN potentially blocking for longer periods of time. * * @return Redis|array|false An array of elements or false on failure. * * @see https://redis.io/commands/zscan * @see https://redis.io/commands/scan * @see Redis::scan() * * NOTE: See Redis::scan() for detailed example code on how to call SCAN like commands. * */ public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|false; /** * Retrieve the union of one or more sorted sets * * @param array $keys One ore more sorted set key names * @param array $weights An optional array with floating point weights used when performing the union. * Note that if this argument is passed, it must contain the same number of * elements as the $keys array. * @param array $options An array that modifies how this command functions. * * * $options = [ * # By default when members exist in more than one set Redis will SUM * # total score for each match. Instead, it can return the AVG, MIN, * # or MAX value based on this option. * 'AGGREGATE' => 'sum' | 'min' | 'max' * * # Whether Redis should also return each members aggregated score. * 'WITHSCORES' => true | false * ] * * * @return Redis|array|false The union of each sorted set or false on failure * * @example * $redis->del('store1', 'store2', 'store3'); * $redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas'); * $redis->zAdd('store2', 3, 'apples', 5, 'coconuts', 2, 'bananas'); * $redis->zAdd('store3', 2, 'bananas', 6, 'apples', 4, 'figs'); * * $redis->zUnion(['store1', 'store2', 'store3'], NULL, ['withscores' => true]); * $redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true]); * $redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true, 'aggregate' => 'MIN']); */ public function zunion(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false; /** * Perform a union on one or more Redis sets and store the result in a destination sorted set. * * @param string $dst The destination set to store the union. * @param array $keys One or more input keys on which to perform our union. * @param array $weights An optional weights array used to weight each input set. * @param string $aggregate An optional modifier in how Redis will combine duplicate members. * Valid: 'MIN', 'MAX', 'SUM'. * * @return Redis|int|false The number of members stored in the destination set or false on failure. * * @see https://redis.io/commands/zunionstore * @see Redis::zunion() * * @example * $redis->zAdd('zs1', 1, 'one', 3, 'three'); * $redis->zAdd('zs1', 2, 'two', 4, 'four'); * $redis->zadd('zs3', 1, 'one', 7, 'five'); * * $redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']); */ public function zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false; } class RedisException extends RuntimeException {} redis-6.0.2/redis_arginfo.h0000644000175000000120000030377314515245367016421 0ustar pyatsukhnenkowheel/* This is a generated file, edit the .stub.php file instead. * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__compress, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis__uncompress arginfo_class_Redis__compress ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__prefix, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__serialize, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__unserialize, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis__pack arginfo_class_Redis__serialize #define arginfo_class_Redis__unpack arginfo_class_Redis__unserialize ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_acl, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_append, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_auth, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, credentials, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bgSave, 0, 0, Redis, MAY_BE_BOOL) ZEND_END_ARG_INFO() #define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis_bgSave ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitcount, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitop, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, deskey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitpos, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, bit, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_blPop, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key_or_keys, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_LONG, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_brPop arginfo_class_Redis_blPop ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_brpoplpush, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, timeout, MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bzPopMax, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_bzPopMin arginfo_class_Redis_bzPopMax ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bzmpop, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zmpop, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1") ZEND_END_ARG_INFO() #define arginfo_class_Redis_blmpop arginfo_class_Redis_bzmpop #define arginfo_class_Redis_lmpop arginfo_class_Redis_zmpop ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_clearLastError, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_client, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, opt, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_close arginfo_class_Redis_clearLastError ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 0, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 1, "null") ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_copy, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dbSize, 0, 0, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_debug, 0, 1, Redis, MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_decr, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_decrBy, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_del, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_delete arginfo_class_Redis_del #define arginfo_class_Redis_discard arginfo_class_Redis_bgSave #define arginfo_class_Redis_dump arginfo_class_Redis_debug ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_echo, 0, 1, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_eval, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, script, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_eval_ro, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, script_sha, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_evalsha, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, sha1, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_Redis_evalsha_ro arginfo_class_Redis_evalsha ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_exec, 0, 0, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_exists, 0, 1, Redis, MAY_BE_LONG|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expire, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expireAt, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_failover, 0, 0, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, to, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, abort, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expiretime, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_pexpiretime arginfo_class_Redis_expiretime ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_fcall, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, fn, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, keys, IS_ARRAY, 0, "[]") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() #define arginfo_class_Redis_fcall_ro arginfo_class_Redis_fcall ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_flushAll, 0, 0, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sync, _IS_BOOL, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_function, 0, 1, Redis, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geoadd, 0, 4, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples_and_options, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geodist, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geohash, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_geopos arginfo_class_Redis_geohash ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_georadius, 0, 5, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() #define arginfo_class_Redis_georadius_ro arginfo_class_Redis_georadius ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_georadiusbymember, 0, 4, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() #define arginfo_class_Redis_georadiusbymember_ro arginfo_class_Redis_georadiusbymember ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearch, 0, 4, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geosearchstore, 0, 5, Redis, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_get, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getAuth, 0, 0, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getBit, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getEx, 0, 1, Redis, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getDBNum, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getDel, 0, 1, Redis, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getHost, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getLastError, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() #define arginfo_class_Redis_getMode arginfo_class_Redis_getDBNum ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getOption, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_getPersistentID arginfo_class_Redis_getLastError #define arginfo_class_Redis_getPort arginfo_class_Redis_getDBNum ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getRange, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getReadTimeout, 0, 0, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getset, 0, 2, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getTimeout, 0, 0, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getTransferredBytes, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_clearTransferredBytes, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hDel, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_fields, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hExists, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGet, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hGetAll, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrBy, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrByFloat, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_hKeys arginfo_class_Redis_hGetAll #define arginfo_class_Redis_hLen arginfo_class_Redis_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMget, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMset, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, fieldvals, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSet, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSetNx, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hStrLen, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_hVals arginfo_class_Redis_hGetAll ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_Redis_incr arginfo_class_Redis_decr #define arginfo_class_Redis_incrBy arginfo_class_Redis_decrBy ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_incrByFloat, 0, 2, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_info, 0, 0, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_VARIADIC_TYPE_INFO(0, sections, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_isConnected arginfo_class_Redis_clearLastError ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, pos, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, pivot, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_lLen arginfo_class_Redis_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lMove, 0, 4, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_blmove, 0, 5, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPop, 0, 1, Redis, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPush, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, elements, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_rPush arginfo_class_Redis_lPush #define arginfo_class_Redis_lPushx arginfo_class_Redis_append #define arginfo_class_Redis_rPushx arginfo_class_Redis_append ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lSet, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_lastSave arginfo_class_Redis_getDBNum ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lindex, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lrange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lrem, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ltrim, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mget, 0, 1, Redis, MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_migrate, 0, 5, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, dstdb, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, copy, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, replace, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_move, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mset, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_msetnx arginfo_class_Redis_mset ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_multi, 0, 0, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_object, 0, 2, Redis, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_open, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_pconnect arginfo_class_Redis_open ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_persist, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpire, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfadd, 0, 2, Redis, MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, elements, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfcount, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key_or_keys, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfmerge, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, srckeys, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ping, 0, 0, Redis, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL) ZEND_END_ARG_INFO() #define arginfo_class_Redis_popen arginfo_class_Redis_open ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_psetex, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, expire, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_psubscribe, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_pttl arginfo_class_Redis_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_publish, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pubsub, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_punsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rPop, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_randomKey, 0, 0, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rawcommand, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rename, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, old_name, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, new_name, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_renameNx, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key_src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key_dst, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_reset arginfo_class_Redis_bgSave ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_restore, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_role arginfo_class_Redis_getAuth ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rpoplpush, 0, 2, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dstkey, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sAdd, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sAddArray, 0, 2, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sDiff, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sDiffStore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sInter, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sintercard, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() #define arginfo_class_Redis_sInterStore arginfo_class_Redis_del #define arginfo_class_Redis_sMembers arginfo_class_Redis_hGetAll #define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sMove, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sPop, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_Redis_sRandMember arginfo_class_Redis_sPop #define arginfo_class_Redis_sUnion arginfo_class_Redis_sDiff #define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore #define arginfo_class_Redis_save arginfo_class_Redis_bgSave ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_scard arginfo_class_Redis_expiretime #define arginfo_class_Redis_script arginfo_class_Redis_rawcommand ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_select, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, db, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_set, 0, 2, Redis, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setBit, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setRange, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_setOption, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setex, 0, 0, 3) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, expire, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setnx, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_sismember arginfo_class_Redis_setnx ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_slaveof, 0, 0, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_END_ARG_INFO() #define arginfo_class_Redis_replicaof arginfo_class_Redis_slaveof ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_touch, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key_or_array, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, more_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slowlog, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sort, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_sort_ro arginfo_class_Redis_sort ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sortAsc, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, get, IS_MIXED, 0, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, store, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_sortAscAlpha arginfo_class_Redis_sortAsc #define arginfo_class_Redis_sortDesc arginfo_class_Redis_sortAsc #define arginfo_class_Redis_sortDescAlpha arginfo_class_Redis_sortAsc #define arginfo_class_Redis_srem arginfo_class_Redis_sAdd ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sscan, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_ssubscribe, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_strlen arginfo_class_Redis_expiretime #define arginfo_class_Redis_subscribe arginfo_class_Redis_ssubscribe ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sunsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_swapdb, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_time, 0, 0, Redis, MAY_BE_ARRAY) ZEND_END_ARG_INFO() #define arginfo_class_Redis_ttl arginfo_class_Redis_expiretime #define arginfo_class_Redis_type arginfo_class_Redis_expiretime #define arginfo_class_Redis_unlink arginfo_class_Redis_del #define arginfo_class_Redis_unsubscribe arginfo_class_Redis_sunsubscribe #define arginfo_class_Redis_unwatch arginfo_class_Redis_bgSave ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_watch, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_wait, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, numreplicas, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xack, 0, 3, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xadd, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, maxlen, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nomkstream, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xautoclaim, 0, 5, Redis, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min_idle, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, justid, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min_idle, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xdel, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xinfo, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() #define arginfo_class_Redis_xlen arginfo_class_Redis_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xpending, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xrange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xread, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xreadgroup, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xrevrange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xtrim, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, threshold, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, minid, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zAdd, 0, 2, Redis, MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, score_or_options, MAY_BE_ARRAY|MAY_BE_DOUBLE, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, more_scores_and_mems, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zCard arginfo_class_Redis_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zCount, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zIncrBy, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zLexCount, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zMscore, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zPopMax, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, start, MAY_BE_STRING|MAY_BE_LONG, NULL) ZEND_ARG_TYPE_MASK(0, end, MAY_BE_STRING|MAY_BE_LONG, NULL) ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByLex, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByScore, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zrangestore, 0, 4, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dstkey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRank, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRem, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRemRangeByLex arginfo_class_Redis_zLexCount ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRemRangeByRank, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_zCount ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scores, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRangeByLex, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRangeByScore, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL, "[]") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRevRank arginfo_class_Redis_zRank ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zScore, 0, 2, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiff, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiffstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zinter, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zintercard arginfo_class_Redis_sintercard ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zinterstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zunion arginfo_class_Redis_zinter ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zunionstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_METHOD(Redis, __construct); ZEND_METHOD(Redis, __destruct); ZEND_METHOD(Redis, _compress); ZEND_METHOD(Redis, _uncompress); ZEND_METHOD(Redis, _prefix); ZEND_METHOD(Redis, _serialize); ZEND_METHOD(Redis, _unserialize); ZEND_METHOD(Redis, _pack); ZEND_METHOD(Redis, _unpack); ZEND_METHOD(Redis, acl); ZEND_METHOD(Redis, append); ZEND_METHOD(Redis, auth); ZEND_METHOD(Redis, bgSave); ZEND_METHOD(Redis, bgrewriteaof); ZEND_METHOD(Redis, bitcount); ZEND_METHOD(Redis, bitop); ZEND_METHOD(Redis, bitpos); ZEND_METHOD(Redis, blPop); ZEND_METHOD(Redis, brPop); ZEND_METHOD(Redis, brpoplpush); ZEND_METHOD(Redis, bzPopMax); ZEND_METHOD(Redis, bzPopMin); ZEND_METHOD(Redis, bzmpop); ZEND_METHOD(Redis, zmpop); ZEND_METHOD(Redis, blmpop); ZEND_METHOD(Redis, lmpop); ZEND_METHOD(Redis, clearLastError); ZEND_METHOD(Redis, client); ZEND_METHOD(Redis, close); ZEND_METHOD(Redis, command); ZEND_METHOD(Redis, config); ZEND_METHOD(Redis, connect); ZEND_METHOD(Redis, copy); ZEND_METHOD(Redis, dbSize); ZEND_METHOD(Redis, debug); ZEND_METHOD(Redis, decr); ZEND_METHOD(Redis, decrBy); ZEND_METHOD(Redis, del); ZEND_METHOD(Redis, discard); ZEND_METHOD(Redis, dump); ZEND_METHOD(Redis, echo); ZEND_METHOD(Redis, eval); ZEND_METHOD(Redis, eval_ro); ZEND_METHOD(Redis, evalsha); ZEND_METHOD(Redis, evalsha_ro); ZEND_METHOD(Redis, exec); ZEND_METHOD(Redis, exists); ZEND_METHOD(Redis, expire); ZEND_METHOD(Redis, expireAt); ZEND_METHOD(Redis, failover); ZEND_METHOD(Redis, expiretime); ZEND_METHOD(Redis, pexpiretime); ZEND_METHOD(Redis, fcall); ZEND_METHOD(Redis, fcall_ro); ZEND_METHOD(Redis, flushAll); ZEND_METHOD(Redis, flushDB); ZEND_METHOD(Redis, function); ZEND_METHOD(Redis, geoadd); ZEND_METHOD(Redis, geodist); ZEND_METHOD(Redis, geohash); ZEND_METHOD(Redis, geopos); ZEND_METHOD(Redis, georadius); ZEND_METHOD(Redis, georadius_ro); ZEND_METHOD(Redis, georadiusbymember); ZEND_METHOD(Redis, georadiusbymember_ro); ZEND_METHOD(Redis, geosearch); ZEND_METHOD(Redis, geosearchstore); ZEND_METHOD(Redis, get); ZEND_METHOD(Redis, getAuth); ZEND_METHOD(Redis, getBit); ZEND_METHOD(Redis, getEx); ZEND_METHOD(Redis, getDBNum); ZEND_METHOD(Redis, getDel); ZEND_METHOD(Redis, getHost); ZEND_METHOD(Redis, getLastError); ZEND_METHOD(Redis, getMode); ZEND_METHOD(Redis, getOption); ZEND_METHOD(Redis, getPersistentID); ZEND_METHOD(Redis, getPort); ZEND_METHOD(Redis, getRange); ZEND_METHOD(Redis, lcs); ZEND_METHOD(Redis, getReadTimeout); ZEND_METHOD(Redis, getset); ZEND_METHOD(Redis, getTimeout); ZEND_METHOD(Redis, getTransferredBytes); ZEND_METHOD(Redis, clearTransferredBytes); ZEND_METHOD(Redis, hDel); ZEND_METHOD(Redis, hExists); ZEND_METHOD(Redis, hGet); ZEND_METHOD(Redis, hGetAll); ZEND_METHOD(Redis, hIncrBy); ZEND_METHOD(Redis, hIncrByFloat); ZEND_METHOD(Redis, hKeys); ZEND_METHOD(Redis, hLen); ZEND_METHOD(Redis, hMget); ZEND_METHOD(Redis, hMset); ZEND_METHOD(Redis, hRandField); ZEND_METHOD(Redis, hSet); ZEND_METHOD(Redis, hSetNx); ZEND_METHOD(Redis, hStrLen); ZEND_METHOD(Redis, hVals); ZEND_METHOD(Redis, hscan); ZEND_METHOD(Redis, incr); ZEND_METHOD(Redis, incrBy); ZEND_METHOD(Redis, incrByFloat); ZEND_METHOD(Redis, info); ZEND_METHOD(Redis, isConnected); ZEND_METHOD(Redis, keys); ZEND_METHOD(Redis, lInsert); ZEND_METHOD(Redis, lLen); ZEND_METHOD(Redis, lMove); ZEND_METHOD(Redis, blmove); ZEND_METHOD(Redis, lPop); ZEND_METHOD(Redis, lPos); ZEND_METHOD(Redis, lPush); ZEND_METHOD(Redis, rPush); ZEND_METHOD(Redis, lPushx); ZEND_METHOD(Redis, rPushx); ZEND_METHOD(Redis, lSet); ZEND_METHOD(Redis, lastSave); ZEND_METHOD(Redis, lindex); ZEND_METHOD(Redis, lrange); ZEND_METHOD(Redis, lrem); ZEND_METHOD(Redis, ltrim); ZEND_METHOD(Redis, mget); ZEND_METHOD(Redis, migrate); ZEND_METHOD(Redis, move); ZEND_METHOD(Redis, mset); ZEND_METHOD(Redis, msetnx); ZEND_METHOD(Redis, multi); ZEND_METHOD(Redis, object); ZEND_METHOD(Redis, pconnect); ZEND_METHOD(Redis, persist); ZEND_METHOD(Redis, pexpire); ZEND_METHOD(Redis, pexpireAt); ZEND_METHOD(Redis, pfadd); ZEND_METHOD(Redis, pfcount); ZEND_METHOD(Redis, pfmerge); ZEND_METHOD(Redis, ping); ZEND_METHOD(Redis, pipeline); ZEND_METHOD(Redis, psetex); ZEND_METHOD(Redis, psubscribe); ZEND_METHOD(Redis, pttl); ZEND_METHOD(Redis, publish); ZEND_METHOD(Redis, pubsub); ZEND_METHOD(Redis, punsubscribe); ZEND_METHOD(Redis, rPop); ZEND_METHOD(Redis, randomKey); ZEND_METHOD(Redis, rawcommand); ZEND_METHOD(Redis, rename); ZEND_METHOD(Redis, renameNx); ZEND_METHOD(Redis, reset); ZEND_METHOD(Redis, restore); ZEND_METHOD(Redis, role); ZEND_METHOD(Redis, rpoplpush); ZEND_METHOD(Redis, sAdd); ZEND_METHOD(Redis, sAddArray); ZEND_METHOD(Redis, sDiff); ZEND_METHOD(Redis, sDiffStore); ZEND_METHOD(Redis, sInter); ZEND_METHOD(Redis, sintercard); ZEND_METHOD(Redis, sInterStore); ZEND_METHOD(Redis, sMembers); ZEND_METHOD(Redis, sMisMember); ZEND_METHOD(Redis, sMove); ZEND_METHOD(Redis, sPop); ZEND_METHOD(Redis, sRandMember); ZEND_METHOD(Redis, sUnion); ZEND_METHOD(Redis, sUnionStore); ZEND_METHOD(Redis, save); ZEND_METHOD(Redis, scan); ZEND_METHOD(Redis, scard); ZEND_METHOD(Redis, script); ZEND_METHOD(Redis, select); ZEND_METHOD(Redis, set); ZEND_METHOD(Redis, setBit); ZEND_METHOD(Redis, setRange); ZEND_METHOD(Redis, setOption); ZEND_METHOD(Redis, setex); ZEND_METHOD(Redis, setnx); ZEND_METHOD(Redis, sismember); ZEND_METHOD(Redis, slaveof); ZEND_METHOD(Redis, replicaof); ZEND_METHOD(Redis, touch); ZEND_METHOD(Redis, slowlog); ZEND_METHOD(Redis, sort); ZEND_METHOD(Redis, sort_ro); ZEND_METHOD(Redis, sortAsc); ZEND_METHOD(Redis, sortAscAlpha); ZEND_METHOD(Redis, sortDesc); ZEND_METHOD(Redis, sortDescAlpha); ZEND_METHOD(Redis, srem); ZEND_METHOD(Redis, sscan); ZEND_METHOD(Redis, ssubscribe); ZEND_METHOD(Redis, strlen); ZEND_METHOD(Redis, subscribe); ZEND_METHOD(Redis, sunsubscribe); ZEND_METHOD(Redis, swapdb); ZEND_METHOD(Redis, time); ZEND_METHOD(Redis, ttl); ZEND_METHOD(Redis, type); ZEND_METHOD(Redis, unlink); ZEND_METHOD(Redis, unsubscribe); ZEND_METHOD(Redis, unwatch); ZEND_METHOD(Redis, watch); ZEND_METHOD(Redis, wait); ZEND_METHOD(Redis, xack); ZEND_METHOD(Redis, xadd); ZEND_METHOD(Redis, xautoclaim); ZEND_METHOD(Redis, xclaim); ZEND_METHOD(Redis, xdel); ZEND_METHOD(Redis, xgroup); ZEND_METHOD(Redis, xinfo); ZEND_METHOD(Redis, xlen); ZEND_METHOD(Redis, xpending); ZEND_METHOD(Redis, xrange); ZEND_METHOD(Redis, xread); ZEND_METHOD(Redis, xreadgroup); ZEND_METHOD(Redis, xrevrange); ZEND_METHOD(Redis, xtrim); ZEND_METHOD(Redis, zAdd); ZEND_METHOD(Redis, zCard); ZEND_METHOD(Redis, zCount); ZEND_METHOD(Redis, zIncrBy); ZEND_METHOD(Redis, zLexCount); ZEND_METHOD(Redis, zMscore); ZEND_METHOD(Redis, zPopMax); ZEND_METHOD(Redis, zPopMin); ZEND_METHOD(Redis, zRange); ZEND_METHOD(Redis, zRangeByLex); ZEND_METHOD(Redis, zRangeByScore); ZEND_METHOD(Redis, zrangestore); ZEND_METHOD(Redis, zRandMember); ZEND_METHOD(Redis, zRank); ZEND_METHOD(Redis, zRem); ZEND_METHOD(Redis, zRemRangeByLex); ZEND_METHOD(Redis, zRemRangeByRank); ZEND_METHOD(Redis, zRemRangeByScore); ZEND_METHOD(Redis, zRevRange); ZEND_METHOD(Redis, zRevRangeByLex); ZEND_METHOD(Redis, zRevRangeByScore); ZEND_METHOD(Redis, zRevRank); ZEND_METHOD(Redis, zScore); ZEND_METHOD(Redis, zdiff); ZEND_METHOD(Redis, zdiffstore); ZEND_METHOD(Redis, zinter); ZEND_METHOD(Redis, zintercard); ZEND_METHOD(Redis, zinterstore); ZEND_METHOD(Redis, zscan); ZEND_METHOD(Redis, zunion); ZEND_METHOD(Redis, zunionstore); static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC) ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _prefix, arginfo_class_Redis__prefix, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC) ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC) ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC) ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgSave, arginfo_class_Redis_bgSave, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgrewriteaof, arginfo_class_Redis_bgrewriteaof, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC) ZEND_ME(Redis, blPop, arginfo_class_Redis_blPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, brPop, arginfo_class_Redis_brPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, brpoplpush, arginfo_class_Redis_brpoplpush, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bzPopMax, arginfo_class_Redis_bzPopMax, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bzPopMin, arginfo_class_Redis_bzPopMin, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bzmpop, arginfo_class_Redis_bzmpop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zmpop, arginfo_class_Redis_zmpop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, blmpop, arginfo_class_Redis_blmpop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lmpop, arginfo_class_Redis_lmpop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, clearLastError, arginfo_class_Redis_clearLastError, ZEND_ACC_PUBLIC) ZEND_ME(Redis, client, arginfo_class_Redis_client, ZEND_ACC_PUBLIC) ZEND_ME(Redis, close, arginfo_class_Redis_close, ZEND_ACC_PUBLIC) ZEND_ME(Redis, command, arginfo_class_Redis_command, ZEND_ACC_PUBLIC) ZEND_ME(Redis, config, arginfo_class_Redis_config, ZEND_ACC_PUBLIC) ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC) ZEND_ME(Redis, copy, arginfo_class_Redis_copy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, dbSize, arginfo_class_Redis_dbSize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, debug, arginfo_class_Redis_debug, ZEND_ACC_PUBLIC) ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC) ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC) ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC) ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC) ZEND_ME(Redis, eval, arginfo_class_Redis_eval, ZEND_ACC_PUBLIC) ZEND_ME(Redis, eval_ro, arginfo_class_Redis_eval_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, evalsha, arginfo_class_Redis_evalsha, ZEND_ACC_PUBLIC) ZEND_ME(Redis, evalsha_ro, arginfo_class_Redis_evalsha_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, exec, arginfo_class_Redis_exec, ZEND_ACC_PUBLIC) ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC) ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expiretime, arginfo_class_Redis_expiretime, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC) ZEND_ME(Redis, fcall, arginfo_class_Redis_fcall, ZEND_ACC_PUBLIC) ZEND_ME(Redis, fcall_ro, arginfo_class_Redis_fcall_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC) ZEND_ME(Redis, function, arginfo_class_Redis_function, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geodist, arginfo_class_Redis_geodist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geohash, arginfo_class_Redis_geohash, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geopos, arginfo_class_Redis_geopos, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadius, arginfo_class_Redis_georadius, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadius_ro, arginfo_class_Redis_georadius_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadiusbymember, arginfo_class_Redis_georadiusbymember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadiusbymember_ro, arginfo_class_Redis_georadiusbymember_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getEx, arginfo_class_Redis_getEx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getDBNum, arginfo_class_Redis_getDBNum, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getDel, arginfo_class_Redis_getDel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getHost, arginfo_class_Redis_getHost, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getLastError, arginfo_class_Redis_getLastError, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getMode, arginfo_class_Redis_getMode, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getTransferredBytes, arginfo_class_Redis_getTransferredBytes, ZEND_ACC_PUBLIC) ZEND_ME(Redis, clearTransferredBytes, arginfo_class_Redis_clearTransferredBytes, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hDel, arginfo_class_Redis_hDel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGetAll, arginfo_class_Redis_hGetAll, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hIncrBy, arginfo_class_Redis_hIncrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hIncrByFloat, arginfo_class_Redis_hIncrByFloat, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hKeys, arginfo_class_Redis_hKeys, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hLen, arginfo_class_Redis_hLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hMget, arginfo_class_Redis_hMget, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hMset, arginfo_class_Redis_hMset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hRandField, arginfo_class_Redis_hRandField, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hSet, arginfo_class_Redis_hSet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC) ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC) ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC) ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC) ZEND_ME(Redis, blmove, arginfo_class_Redis_blmove, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPos, arginfo_class_Redis_lPos, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lSet, arginfo_class_Redis_lSet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lastSave, arginfo_class_Redis_lastSave, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lindex, arginfo_class_Redis_lindex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lrange, arginfo_class_Redis_lrange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lrem, arginfo_class_Redis_lrem, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ltrim, arginfo_class_Redis_ltrim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC) ZEND_ME(Redis, migrate, arginfo_class_Redis_migrate, ZEND_ACC_PUBLIC) ZEND_ME(Redis, move, arginfo_class_Redis_move, ZEND_ACC_PUBLIC) ZEND_ME(Redis, mset, arginfo_class_Redis_mset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, msetnx, arginfo_class_Redis_msetnx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC) ZEND_ME(Redis, object, arginfo_class_Redis_object, ZEND_ACC_PUBLIC) ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC) ZEND_ME(Redis, persist, arginfo_class_Redis_persist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpire, arginfo_class_Redis_pexpire, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpireAt, arginfo_class_Redis_pexpireAt, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pfadd, arginfo_class_Redis_pfadd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pfcount, arginfo_class_Redis_pfcount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pfmerge, arginfo_class_Redis_pfmerge, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC) ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, psubscribe, arginfo_class_Redis_psubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pttl, arginfo_class_Redis_pttl, ZEND_ACC_PUBLIC) ZEND_ME(Redis, publish, arginfo_class_Redis_publish, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pubsub, arginfo_class_Redis_pubsub, ZEND_ACC_PUBLIC) ZEND_ME(Redis, punsubscribe, arginfo_class_Redis_punsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, randomKey, arginfo_class_Redis_randomKey, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rawcommand, arginfo_class_Redis_rawcommand, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC) ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, reset, arginfo_class_Redis_reset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, restore, arginfo_class_Redis_restore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, role, arginfo_class_Redis_role, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rpoplpush, arginfo_class_Redis_rpoplpush, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sAdd, arginfo_class_Redis_sAdd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sAddArray, arginfo_class_Redis_sAddArray, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sDiff, arginfo_class_Redis_sDiff, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sDiffStore, arginfo_class_Redis_sDiffStore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sInter, arginfo_class_Redis_sInter, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sintercard, arginfo_class_Redis_sintercard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sInterStore, arginfo_class_Redis_sInterStore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sMembers, arginfo_class_Redis_sMembers, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sMisMember, arginfo_class_Redis_sMisMember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sMove, arginfo_class_Redis_sMove, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sPop, arginfo_class_Redis_sPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sRandMember, arginfo_class_Redis_sRandMember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sUnion, arginfo_class_Redis_sUnion, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sUnionStore, arginfo_class_Redis_sUnionStore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, save, arginfo_class_Redis_save, ZEND_ACC_PUBLIC) ZEND_ME(Redis, scan, arginfo_class_Redis_scan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, scard, arginfo_class_Redis_scard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, script, arginfo_class_Redis_script, ZEND_ACC_PUBLIC) ZEND_ME(Redis, select, arginfo_class_Redis_select, ZEND_ACC_PUBLIC) ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setOption, arginfo_class_Redis_setOption, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sismember, arginfo_class_Redis_sismember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, replicaof, arginfo_class_Redis_replicaof, ZEND_ACC_PUBLIC) ZEND_ME(Redis, touch, arginfo_class_Redis_touch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sort_ro, arginfo_class_Redis_sort_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sortAsc, arginfo_class_Redis_sortAsc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, sortAscAlpha, arginfo_class_Redis_sortAscAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, sortDesc, arginfo_class_Redis_sortDesc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, sortDescAlpha, arginfo_class_Redis_sortDescAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, srem, arginfo_class_Redis_srem, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sscan, arginfo_class_Redis_sscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ssubscribe, arginfo_class_Redis_ssubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, subscribe, arginfo_class_Redis_subscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sunsubscribe, arginfo_class_Redis_sunsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, swapdb, arginfo_class_Redis_swapdb, ZEND_ACC_PUBLIC) ZEND_ME(Redis, time, arginfo_class_Redis_time, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ttl, arginfo_class_Redis_ttl, ZEND_ACC_PUBLIC) ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC) ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC) ZEND_ME(Redis, unsubscribe, arginfo_class_Redis_unsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, wait, arginfo_class_Redis_wait, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xack, arginfo_class_Redis_xack, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xadd, arginfo_class_Redis_xadd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xautoclaim, arginfo_class_Redis_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xclaim, arginfo_class_Redis_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xdel, arginfo_class_Redis_xdel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xgroup, arginfo_class_Redis_xgroup, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xinfo, arginfo_class_Redis_xinfo, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xlen, arginfo_class_Redis_xlen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xpending, arginfo_class_Redis_xpending, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xrange, arginfo_class_Redis_xrange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xread, arginfo_class_Redis_xread, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xreadgroup, arginfo_class_Redis_xreadgroup, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xrevrange, arginfo_class_Redis_xrevrange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xtrim, arginfo_class_Redis_xtrim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zAdd, arginfo_class_Redis_zAdd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zCard, arginfo_class_Redis_zCard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zCount, arginfo_class_Redis_zCount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zIncrBy, arginfo_class_Redis_zIncrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zLexCount, arginfo_class_Redis_zLexCount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zMscore, arginfo_class_Redis_zMscore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zPopMax, arginfo_class_Redis_zPopMax, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zPopMin, arginfo_class_Redis_zPopMin, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zrangestore, arginfo_class_Redis_zrangestore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRandMember, arginfo_class_Redis_zRandMember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRemRangeByLex, arginfo_class_Redis_zRemRangeByLex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRemRangeByRank, arginfo_class_Redis_zRemRangeByRank, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRemRangeByScore, arginfo_class_Redis_zRemRangeByScore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRevRange, arginfo_class_Redis_zRevRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRevRangeByLex, arginfo_class_Redis_zRevRangeByLex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRevRangeByScore, arginfo_class_Redis_zRevRangeByScore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRevRank, arginfo_class_Redis_zRevRank, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zScore, arginfo_class_Redis_zScore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zdiff, arginfo_class_Redis_zdiff, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zdiffstore, arginfo_class_Redis_zdiffstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zinter, arginfo_class_Redis_zinter, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zintercard, arginfo_class_Redis_zintercard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zinterstore, arginfo_class_Redis_zinterstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zscan, arginfo_class_Redis_zscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zunion, arginfo_class_Redis_zunion, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zunionstore, arginfo_class_Redis_zunionstore, ZEND_ACC_PUBLIC) ZEND_FE_END }; static const zend_function_entry class_RedisException_methods[] = { ZEND_FE_END }; static zend_class_entry *register_class_Redis(void) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); zval const_REDIS_NOT_FOUND_value; ZVAL_LONG(&const_REDIS_NOT_FOUND_value, REDIS_NOT_FOUND); zend_string *const_REDIS_NOT_FOUND_name = zend_string_init_interned("REDIS_NOT_FOUND", sizeof("REDIS_NOT_FOUND") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_NOT_FOUND_name, &const_REDIS_NOT_FOUND_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_NOT_FOUND_name); zval const_REDIS_STRING_value; ZVAL_LONG(&const_REDIS_STRING_value, REDIS_STRING); zend_string *const_REDIS_STRING_name = zend_string_init_interned("REDIS_STRING", sizeof("REDIS_STRING") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_STRING_name, &const_REDIS_STRING_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_STRING_name); zval const_REDIS_SET_value; ZVAL_LONG(&const_REDIS_SET_value, REDIS_SET); zend_string *const_REDIS_SET_name = zend_string_init_interned("REDIS_SET", sizeof("REDIS_SET") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_SET_name, &const_REDIS_SET_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_SET_name); zval const_REDIS_LIST_value; ZVAL_LONG(&const_REDIS_LIST_value, REDIS_LIST); zend_string *const_REDIS_LIST_name = zend_string_init_interned("REDIS_LIST", sizeof("REDIS_LIST") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_LIST_name, &const_REDIS_LIST_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_LIST_name); zval const_REDIS_ZSET_value; ZVAL_LONG(&const_REDIS_ZSET_value, REDIS_ZSET); zend_string *const_REDIS_ZSET_name = zend_string_init_interned("REDIS_ZSET", sizeof("REDIS_ZSET") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_ZSET_name, &const_REDIS_ZSET_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_ZSET_name); zval const_REDIS_HASH_value; ZVAL_LONG(&const_REDIS_HASH_value, REDIS_HASH); zend_string *const_REDIS_HASH_name = zend_string_init_interned("REDIS_HASH", sizeof("REDIS_HASH") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_HASH_name, &const_REDIS_HASH_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_HASH_name); zval const_REDIS_STREAM_value; ZVAL_LONG(&const_REDIS_STREAM_value, REDIS_STREAM); zend_string *const_REDIS_STREAM_name = zend_string_init_interned("REDIS_STREAM", sizeof("REDIS_STREAM") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_STREAM_name, &const_REDIS_STREAM_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_STREAM_name); zval const_ATOMIC_value; ZVAL_LONG(&const_ATOMIC_value, ATOMIC); zend_string *const_ATOMIC_name = zend_string_init_interned("ATOMIC", sizeof("ATOMIC") - 1, 1); zend_declare_class_constant_ex(class_entry, const_ATOMIC_name, &const_ATOMIC_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_ATOMIC_name); zval const_MULTI_value; ZVAL_LONG(&const_MULTI_value, MULTI); zend_string *const_MULTI_name = zend_string_init_interned("MULTI", sizeof("MULTI") - 1, 1); zend_declare_class_constant_ex(class_entry, const_MULTI_name, &const_MULTI_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_MULTI_name); zval const_PIPELINE_value; ZVAL_LONG(&const_PIPELINE_value, PIPELINE); zend_string *const_PIPELINE_name = zend_string_init_interned("PIPELINE", sizeof("PIPELINE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_PIPELINE_name, &const_PIPELINE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_PIPELINE_name); zval const_OPT_SERIALIZER_value; ZVAL_LONG(&const_OPT_SERIALIZER_value, REDIS_OPT_SERIALIZER); zend_string *const_OPT_SERIALIZER_name = zend_string_init_interned("OPT_SERIALIZER", sizeof("OPT_SERIALIZER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_SERIALIZER_name, &const_OPT_SERIALIZER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_SERIALIZER_name); zval const_OPT_PREFIX_value; ZVAL_LONG(&const_OPT_PREFIX_value, REDIS_OPT_PREFIX); zend_string *const_OPT_PREFIX_name = zend_string_init_interned("OPT_PREFIX", sizeof("OPT_PREFIX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_PREFIX_name, &const_OPT_PREFIX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_PREFIX_name); zval const_OPT_READ_TIMEOUT_value; ZVAL_LONG(&const_OPT_READ_TIMEOUT_value, REDIS_OPT_READ_TIMEOUT); zend_string *const_OPT_READ_TIMEOUT_name = zend_string_init_interned("OPT_READ_TIMEOUT", sizeof("OPT_READ_TIMEOUT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_READ_TIMEOUT_name, &const_OPT_READ_TIMEOUT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_READ_TIMEOUT_name); zval const_OPT_TCP_KEEPALIVE_value; ZVAL_LONG(&const_OPT_TCP_KEEPALIVE_value, REDIS_OPT_TCP_KEEPALIVE); zend_string *const_OPT_TCP_KEEPALIVE_name = zend_string_init_interned("OPT_TCP_KEEPALIVE", sizeof("OPT_TCP_KEEPALIVE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_TCP_KEEPALIVE_name, &const_OPT_TCP_KEEPALIVE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_TCP_KEEPALIVE_name); zval const_OPT_COMPRESSION_value; ZVAL_LONG(&const_OPT_COMPRESSION_value, REDIS_OPT_COMPRESSION); zend_string *const_OPT_COMPRESSION_name = zend_string_init_interned("OPT_COMPRESSION", sizeof("OPT_COMPRESSION") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_COMPRESSION_name, &const_OPT_COMPRESSION_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_COMPRESSION_name); zval const_OPT_REPLY_LITERAL_value; ZVAL_LONG(&const_OPT_REPLY_LITERAL_value, REDIS_OPT_REPLY_LITERAL); zend_string *const_OPT_REPLY_LITERAL_name = zend_string_init_interned("OPT_REPLY_LITERAL", sizeof("OPT_REPLY_LITERAL") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_REPLY_LITERAL_name, &const_OPT_REPLY_LITERAL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_REPLY_LITERAL_name); zval const_OPT_COMPRESSION_LEVEL_value; ZVAL_LONG(&const_OPT_COMPRESSION_LEVEL_value, REDIS_OPT_COMPRESSION_LEVEL); zend_string *const_OPT_COMPRESSION_LEVEL_name = zend_string_init_interned("OPT_COMPRESSION_LEVEL", sizeof("OPT_COMPRESSION_LEVEL") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_COMPRESSION_LEVEL_name, &const_OPT_COMPRESSION_LEVEL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_COMPRESSION_LEVEL_name); zval const_OPT_NULL_MULTIBULK_AS_NULL_value; ZVAL_LONG(&const_OPT_NULL_MULTIBULK_AS_NULL_value, REDIS_OPT_NULL_MBULK_AS_NULL); zend_string *const_OPT_NULL_MULTIBULK_AS_NULL_name = zend_string_init_interned("OPT_NULL_MULTIBULK_AS_NULL", sizeof("OPT_NULL_MULTIBULK_AS_NULL") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_NULL_MULTIBULK_AS_NULL_name, &const_OPT_NULL_MULTIBULK_AS_NULL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_NULL_MULTIBULK_AS_NULL_name); zval const_SERIALIZER_NONE_value; ZVAL_LONG(&const_SERIALIZER_NONE_value, REDIS_SERIALIZER_NONE); zend_string *const_SERIALIZER_NONE_name = zend_string_init_interned("SERIALIZER_NONE", sizeof("SERIALIZER_NONE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_NONE_name, &const_SERIALIZER_NONE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_NONE_name); zval const_SERIALIZER_PHP_value; ZVAL_LONG(&const_SERIALIZER_PHP_value, REDIS_SERIALIZER_PHP); zend_string *const_SERIALIZER_PHP_name = zend_string_init_interned("SERIALIZER_PHP", sizeof("SERIALIZER_PHP") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_PHP_name, &const_SERIALIZER_PHP_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_PHP_name); #if defined(HAVE_REDIS_IGBINARY) zval const_SERIALIZER_IGBINARY_value; ZVAL_LONG(&const_SERIALIZER_IGBINARY_value, REDIS_SERIALIZER_IGBINARY); zend_string *const_SERIALIZER_IGBINARY_name = zend_string_init_interned("SERIALIZER_IGBINARY", sizeof("SERIALIZER_IGBINARY") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_IGBINARY_name, &const_SERIALIZER_IGBINARY_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_IGBINARY_name); #endif #if defined(HAVE_REDIS_MSGPACK) zval const_SERIALIZER_MSGPACK_value; ZVAL_LONG(&const_SERIALIZER_MSGPACK_value, REDIS_SERIALIZER_MSGPACK); zend_string *const_SERIALIZER_MSGPACK_name = zend_string_init_interned("SERIALIZER_MSGPACK", sizeof("SERIALIZER_MSGPACK") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_MSGPACK_name, &const_SERIALIZER_MSGPACK_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_MSGPACK_name); #endif zval const_SERIALIZER_JSON_value; ZVAL_LONG(&const_SERIALIZER_JSON_value, REDIS_SERIALIZER_JSON); zend_string *const_SERIALIZER_JSON_name = zend_string_init_interned("SERIALIZER_JSON", sizeof("SERIALIZER_JSON") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_JSON_name, &const_SERIALIZER_JSON_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_JSON_name); zval const_COMPRESSION_NONE_value; ZVAL_LONG(&const_COMPRESSION_NONE_value, REDIS_COMPRESSION_NONE); zend_string *const_COMPRESSION_NONE_name = zend_string_init_interned("COMPRESSION_NONE", sizeof("COMPRESSION_NONE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_NONE_name, &const_COMPRESSION_NONE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_NONE_name); #if defined(HAVE_REDIS_LZF) zval const_COMPRESSION_LZF_value; ZVAL_LONG(&const_COMPRESSION_LZF_value, REDIS_COMPRESSION_LZF); zend_string *const_COMPRESSION_LZF_name = zend_string_init_interned("COMPRESSION_LZF", sizeof("COMPRESSION_LZF") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_LZF_name, &const_COMPRESSION_LZF_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_LZF_name); #endif #if defined(HAVE_REDIS_ZSTD) zval const_COMPRESSION_ZSTD_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_value, REDIS_COMPRESSION_ZSTD); zend_string *const_COMPRESSION_ZSTD_name = zend_string_init_interned("COMPRESSION_ZSTD", sizeof("COMPRESSION_ZSTD") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_name, &const_COMPRESSION_ZSTD_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_name); #endif #if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_DEFAULT) zval const_COMPRESSION_ZSTD_DEFAULT_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_DEFAULT_value, ZSTD_CLEVEL_DEFAULT); zend_string *const_COMPRESSION_ZSTD_DEFAULT_name = zend_string_init_interned("COMPRESSION_ZSTD_DEFAULT", sizeof("COMPRESSION_ZSTD_DEFAULT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); #endif #if defined(HAVE_REDIS_ZSTD) && !(defined(ZSTD_CLEVEL_DEFAULT)) zval const_COMPRESSION_ZSTD_DEFAULT_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_DEFAULT_value, 3); zend_string *const_COMPRESSION_ZSTD_DEFAULT_name = zend_string_init_interned("COMPRESSION_ZSTD_DEFAULT", sizeof("COMPRESSION_ZSTD_DEFAULT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); #endif #if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_MAX) zval const_COMPRESSION_ZSTD_MAX_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_CLEVEL_MAX); zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_MAX_name); #endif #if defined(HAVE_REDIS_ZSTD) zval const_COMPRESSION_ZSTD_MAX_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_maxCLevel()); zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_MAX_name); #endif #if defined(HAVE_REDIS_LZ4) zval const_COMPRESSION_LZ4_value; ZVAL_LONG(&const_COMPRESSION_LZ4_value, REDIS_COMPRESSION_LZ4); zend_string *const_COMPRESSION_LZ4_name = zend_string_init_interned("COMPRESSION_LZ4", sizeof("COMPRESSION_LZ4") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_LZ4_name, &const_COMPRESSION_LZ4_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_LZ4_name); #endif zval const_OPT_SCAN_value; ZVAL_LONG(&const_OPT_SCAN_value, REDIS_OPT_SCAN); zend_string *const_OPT_SCAN_name = zend_string_init_interned("OPT_SCAN", sizeof("OPT_SCAN") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_SCAN_name, &const_OPT_SCAN_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_SCAN_name); zval const_SCAN_RETRY_value; ZVAL_LONG(&const_SCAN_RETRY_value, REDIS_SCAN_RETRY); zend_string *const_SCAN_RETRY_name = zend_string_init_interned("SCAN_RETRY", sizeof("SCAN_RETRY") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SCAN_RETRY_name, &const_SCAN_RETRY_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SCAN_RETRY_name); zval const_SCAN_NORETRY_value; ZVAL_LONG(&const_SCAN_NORETRY_value, REDIS_SCAN_NORETRY); zend_string *const_SCAN_NORETRY_name = zend_string_init_interned("SCAN_NORETRY", sizeof("SCAN_NORETRY") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SCAN_NORETRY_name, &const_SCAN_NORETRY_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SCAN_NORETRY_name); zval const_SCAN_PREFIX_value; ZVAL_LONG(&const_SCAN_PREFIX_value, REDIS_SCAN_PREFIX); zend_string *const_SCAN_PREFIX_name = zend_string_init_interned("SCAN_PREFIX", sizeof("SCAN_PREFIX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SCAN_PREFIX_name, &const_SCAN_PREFIX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SCAN_PREFIX_name); zval const_SCAN_NOPREFIX_value; ZVAL_LONG(&const_SCAN_NOPREFIX_value, REDIS_SCAN_NOPREFIX); zend_string *const_SCAN_NOPREFIX_name = zend_string_init_interned("SCAN_NOPREFIX", sizeof("SCAN_NOPREFIX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SCAN_NOPREFIX_name, &const_SCAN_NOPREFIX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SCAN_NOPREFIX_name); zval const_BEFORE_value; zend_string *const_BEFORE_value_str = zend_string_init("before", strlen("before"), 1); ZVAL_STR(&const_BEFORE_value, const_BEFORE_value_str); zend_string *const_BEFORE_name = zend_string_init_interned("BEFORE", sizeof("BEFORE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BEFORE_name, &const_BEFORE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BEFORE_name); zval const_AFTER_value; zend_string *const_AFTER_value_str = zend_string_init("after", strlen("after"), 1); ZVAL_STR(&const_AFTER_value, const_AFTER_value_str); zend_string *const_AFTER_name = zend_string_init_interned("AFTER", sizeof("AFTER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_AFTER_name, &const_AFTER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_AFTER_name); zval const_LEFT_value; zend_string *const_LEFT_value_str = zend_string_init("left", strlen("left"), 1); ZVAL_STR(&const_LEFT_value, const_LEFT_value_str); zend_string *const_LEFT_name = zend_string_init_interned("LEFT", sizeof("LEFT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_LEFT_name, &const_LEFT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_LEFT_name); zval const_RIGHT_value; zend_string *const_RIGHT_value_str = zend_string_init("right", strlen("right"), 1); ZVAL_STR(&const_RIGHT_value, const_RIGHT_value_str); zend_string *const_RIGHT_name = zend_string_init_interned("RIGHT", sizeof("RIGHT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_RIGHT_name, &const_RIGHT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_RIGHT_name); zval const_OPT_MAX_RETRIES_value; ZVAL_LONG(&const_OPT_MAX_RETRIES_value, REDIS_OPT_MAX_RETRIES); zend_string *const_OPT_MAX_RETRIES_name = zend_string_init_interned("OPT_MAX_RETRIES", sizeof("OPT_MAX_RETRIES") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_MAX_RETRIES_name, &const_OPT_MAX_RETRIES_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_MAX_RETRIES_name); zval const_OPT_BACKOFF_ALGORITHM_value; ZVAL_LONG(&const_OPT_BACKOFF_ALGORITHM_value, REDIS_OPT_BACKOFF_ALGORITHM); zend_string *const_OPT_BACKOFF_ALGORITHM_name = zend_string_init_interned("OPT_BACKOFF_ALGORITHM", sizeof("OPT_BACKOFF_ALGORITHM") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_ALGORITHM_name, &const_OPT_BACKOFF_ALGORITHM_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_BACKOFF_ALGORITHM_name); zval const_BACKOFF_ALGORITHM_DEFAULT_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_DEFAULT_value, REDIS_BACKOFF_ALGORITHM_DEFAULT); zend_string *const_BACKOFF_ALGORITHM_DEFAULT_name = zend_string_init_interned("BACKOFF_ALGORITHM_DEFAULT", sizeof("BACKOFF_ALGORITHM_DEFAULT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_DEFAULT_name, &const_BACKOFF_ALGORITHM_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_DEFAULT_name); zval const_BACKOFF_ALGORITHM_CONSTANT_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_CONSTANT_value, REDIS_BACKOFF_ALGORITHM_CONSTANT); zend_string *const_BACKOFF_ALGORITHM_CONSTANT_name = zend_string_init_interned("BACKOFF_ALGORITHM_CONSTANT", sizeof("BACKOFF_ALGORITHM_CONSTANT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_CONSTANT_name, &const_BACKOFF_ALGORITHM_CONSTANT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_CONSTANT_name); zval const_BACKOFF_ALGORITHM_UNIFORM_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_UNIFORM_value, REDIS_BACKOFF_ALGORITHM_UNIFORM); zend_string *const_BACKOFF_ALGORITHM_UNIFORM_name = zend_string_init_interned("BACKOFF_ALGORITHM_UNIFORM", sizeof("BACKOFF_ALGORITHM_UNIFORM") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_UNIFORM_name, &const_BACKOFF_ALGORITHM_UNIFORM_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_UNIFORM_name); zval const_BACKOFF_ALGORITHM_EXPONENTIAL_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_EXPONENTIAL_value, REDIS_BACKOFF_ALGORITHM_EXPONENTIAL); zend_string *const_BACKOFF_ALGORITHM_EXPONENTIAL_name = zend_string_init_interned("BACKOFF_ALGORITHM_EXPONENTIAL", sizeof("BACKOFF_ALGORITHM_EXPONENTIAL") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_EXPONENTIAL_name, &const_BACKOFF_ALGORITHM_EXPONENTIAL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_EXPONENTIAL_name); zval const_BACKOFF_ALGORITHM_FULL_JITTER_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_FULL_JITTER_value, REDIS_BACKOFF_ALGORITHM_FULL_JITTER); zend_string *const_BACKOFF_ALGORITHM_FULL_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_FULL_JITTER", sizeof("BACKOFF_ALGORITHM_FULL_JITTER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_FULL_JITTER_name, &const_BACKOFF_ALGORITHM_FULL_JITTER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_FULL_JITTER_name); zval const_BACKOFF_ALGORITHM_EQUAL_JITTER_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_EQUAL_JITTER_value, REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER); zend_string *const_BACKOFF_ALGORITHM_EQUAL_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_EQUAL_JITTER", sizeof("BACKOFF_ALGORITHM_EQUAL_JITTER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_EQUAL_JITTER_name, &const_BACKOFF_ALGORITHM_EQUAL_JITTER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_EQUAL_JITTER_name); zval const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value, REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER); zend_string *const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_DECORRELATED_JITTER", sizeof("BACKOFF_ALGORITHM_DECORRELATED_JITTER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name, &const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name); zval const_OPT_BACKOFF_BASE_value; ZVAL_LONG(&const_OPT_BACKOFF_BASE_value, REDIS_OPT_BACKOFF_BASE); zend_string *const_OPT_BACKOFF_BASE_name = zend_string_init_interned("OPT_BACKOFF_BASE", sizeof("OPT_BACKOFF_BASE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_BASE_name, &const_OPT_BACKOFF_BASE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_BACKOFF_BASE_name); zval const_OPT_BACKOFF_CAP_value; ZVAL_LONG(&const_OPT_BACKOFF_CAP_value, REDIS_OPT_BACKOFF_CAP); zend_string *const_OPT_BACKOFF_CAP_name = zend_string_init_interned("OPT_BACKOFF_CAP", sizeof("OPT_BACKOFF_CAP") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_CAP_name, &const_OPT_BACKOFF_CAP_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_BACKOFF_CAP_name); #if (PHP_VERSION_ID >= 80200) zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "auth", sizeof("auth") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "migrate", sizeof("migrate") - 1), 7, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); #endif return class_entry; } static zend_class_entry *register_class_RedisException(zend_class_entry *class_entry_RuntimeException) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisException", class_RedisException_methods); class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException); return class_entry; } redis-6.0.2/redis_legacy_arginfo.h0000644000175000000120000022502214515245367017733 0ustar pyatsukhnenkowheel/* This is a generated file, edit the .stub.php file instead. * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__compress, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_Redis__uncompress arginfo_class_Redis__compress ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__prefix, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() #define arginfo_class_Redis__serialize arginfo_class_Redis__compress #define arginfo_class_Redis__unserialize arginfo_class_Redis__compress #define arginfo_class_Redis__pack arginfo_class_Redis__compress #define arginfo_class_Redis__unpack arginfo_class_Redis__compress ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_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_class_Redis_append, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_auth, 0, 0, 1) ZEND_ARG_INFO(0, credentials) ZEND_END_ARG_INFO() #define arginfo_class_Redis_bgSave arginfo_class_Redis___destruct #define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, bybit) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitop, 0, 0, 3) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, deskey) ZEND_ARG_INFO(0, srckey) ZEND_ARG_VARIADIC_INFO(0, other_keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, bit) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, bybit) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_blPop, 0, 0, 2) ZEND_ARG_INFO(0, key_or_keys) ZEND_ARG_INFO(0, timeout_or_key) ZEND_ARG_VARIADIC_INFO(0, extra_args) ZEND_END_ARG_INFO() #define arginfo_class_Redis_brPop arginfo_class_Redis_blPop ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_brpoplpush, 0, 0, 3) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bzPopMax, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timeout_or_key) ZEND_ARG_VARIADIC_INFO(0, extra_args) ZEND_END_ARG_INFO() #define arginfo_class_Redis_bzPopMin arginfo_class_Redis_bzPopMax ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bzmpop, 0, 0, 3) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, from) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zmpop, 0, 0, 2) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, from) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_Redis_blmpop arginfo_class_Redis_bzmpop #define arginfo_class_Redis_lmpop arginfo_class_Redis_zmpop #define arginfo_class_Redis_clearLastError arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_client, 0, 0, 1) ZEND_ARG_INFO(0, opt) ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() #define arginfo_class_Redis_close arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_command, 0, 0, 0) ZEND_ARG_INFO(0, opt) ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_config, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, key_or_settings) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_connect, 0, 0, 1) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, persistent_id) ZEND_ARG_INFO(0, retry_interval) ZEND_ARG_INFO(0, read_timeout) ZEND_ARG_INFO(0, context) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_copy, 0, 0, 2) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_dbSize arginfo_class_Redis___destruct #define arginfo_class_Redis_debug arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_decr, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, by) ZEND_END_ARG_INFO() #define arginfo_class_Redis_decrBy arginfo_class_Redis_append ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_VARIADIC_INFO(0, other_keys) ZEND_END_ARG_INFO() #define arginfo_class_Redis_delete arginfo_class_Redis_del #define arginfo_class_Redis_discard arginfo_class_Redis___destruct #define arginfo_class_Redis_dump arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1) ZEND_ARG_INFO(0, str) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_eval, 0, 0, 1) ZEND_ARG_INFO(0, script) ZEND_ARG_INFO(0, args) ZEND_ARG_INFO(0, num_keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_eval_ro, 0, 0, 1) ZEND_ARG_INFO(0, script_sha) ZEND_ARG_INFO(0, args) ZEND_ARG_INFO(0, num_keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_evalsha, 0, 0, 1) ZEND_ARG_INFO(0, sha1) ZEND_ARG_INFO(0, args) ZEND_ARG_INFO(0, num_keys) ZEND_END_ARG_INFO() #define arginfo_class_Redis_evalsha_ro arginfo_class_Redis_evalsha #define arginfo_class_Redis_exec arginfo_class_Redis___destruct #define arginfo_class_Redis_exists arginfo_class_Redis_del ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expire, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expireAt, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timestamp) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_failover, 0, 0, 0) ZEND_ARG_INFO(0, to) ZEND_ARG_INFO(0, abort) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() #define arginfo_class_Redis_expiretime arginfo_class_Redis__prefix #define arginfo_class_Redis_pexpiretime arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_fcall, 0, 0, 1) ZEND_ARG_INFO(0, fn) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, args) ZEND_END_ARG_INFO() #define arginfo_class_Redis_fcall_ro arginfo_class_Redis_fcall ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, 0) ZEND_ARG_INFO(0, sync) ZEND_END_ARG_INFO() #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_function, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geoadd, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, lng) ZEND_ARG_INFO(0, lat) ZEND_ARG_INFO(0, member) ZEND_ARG_VARIADIC_INFO(0, other_triples_and_options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geodist, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, unit) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geohash, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) ZEND_ARG_VARIADIC_INFO(0, other_members) ZEND_END_ARG_INFO() #define arginfo_class_Redis_geopos arginfo_class_Redis_geohash ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_georadius, 0, 0, 5) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, lng) ZEND_ARG_INFO(0, lat) ZEND_ARG_INFO(0, radius) ZEND_ARG_INFO(0, unit) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_georadius_ro arginfo_class_Redis_georadius ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_georadiusbymember, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) ZEND_ARG_INFO(0, radius) ZEND_ARG_INFO(0, unit) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_georadiusbymember_ro arginfo_class_Redis_georadiusbymember ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geosearch, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, position) ZEND_ARG_INFO(0, shape) ZEND_ARG_INFO(0, unit) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geosearchstore, 0, 0, 5) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, position) ZEND_ARG_INFO(0, shape) ZEND_ARG_INFO(0, unit) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_get arginfo_class_Redis__prefix #define arginfo_class_Redis_getAuth arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, idx) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getEx, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_getDBNum arginfo_class_Redis___destruct #define arginfo_class_Redis_getDel arginfo_class_Redis__prefix #define arginfo_class_Redis_getHost arginfo_class_Redis___destruct #define arginfo_class_Redis_getLastError arginfo_class_Redis___destruct #define arginfo_class_Redis_getMode arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getOption, 0, 0, 1) ZEND_ARG_INFO(0, option) ZEND_END_ARG_INFO() #define arginfo_class_Redis_getPersistentID arginfo_class_Redis___destruct #define arginfo_class_Redis_getPort arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lcs, 0, 0, 2) ZEND_ARG_INFO(0, key1) ZEND_ARG_INFO(0, key2) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_getReadTimeout arginfo_class_Redis___destruct #define arginfo_class_Redis_getset arginfo_class_Redis_append #define arginfo_class_Redis_getTimeout arginfo_class_Redis___destruct #define arginfo_class_Redis_getTransferredBytes arginfo_class_Redis___destruct #define arginfo_class_Redis_clearTransferredBytes arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hDel, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, field) ZEND_ARG_VARIADIC_INFO(0, other_fields) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hExists, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, field) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hGet, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() #define arginfo_class_Redis_hGetAll arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hIncrBy, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, field) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_Redis_hIncrByFloat arginfo_class_Redis_hIncrBy #define arginfo_class_Redis_hKeys arginfo_class_Redis__prefix #define arginfo_class_Redis_hLen arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMget, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, fields) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMset, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, fieldvals) ZEND_END_ARG_INFO() #define arginfo_class_Redis_hRandField arginfo_class_Redis_getEx ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hSet, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_Redis_hSetNx arginfo_class_Redis_hIncrBy #define arginfo_class_Redis_hStrLen arginfo_class_Redis_hExists #define arginfo_class_Redis_hVals arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(1, iterator) ZEND_ARG_INFO(0, pattern) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_Redis_incr arginfo_class_Redis_decr #define arginfo_class_Redis_incrBy arginfo_class_Redis_append #define arginfo_class_Redis_incrByFloat arginfo_class_Redis_append ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_info, 0, 0, 0) ZEND_ARG_VARIADIC_INFO(0, sections) ZEND_END_ARG_INFO() #define arginfo_class_Redis_isConnected arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1) ZEND_ARG_INFO(0, pattern) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, pos) ZEND_ARG_INFO(0, pivot) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_Redis_lLen arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lMove, 0, 0, 4) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, wherefrom) ZEND_ARG_INFO(0, whereto) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_blmove, 0, 0, 5) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, wherefrom) ZEND_ARG_INFO(0, whereto) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPop, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPos, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_VARIADIC_INFO(0, elements) ZEND_END_ARG_INFO() #define arginfo_class_Redis_rPush arginfo_class_Redis_lPush #define arginfo_class_Redis_lPushx arginfo_class_Redis_append #define arginfo_class_Redis_rPushx arginfo_class_Redis_append ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lSet, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, index) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_Redis_lastSave arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lindex, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, index) ZEND_END_ARG_INFO() #define arginfo_class_Redis_lrange arginfo_class_Redis_getRange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lrem, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_Redis_ltrim arginfo_class_Redis_getRange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_migrate, 0, 0, 5) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, dstdb) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, copy) ZEND_ARG_INFO(0, replace) ZEND_ARG_INFO(0, credentials) ZEND_END_ARG_INFO() #define arginfo_class_Redis_move arginfo_class_Redis_lindex ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mset, 0, 0, 1) ZEND_ARG_INFO(0, key_values) ZEND_END_ARG_INFO() #define arginfo_class_Redis_msetnx arginfo_class_Redis_mset ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_multi, 0, 0, 0) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_object, 0, 0, 2) ZEND_ARG_INFO(0, subcommand) ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() #define arginfo_class_Redis_open arginfo_class_Redis_connect #define arginfo_class_Redis_pconnect arginfo_class_Redis_connect #define arginfo_class_Redis_persist arginfo_class_Redis__prefix #define arginfo_class_Redis_pexpire arginfo_class_Redis_expire #define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfadd, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, elements) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfcount, 0, 0, 1) ZEND_ARG_INFO(0, key_or_keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfmerge, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, srckeys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0) ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO() #define arginfo_class_Redis_pipeline arginfo_class_Redis___destruct #define arginfo_class_Redis_popen arginfo_class_Redis_connect ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psetex, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expire) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psubscribe, 0, 0, 2) ZEND_ARG_INFO(0, patterns) ZEND_ARG_INFO(0, cb) ZEND_END_ARG_INFO() #define arginfo_class_Redis_pttl arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_publish, 0, 0, 2) ZEND_ARG_INFO(0, channel) ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pubsub, 0, 0, 1) ZEND_ARG_INFO(0, command) ZEND_ARG_INFO(0, arg) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_punsubscribe, 0, 0, 1) ZEND_ARG_INFO(0, patterns) ZEND_END_ARG_INFO() #define arginfo_class_Redis_rPop arginfo_class_Redis_lPop #define arginfo_class_Redis_randomKey arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rawcommand, 0, 0, 1) ZEND_ARG_INFO(0, command) ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rename, 0, 0, 2) ZEND_ARG_INFO(0, old_name) ZEND_ARG_INFO(0, new_name) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_renameNx, 0, 0, 2) ZEND_ARG_INFO(0, key_src) ZEND_ARG_INFO(0, key_dst) ZEND_END_ARG_INFO() #define arginfo_class_Redis_reset arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_restore, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, ttl) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_role arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rpoplpush, 0, 0, 2) ZEND_ARG_INFO(0, srckey) ZEND_ARG_INFO(0, dstkey) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sAdd, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_VARIADIC_INFO(0, other_values) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sAddArray, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, values) ZEND_END_ARG_INFO() #define arginfo_class_Redis_sDiff arginfo_class_Redis_del ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sDiffStore, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, key) ZEND_ARG_VARIADIC_INFO(0, other_keys) ZEND_END_ARG_INFO() #define arginfo_class_Redis_sInter arginfo_class_Redis_del ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sintercard, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, limit) ZEND_END_ARG_INFO() #define arginfo_class_Redis_sInterStore arginfo_class_Redis_del #define arginfo_class_Redis_sMembers arginfo_class_Redis__prefix #define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sMove, 0, 0, 3) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_Redis_sPop arginfo_class_Redis_lPop #define arginfo_class_Redis_sRandMember arginfo_class_Redis_lPop #define arginfo_class_Redis_sUnion arginfo_class_Redis_del #define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore #define arginfo_class_Redis_save arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_scan, 0, 0, 1) ZEND_ARG_INFO(1, iterator) ZEND_ARG_INFO(0, pattern) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, type) ZEND_END_ARG_INFO() #define arginfo_class_Redis_scard arginfo_class_Redis__prefix #define arginfo_class_Redis_script arginfo_class_Redis_rawcommand ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_select, 0, 0, 1) ZEND_ARG_INFO(0, db) ZEND_END_ARG_INFO() #define arginfo_class_Redis_set arginfo_class_Redis_lPos ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, idx) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_Redis_setRange arginfo_class_Redis_lSet ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setOption, 0, 0, 2) ZEND_ARG_INFO(0, option) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_Redis_setex arginfo_class_Redis_psetex #define arginfo_class_Redis_setnx arginfo_class_Redis_append #define arginfo_class_Redis_sismember arginfo_class_Redis_append ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, 0) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) ZEND_END_ARG_INFO() #define arginfo_class_Redis_replicaof arginfo_class_Redis_slaveof ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_touch, 0, 0, 1) ZEND_ARG_INFO(0, key_or_array) ZEND_ARG_VARIADIC_INFO(0, more_keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slowlog, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, length) ZEND_END_ARG_INFO() #define arginfo_class_Redis_sort arginfo_class_Redis_getEx #define arginfo_class_Redis_sort_ro arginfo_class_Redis_getEx ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sortAsc, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, pattern) ZEND_ARG_INFO(0, get) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, store) ZEND_END_ARG_INFO() #define arginfo_class_Redis_sortAscAlpha arginfo_class_Redis_sortAsc #define arginfo_class_Redis_sortDesc arginfo_class_Redis_sortAsc #define arginfo_class_Redis_sortDescAlpha arginfo_class_Redis_sortAsc #define arginfo_class_Redis_srem arginfo_class_Redis_sAdd #define arginfo_class_Redis_sscan arginfo_class_Redis_hscan ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ssubscribe, 0, 0, 2) ZEND_ARG_INFO(0, channels) ZEND_ARG_INFO(0, cb) ZEND_END_ARG_INFO() #define arginfo_class_Redis_strlen arginfo_class_Redis__prefix #define arginfo_class_Redis_subscribe arginfo_class_Redis_ssubscribe ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sunsubscribe, 0, 0, 1) ZEND_ARG_INFO(0, channels) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_swapdb, 0, 0, 2) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_END_ARG_INFO() #define arginfo_class_Redis_time arginfo_class_Redis___destruct #define arginfo_class_Redis_ttl arginfo_class_Redis__prefix #define arginfo_class_Redis_type arginfo_class_Redis__prefix #define arginfo_class_Redis_unlink arginfo_class_Redis_del #define arginfo_class_Redis_unsubscribe arginfo_class_Redis_sunsubscribe #define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct #define arginfo_class_Redis_watch arginfo_class_Redis_del ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_wait, 0, 0, 2) ZEND_ARG_INFO(0, numreplicas) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xack, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, ids) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xadd, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, id) ZEND_ARG_INFO(0, values) ZEND_ARG_INFO(0, maxlen) ZEND_ARG_INFO(0, approx) ZEND_ARG_INFO(0, nomkstream) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xautoclaim, 0, 0, 5) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, consumer) ZEND_ARG_INFO(0, min_idle) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, justid) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xclaim, 0, 0, 6) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, consumer) ZEND_ARG_INFO(0, min_idle) ZEND_ARG_INFO(0, ids) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xdel, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, ids) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xgroup, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, id_or_consumer) ZEND_ARG_INFO(0, mkstream) ZEND_ARG_INFO(0, entries_read) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xinfo, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, arg1) ZEND_ARG_INFO(0, arg2) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_Redis_xlen arginfo_class_Redis__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xpending, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, consumer) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xread, 0, 0, 1) ZEND_ARG_INFO(0, streams) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, block) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xreadgroup, 0, 0, 3) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, consumer) ZEND_ARG_INFO(0, streams) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, block) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xrevrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xtrim, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, threshold) ZEND_ARG_INFO(0, approx) ZEND_ARG_INFO(0, minid) ZEND_ARG_INFO(0, limit) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zAdd, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, score_or_options) ZEND_ARG_VARIADIC_INFO(0, more_scores_and_mems) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zCard arginfo_class_Redis__prefix #define arginfo_class_Redis_zCount arginfo_class_Redis_getRange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zIncrBy, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zLexCount, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, min) ZEND_ARG_INFO(0, max) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zMscore arginfo_class_Redis_geohash #define arginfo_class_Redis_zPopMax arginfo_class_Redis_lPop #define arginfo_class_Redis_zPopMin arginfo_class_Redis_lPop ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, min) ZEND_ARG_INFO(0, max) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRangeByScore arginfo_class_Redis_zRange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zrangestore, 0, 0, 4) ZEND_ARG_INFO(0, dstkey) ZEND_ARG_INFO(0, srckey) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRandMember arginfo_class_Redis_getEx #define arginfo_class_Redis_zRank arginfo_class_Redis_hGet #define arginfo_class_Redis_zRem arginfo_class_Redis_geohash #define arginfo_class_Redis_zRemRangeByLex arginfo_class_Redis_zLexCount #define arginfo_class_Redis_zRemRangeByRank arginfo_class_Redis_getRange #define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_getRange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, scores) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRangeByLex, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, max) ZEND_ARG_INFO(0, min) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRangeByScore, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, max) ZEND_ARG_INFO(0, min) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRevRank arginfo_class_Redis_hGet #define arginfo_class_Redis_zScore arginfo_class_Redis_hGet ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zdiff, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zdiffstore, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zinter, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, weights) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zintercard arginfo_class_Redis_sintercard ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zinterstore, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, weights) ZEND_ARG_INFO(0, aggregate) ZEND_END_ARG_INFO() #define arginfo_class_Redis_zscan arginfo_class_Redis_hscan #define arginfo_class_Redis_zunion arginfo_class_Redis_zinter #define arginfo_class_Redis_zunionstore arginfo_class_Redis_zinterstore ZEND_METHOD(Redis, __construct); ZEND_METHOD(Redis, __destruct); ZEND_METHOD(Redis, _compress); ZEND_METHOD(Redis, _uncompress); ZEND_METHOD(Redis, _prefix); ZEND_METHOD(Redis, _serialize); ZEND_METHOD(Redis, _unserialize); ZEND_METHOD(Redis, _pack); ZEND_METHOD(Redis, _unpack); ZEND_METHOD(Redis, acl); ZEND_METHOD(Redis, append); ZEND_METHOD(Redis, auth); ZEND_METHOD(Redis, bgSave); ZEND_METHOD(Redis, bgrewriteaof); ZEND_METHOD(Redis, bitcount); ZEND_METHOD(Redis, bitop); ZEND_METHOD(Redis, bitpos); ZEND_METHOD(Redis, blPop); ZEND_METHOD(Redis, brPop); ZEND_METHOD(Redis, brpoplpush); ZEND_METHOD(Redis, bzPopMax); ZEND_METHOD(Redis, bzPopMin); ZEND_METHOD(Redis, bzmpop); ZEND_METHOD(Redis, zmpop); ZEND_METHOD(Redis, blmpop); ZEND_METHOD(Redis, lmpop); ZEND_METHOD(Redis, clearLastError); ZEND_METHOD(Redis, client); ZEND_METHOD(Redis, close); ZEND_METHOD(Redis, command); ZEND_METHOD(Redis, config); ZEND_METHOD(Redis, connect); ZEND_METHOD(Redis, copy); ZEND_METHOD(Redis, dbSize); ZEND_METHOD(Redis, debug); ZEND_METHOD(Redis, decr); ZEND_METHOD(Redis, decrBy); ZEND_METHOD(Redis, del); ZEND_METHOD(Redis, discard); ZEND_METHOD(Redis, dump); ZEND_METHOD(Redis, echo); ZEND_METHOD(Redis, eval); ZEND_METHOD(Redis, eval_ro); ZEND_METHOD(Redis, evalsha); ZEND_METHOD(Redis, evalsha_ro); ZEND_METHOD(Redis, exec); ZEND_METHOD(Redis, exists); ZEND_METHOD(Redis, expire); ZEND_METHOD(Redis, expireAt); ZEND_METHOD(Redis, failover); ZEND_METHOD(Redis, expiretime); ZEND_METHOD(Redis, pexpiretime); ZEND_METHOD(Redis, fcall); ZEND_METHOD(Redis, fcall_ro); ZEND_METHOD(Redis, flushAll); ZEND_METHOD(Redis, flushDB); ZEND_METHOD(Redis, function); ZEND_METHOD(Redis, geoadd); ZEND_METHOD(Redis, geodist); ZEND_METHOD(Redis, geohash); ZEND_METHOD(Redis, geopos); ZEND_METHOD(Redis, georadius); ZEND_METHOD(Redis, georadius_ro); ZEND_METHOD(Redis, georadiusbymember); ZEND_METHOD(Redis, georadiusbymember_ro); ZEND_METHOD(Redis, geosearch); ZEND_METHOD(Redis, geosearchstore); ZEND_METHOD(Redis, get); ZEND_METHOD(Redis, getAuth); ZEND_METHOD(Redis, getBit); ZEND_METHOD(Redis, getEx); ZEND_METHOD(Redis, getDBNum); ZEND_METHOD(Redis, getDel); ZEND_METHOD(Redis, getHost); ZEND_METHOD(Redis, getLastError); ZEND_METHOD(Redis, getMode); ZEND_METHOD(Redis, getOption); ZEND_METHOD(Redis, getPersistentID); ZEND_METHOD(Redis, getPort); ZEND_METHOD(Redis, getRange); ZEND_METHOD(Redis, lcs); ZEND_METHOD(Redis, getReadTimeout); ZEND_METHOD(Redis, getset); ZEND_METHOD(Redis, getTimeout); ZEND_METHOD(Redis, getTransferredBytes); ZEND_METHOD(Redis, clearTransferredBytes); ZEND_METHOD(Redis, hDel); ZEND_METHOD(Redis, hExists); ZEND_METHOD(Redis, hGet); ZEND_METHOD(Redis, hGetAll); ZEND_METHOD(Redis, hIncrBy); ZEND_METHOD(Redis, hIncrByFloat); ZEND_METHOD(Redis, hKeys); ZEND_METHOD(Redis, hLen); ZEND_METHOD(Redis, hMget); ZEND_METHOD(Redis, hMset); ZEND_METHOD(Redis, hRandField); ZEND_METHOD(Redis, hSet); ZEND_METHOD(Redis, hSetNx); ZEND_METHOD(Redis, hStrLen); ZEND_METHOD(Redis, hVals); ZEND_METHOD(Redis, hscan); ZEND_METHOD(Redis, incr); ZEND_METHOD(Redis, incrBy); ZEND_METHOD(Redis, incrByFloat); ZEND_METHOD(Redis, info); ZEND_METHOD(Redis, isConnected); ZEND_METHOD(Redis, keys); ZEND_METHOD(Redis, lInsert); ZEND_METHOD(Redis, lLen); ZEND_METHOD(Redis, lMove); ZEND_METHOD(Redis, blmove); ZEND_METHOD(Redis, lPop); ZEND_METHOD(Redis, lPos); ZEND_METHOD(Redis, lPush); ZEND_METHOD(Redis, rPush); ZEND_METHOD(Redis, lPushx); ZEND_METHOD(Redis, rPushx); ZEND_METHOD(Redis, lSet); ZEND_METHOD(Redis, lastSave); ZEND_METHOD(Redis, lindex); ZEND_METHOD(Redis, lrange); ZEND_METHOD(Redis, lrem); ZEND_METHOD(Redis, ltrim); ZEND_METHOD(Redis, mget); ZEND_METHOD(Redis, migrate); ZEND_METHOD(Redis, move); ZEND_METHOD(Redis, mset); ZEND_METHOD(Redis, msetnx); ZEND_METHOD(Redis, multi); ZEND_METHOD(Redis, object); ZEND_METHOD(Redis, pconnect); ZEND_METHOD(Redis, persist); ZEND_METHOD(Redis, pexpire); ZEND_METHOD(Redis, pexpireAt); ZEND_METHOD(Redis, pfadd); ZEND_METHOD(Redis, pfcount); ZEND_METHOD(Redis, pfmerge); ZEND_METHOD(Redis, ping); ZEND_METHOD(Redis, pipeline); ZEND_METHOD(Redis, psetex); ZEND_METHOD(Redis, psubscribe); ZEND_METHOD(Redis, pttl); ZEND_METHOD(Redis, publish); ZEND_METHOD(Redis, pubsub); ZEND_METHOD(Redis, punsubscribe); ZEND_METHOD(Redis, rPop); ZEND_METHOD(Redis, randomKey); ZEND_METHOD(Redis, rawcommand); ZEND_METHOD(Redis, rename); ZEND_METHOD(Redis, renameNx); ZEND_METHOD(Redis, reset); ZEND_METHOD(Redis, restore); ZEND_METHOD(Redis, role); ZEND_METHOD(Redis, rpoplpush); ZEND_METHOD(Redis, sAdd); ZEND_METHOD(Redis, sAddArray); ZEND_METHOD(Redis, sDiff); ZEND_METHOD(Redis, sDiffStore); ZEND_METHOD(Redis, sInter); ZEND_METHOD(Redis, sintercard); ZEND_METHOD(Redis, sInterStore); ZEND_METHOD(Redis, sMembers); ZEND_METHOD(Redis, sMisMember); ZEND_METHOD(Redis, sMove); ZEND_METHOD(Redis, sPop); ZEND_METHOD(Redis, sRandMember); ZEND_METHOD(Redis, sUnion); ZEND_METHOD(Redis, sUnionStore); ZEND_METHOD(Redis, save); ZEND_METHOD(Redis, scan); ZEND_METHOD(Redis, scard); ZEND_METHOD(Redis, script); ZEND_METHOD(Redis, select); ZEND_METHOD(Redis, set); ZEND_METHOD(Redis, setBit); ZEND_METHOD(Redis, setRange); ZEND_METHOD(Redis, setOption); ZEND_METHOD(Redis, setex); ZEND_METHOD(Redis, setnx); ZEND_METHOD(Redis, sismember); ZEND_METHOD(Redis, slaveof); ZEND_METHOD(Redis, replicaof); ZEND_METHOD(Redis, touch); ZEND_METHOD(Redis, slowlog); ZEND_METHOD(Redis, sort); ZEND_METHOD(Redis, sort_ro); ZEND_METHOD(Redis, sortAsc); ZEND_METHOD(Redis, sortAscAlpha); ZEND_METHOD(Redis, sortDesc); ZEND_METHOD(Redis, sortDescAlpha); ZEND_METHOD(Redis, srem); ZEND_METHOD(Redis, sscan); ZEND_METHOD(Redis, ssubscribe); ZEND_METHOD(Redis, strlen); ZEND_METHOD(Redis, subscribe); ZEND_METHOD(Redis, sunsubscribe); ZEND_METHOD(Redis, swapdb); ZEND_METHOD(Redis, time); ZEND_METHOD(Redis, ttl); ZEND_METHOD(Redis, type); ZEND_METHOD(Redis, unlink); ZEND_METHOD(Redis, unsubscribe); ZEND_METHOD(Redis, unwatch); ZEND_METHOD(Redis, watch); ZEND_METHOD(Redis, wait); ZEND_METHOD(Redis, xack); ZEND_METHOD(Redis, xadd); ZEND_METHOD(Redis, xautoclaim); ZEND_METHOD(Redis, xclaim); ZEND_METHOD(Redis, xdel); ZEND_METHOD(Redis, xgroup); ZEND_METHOD(Redis, xinfo); ZEND_METHOD(Redis, xlen); ZEND_METHOD(Redis, xpending); ZEND_METHOD(Redis, xrange); ZEND_METHOD(Redis, xread); ZEND_METHOD(Redis, xreadgroup); ZEND_METHOD(Redis, xrevrange); ZEND_METHOD(Redis, xtrim); ZEND_METHOD(Redis, zAdd); ZEND_METHOD(Redis, zCard); ZEND_METHOD(Redis, zCount); ZEND_METHOD(Redis, zIncrBy); ZEND_METHOD(Redis, zLexCount); ZEND_METHOD(Redis, zMscore); ZEND_METHOD(Redis, zPopMax); ZEND_METHOD(Redis, zPopMin); ZEND_METHOD(Redis, zRange); ZEND_METHOD(Redis, zRangeByLex); ZEND_METHOD(Redis, zRangeByScore); ZEND_METHOD(Redis, zrangestore); ZEND_METHOD(Redis, zRandMember); ZEND_METHOD(Redis, zRank); ZEND_METHOD(Redis, zRem); ZEND_METHOD(Redis, zRemRangeByLex); ZEND_METHOD(Redis, zRemRangeByRank); ZEND_METHOD(Redis, zRemRangeByScore); ZEND_METHOD(Redis, zRevRange); ZEND_METHOD(Redis, zRevRangeByLex); ZEND_METHOD(Redis, zRevRangeByScore); ZEND_METHOD(Redis, zRevRank); ZEND_METHOD(Redis, zScore); ZEND_METHOD(Redis, zdiff); ZEND_METHOD(Redis, zdiffstore); ZEND_METHOD(Redis, zinter); ZEND_METHOD(Redis, zintercard); ZEND_METHOD(Redis, zinterstore); ZEND_METHOD(Redis, zscan); ZEND_METHOD(Redis, zunion); ZEND_METHOD(Redis, zunionstore); static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC) ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _prefix, arginfo_class_Redis__prefix, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC) ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC) ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC) ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgSave, arginfo_class_Redis_bgSave, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgrewriteaof, arginfo_class_Redis_bgrewriteaof, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC) ZEND_ME(Redis, blPop, arginfo_class_Redis_blPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, brPop, arginfo_class_Redis_brPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, brpoplpush, arginfo_class_Redis_brpoplpush, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bzPopMax, arginfo_class_Redis_bzPopMax, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bzPopMin, arginfo_class_Redis_bzPopMin, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bzmpop, arginfo_class_Redis_bzmpop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zmpop, arginfo_class_Redis_zmpop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, blmpop, arginfo_class_Redis_blmpop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lmpop, arginfo_class_Redis_lmpop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, clearLastError, arginfo_class_Redis_clearLastError, ZEND_ACC_PUBLIC) ZEND_ME(Redis, client, arginfo_class_Redis_client, ZEND_ACC_PUBLIC) ZEND_ME(Redis, close, arginfo_class_Redis_close, ZEND_ACC_PUBLIC) ZEND_ME(Redis, command, arginfo_class_Redis_command, ZEND_ACC_PUBLIC) ZEND_ME(Redis, config, arginfo_class_Redis_config, ZEND_ACC_PUBLIC) ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC) ZEND_ME(Redis, copy, arginfo_class_Redis_copy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, dbSize, arginfo_class_Redis_dbSize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, debug, arginfo_class_Redis_debug, ZEND_ACC_PUBLIC) ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC) ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC) ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC) ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC) ZEND_ME(Redis, eval, arginfo_class_Redis_eval, ZEND_ACC_PUBLIC) ZEND_ME(Redis, eval_ro, arginfo_class_Redis_eval_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, evalsha, arginfo_class_Redis_evalsha, ZEND_ACC_PUBLIC) ZEND_ME(Redis, evalsha_ro, arginfo_class_Redis_evalsha_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, exec, arginfo_class_Redis_exec, ZEND_ACC_PUBLIC) ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC) ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expiretime, arginfo_class_Redis_expiretime, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC) ZEND_ME(Redis, fcall, arginfo_class_Redis_fcall, ZEND_ACC_PUBLIC) ZEND_ME(Redis, fcall_ro, arginfo_class_Redis_fcall_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC) ZEND_ME(Redis, function, arginfo_class_Redis_function, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geodist, arginfo_class_Redis_geodist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geohash, arginfo_class_Redis_geohash, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geopos, arginfo_class_Redis_geopos, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadius, arginfo_class_Redis_georadius, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadius_ro, arginfo_class_Redis_georadius_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadiusbymember, arginfo_class_Redis_georadiusbymember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadiusbymember_ro, arginfo_class_Redis_georadiusbymember_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getEx, arginfo_class_Redis_getEx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getDBNum, arginfo_class_Redis_getDBNum, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getDel, arginfo_class_Redis_getDel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getHost, arginfo_class_Redis_getHost, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getLastError, arginfo_class_Redis_getLastError, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getMode, arginfo_class_Redis_getMode, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getTransferredBytes, arginfo_class_Redis_getTransferredBytes, ZEND_ACC_PUBLIC) ZEND_ME(Redis, clearTransferredBytes, arginfo_class_Redis_clearTransferredBytes, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hDel, arginfo_class_Redis_hDel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGetAll, arginfo_class_Redis_hGetAll, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hIncrBy, arginfo_class_Redis_hIncrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hIncrByFloat, arginfo_class_Redis_hIncrByFloat, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hKeys, arginfo_class_Redis_hKeys, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hLen, arginfo_class_Redis_hLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hMget, arginfo_class_Redis_hMget, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hMset, arginfo_class_Redis_hMset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hRandField, arginfo_class_Redis_hRandField, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hSet, arginfo_class_Redis_hSet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC) ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC) ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC) ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC) ZEND_ME(Redis, blmove, arginfo_class_Redis_blmove, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPos, arginfo_class_Redis_lPos, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lSet, arginfo_class_Redis_lSet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lastSave, arginfo_class_Redis_lastSave, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lindex, arginfo_class_Redis_lindex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lrange, arginfo_class_Redis_lrange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lrem, arginfo_class_Redis_lrem, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ltrim, arginfo_class_Redis_ltrim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC) ZEND_ME(Redis, migrate, arginfo_class_Redis_migrate, ZEND_ACC_PUBLIC) ZEND_ME(Redis, move, arginfo_class_Redis_move, ZEND_ACC_PUBLIC) ZEND_ME(Redis, mset, arginfo_class_Redis_mset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, msetnx, arginfo_class_Redis_msetnx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC) ZEND_ME(Redis, object, arginfo_class_Redis_object, ZEND_ACC_PUBLIC) ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC) ZEND_ME(Redis, persist, arginfo_class_Redis_persist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpire, arginfo_class_Redis_pexpire, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpireAt, arginfo_class_Redis_pexpireAt, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pfadd, arginfo_class_Redis_pfadd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pfcount, arginfo_class_Redis_pfcount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pfmerge, arginfo_class_Redis_pfmerge, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC) ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, psubscribe, arginfo_class_Redis_psubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pttl, arginfo_class_Redis_pttl, ZEND_ACC_PUBLIC) ZEND_ME(Redis, publish, arginfo_class_Redis_publish, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pubsub, arginfo_class_Redis_pubsub, ZEND_ACC_PUBLIC) ZEND_ME(Redis, punsubscribe, arginfo_class_Redis_punsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, randomKey, arginfo_class_Redis_randomKey, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rawcommand, arginfo_class_Redis_rawcommand, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC) ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, reset, arginfo_class_Redis_reset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, restore, arginfo_class_Redis_restore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, role, arginfo_class_Redis_role, ZEND_ACC_PUBLIC) ZEND_ME(Redis, rpoplpush, arginfo_class_Redis_rpoplpush, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sAdd, arginfo_class_Redis_sAdd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sAddArray, arginfo_class_Redis_sAddArray, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sDiff, arginfo_class_Redis_sDiff, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sDiffStore, arginfo_class_Redis_sDiffStore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sInter, arginfo_class_Redis_sInter, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sintercard, arginfo_class_Redis_sintercard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sInterStore, arginfo_class_Redis_sInterStore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sMembers, arginfo_class_Redis_sMembers, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sMisMember, arginfo_class_Redis_sMisMember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sMove, arginfo_class_Redis_sMove, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sPop, arginfo_class_Redis_sPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sRandMember, arginfo_class_Redis_sRandMember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sUnion, arginfo_class_Redis_sUnion, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sUnionStore, arginfo_class_Redis_sUnionStore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, save, arginfo_class_Redis_save, ZEND_ACC_PUBLIC) ZEND_ME(Redis, scan, arginfo_class_Redis_scan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, scard, arginfo_class_Redis_scard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, script, arginfo_class_Redis_script, ZEND_ACC_PUBLIC) ZEND_ME(Redis, select, arginfo_class_Redis_select, ZEND_ACC_PUBLIC) ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setOption, arginfo_class_Redis_setOption, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sismember, arginfo_class_Redis_sismember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, replicaof, arginfo_class_Redis_replicaof, ZEND_ACC_PUBLIC) ZEND_ME(Redis, touch, arginfo_class_Redis_touch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sort_ro, arginfo_class_Redis_sort_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sortAsc, arginfo_class_Redis_sortAsc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, sortAscAlpha, arginfo_class_Redis_sortAscAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, sortDesc, arginfo_class_Redis_sortDesc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, sortDescAlpha, arginfo_class_Redis_sortDescAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, srem, arginfo_class_Redis_srem, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sscan, arginfo_class_Redis_sscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ssubscribe, arginfo_class_Redis_ssubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, subscribe, arginfo_class_Redis_subscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sunsubscribe, arginfo_class_Redis_sunsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, swapdb, arginfo_class_Redis_swapdb, ZEND_ACC_PUBLIC) ZEND_ME(Redis, time, arginfo_class_Redis_time, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ttl, arginfo_class_Redis_ttl, ZEND_ACC_PUBLIC) ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC) ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC) ZEND_ME(Redis, unsubscribe, arginfo_class_Redis_unsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, wait, arginfo_class_Redis_wait, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xack, arginfo_class_Redis_xack, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xadd, arginfo_class_Redis_xadd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xautoclaim, arginfo_class_Redis_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xclaim, arginfo_class_Redis_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xdel, arginfo_class_Redis_xdel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xgroup, arginfo_class_Redis_xgroup, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xinfo, arginfo_class_Redis_xinfo, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xlen, arginfo_class_Redis_xlen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xpending, arginfo_class_Redis_xpending, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xrange, arginfo_class_Redis_xrange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xread, arginfo_class_Redis_xread, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xreadgroup, arginfo_class_Redis_xreadgroup, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xrevrange, arginfo_class_Redis_xrevrange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xtrim, arginfo_class_Redis_xtrim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zAdd, arginfo_class_Redis_zAdd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zCard, arginfo_class_Redis_zCard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zCount, arginfo_class_Redis_zCount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zIncrBy, arginfo_class_Redis_zIncrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zLexCount, arginfo_class_Redis_zLexCount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zMscore, arginfo_class_Redis_zMscore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zPopMax, arginfo_class_Redis_zPopMax, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zPopMin, arginfo_class_Redis_zPopMin, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zrangestore, arginfo_class_Redis_zrangestore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRandMember, arginfo_class_Redis_zRandMember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRemRangeByLex, arginfo_class_Redis_zRemRangeByLex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRemRangeByRank, arginfo_class_Redis_zRemRangeByRank, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRemRangeByScore, arginfo_class_Redis_zRemRangeByScore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRevRange, arginfo_class_Redis_zRevRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRevRangeByLex, arginfo_class_Redis_zRevRangeByLex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRevRangeByScore, arginfo_class_Redis_zRevRangeByScore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRevRank, arginfo_class_Redis_zRevRank, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zScore, arginfo_class_Redis_zScore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zdiff, arginfo_class_Redis_zdiff, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zdiffstore, arginfo_class_Redis_zdiffstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zinter, arginfo_class_Redis_zinter, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zintercard, arginfo_class_Redis_zintercard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zinterstore, arginfo_class_Redis_zinterstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zscan, arginfo_class_Redis_zscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zunion, arginfo_class_Redis_zunion, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zunionstore, arginfo_class_Redis_zunionstore, ZEND_ACC_PUBLIC) ZEND_FE_END }; static const zend_function_entry class_RedisException_methods[] = { ZEND_FE_END }; static zend_class_entry *register_class_Redis(void) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); zval const_REDIS_NOT_FOUND_value; ZVAL_LONG(&const_REDIS_NOT_FOUND_value, REDIS_NOT_FOUND); zend_string *const_REDIS_NOT_FOUND_name = zend_string_init_interned("REDIS_NOT_FOUND", sizeof("REDIS_NOT_FOUND") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_NOT_FOUND_name, &const_REDIS_NOT_FOUND_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_NOT_FOUND_name); zval const_REDIS_STRING_value; ZVAL_LONG(&const_REDIS_STRING_value, REDIS_STRING); zend_string *const_REDIS_STRING_name = zend_string_init_interned("REDIS_STRING", sizeof("REDIS_STRING") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_STRING_name, &const_REDIS_STRING_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_STRING_name); zval const_REDIS_SET_value; ZVAL_LONG(&const_REDIS_SET_value, REDIS_SET); zend_string *const_REDIS_SET_name = zend_string_init_interned("REDIS_SET", sizeof("REDIS_SET") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_SET_name, &const_REDIS_SET_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_SET_name); zval const_REDIS_LIST_value; ZVAL_LONG(&const_REDIS_LIST_value, REDIS_LIST); zend_string *const_REDIS_LIST_name = zend_string_init_interned("REDIS_LIST", sizeof("REDIS_LIST") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_LIST_name, &const_REDIS_LIST_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_LIST_name); zval const_REDIS_ZSET_value; ZVAL_LONG(&const_REDIS_ZSET_value, REDIS_ZSET); zend_string *const_REDIS_ZSET_name = zend_string_init_interned("REDIS_ZSET", sizeof("REDIS_ZSET") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_ZSET_name, &const_REDIS_ZSET_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_ZSET_name); zval const_REDIS_HASH_value; ZVAL_LONG(&const_REDIS_HASH_value, REDIS_HASH); zend_string *const_REDIS_HASH_name = zend_string_init_interned("REDIS_HASH", sizeof("REDIS_HASH") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_HASH_name, &const_REDIS_HASH_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_HASH_name); zval const_REDIS_STREAM_value; ZVAL_LONG(&const_REDIS_STREAM_value, REDIS_STREAM); zend_string *const_REDIS_STREAM_name = zend_string_init_interned("REDIS_STREAM", sizeof("REDIS_STREAM") - 1, 1); zend_declare_class_constant_ex(class_entry, const_REDIS_STREAM_name, &const_REDIS_STREAM_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_STREAM_name); zval const_ATOMIC_value; ZVAL_LONG(&const_ATOMIC_value, ATOMIC); zend_string *const_ATOMIC_name = zend_string_init_interned("ATOMIC", sizeof("ATOMIC") - 1, 1); zend_declare_class_constant_ex(class_entry, const_ATOMIC_name, &const_ATOMIC_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_ATOMIC_name); zval const_MULTI_value; ZVAL_LONG(&const_MULTI_value, MULTI); zend_string *const_MULTI_name = zend_string_init_interned("MULTI", sizeof("MULTI") - 1, 1); zend_declare_class_constant_ex(class_entry, const_MULTI_name, &const_MULTI_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_MULTI_name); zval const_PIPELINE_value; ZVAL_LONG(&const_PIPELINE_value, PIPELINE); zend_string *const_PIPELINE_name = zend_string_init_interned("PIPELINE", sizeof("PIPELINE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_PIPELINE_name, &const_PIPELINE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_PIPELINE_name); zval const_OPT_SERIALIZER_value; ZVAL_LONG(&const_OPT_SERIALIZER_value, REDIS_OPT_SERIALIZER); zend_string *const_OPT_SERIALIZER_name = zend_string_init_interned("OPT_SERIALIZER", sizeof("OPT_SERIALIZER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_SERIALIZER_name, &const_OPT_SERIALIZER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_SERIALIZER_name); zval const_OPT_PREFIX_value; ZVAL_LONG(&const_OPT_PREFIX_value, REDIS_OPT_PREFIX); zend_string *const_OPT_PREFIX_name = zend_string_init_interned("OPT_PREFIX", sizeof("OPT_PREFIX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_PREFIX_name, &const_OPT_PREFIX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_PREFIX_name); zval const_OPT_READ_TIMEOUT_value; ZVAL_LONG(&const_OPT_READ_TIMEOUT_value, REDIS_OPT_READ_TIMEOUT); zend_string *const_OPT_READ_TIMEOUT_name = zend_string_init_interned("OPT_READ_TIMEOUT", sizeof("OPT_READ_TIMEOUT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_READ_TIMEOUT_name, &const_OPT_READ_TIMEOUT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_READ_TIMEOUT_name); zval const_OPT_TCP_KEEPALIVE_value; ZVAL_LONG(&const_OPT_TCP_KEEPALIVE_value, REDIS_OPT_TCP_KEEPALIVE); zend_string *const_OPT_TCP_KEEPALIVE_name = zend_string_init_interned("OPT_TCP_KEEPALIVE", sizeof("OPT_TCP_KEEPALIVE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_TCP_KEEPALIVE_name, &const_OPT_TCP_KEEPALIVE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_TCP_KEEPALIVE_name); zval const_OPT_COMPRESSION_value; ZVAL_LONG(&const_OPT_COMPRESSION_value, REDIS_OPT_COMPRESSION); zend_string *const_OPT_COMPRESSION_name = zend_string_init_interned("OPT_COMPRESSION", sizeof("OPT_COMPRESSION") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_COMPRESSION_name, &const_OPT_COMPRESSION_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_COMPRESSION_name); zval const_OPT_REPLY_LITERAL_value; ZVAL_LONG(&const_OPT_REPLY_LITERAL_value, REDIS_OPT_REPLY_LITERAL); zend_string *const_OPT_REPLY_LITERAL_name = zend_string_init_interned("OPT_REPLY_LITERAL", sizeof("OPT_REPLY_LITERAL") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_REPLY_LITERAL_name, &const_OPT_REPLY_LITERAL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_REPLY_LITERAL_name); zval const_OPT_COMPRESSION_LEVEL_value; ZVAL_LONG(&const_OPT_COMPRESSION_LEVEL_value, REDIS_OPT_COMPRESSION_LEVEL); zend_string *const_OPT_COMPRESSION_LEVEL_name = zend_string_init_interned("OPT_COMPRESSION_LEVEL", sizeof("OPT_COMPRESSION_LEVEL") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_COMPRESSION_LEVEL_name, &const_OPT_COMPRESSION_LEVEL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_COMPRESSION_LEVEL_name); zval const_OPT_NULL_MULTIBULK_AS_NULL_value; ZVAL_LONG(&const_OPT_NULL_MULTIBULK_AS_NULL_value, REDIS_OPT_NULL_MBULK_AS_NULL); zend_string *const_OPT_NULL_MULTIBULK_AS_NULL_name = zend_string_init_interned("OPT_NULL_MULTIBULK_AS_NULL", sizeof("OPT_NULL_MULTIBULK_AS_NULL") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_NULL_MULTIBULK_AS_NULL_name, &const_OPT_NULL_MULTIBULK_AS_NULL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_NULL_MULTIBULK_AS_NULL_name); zval const_SERIALIZER_NONE_value; ZVAL_LONG(&const_SERIALIZER_NONE_value, REDIS_SERIALIZER_NONE); zend_string *const_SERIALIZER_NONE_name = zend_string_init_interned("SERIALIZER_NONE", sizeof("SERIALIZER_NONE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_NONE_name, &const_SERIALIZER_NONE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_NONE_name); zval const_SERIALIZER_PHP_value; ZVAL_LONG(&const_SERIALIZER_PHP_value, REDIS_SERIALIZER_PHP); zend_string *const_SERIALIZER_PHP_name = zend_string_init_interned("SERIALIZER_PHP", sizeof("SERIALIZER_PHP") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_PHP_name, &const_SERIALIZER_PHP_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_PHP_name); #if defined(HAVE_REDIS_IGBINARY) zval const_SERIALIZER_IGBINARY_value; ZVAL_LONG(&const_SERIALIZER_IGBINARY_value, REDIS_SERIALIZER_IGBINARY); zend_string *const_SERIALIZER_IGBINARY_name = zend_string_init_interned("SERIALIZER_IGBINARY", sizeof("SERIALIZER_IGBINARY") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_IGBINARY_name, &const_SERIALIZER_IGBINARY_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_IGBINARY_name); #endif #if defined(HAVE_REDIS_MSGPACK) zval const_SERIALIZER_MSGPACK_value; ZVAL_LONG(&const_SERIALIZER_MSGPACK_value, REDIS_SERIALIZER_MSGPACK); zend_string *const_SERIALIZER_MSGPACK_name = zend_string_init_interned("SERIALIZER_MSGPACK", sizeof("SERIALIZER_MSGPACK") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_MSGPACK_name, &const_SERIALIZER_MSGPACK_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_MSGPACK_name); #endif zval const_SERIALIZER_JSON_value; ZVAL_LONG(&const_SERIALIZER_JSON_value, REDIS_SERIALIZER_JSON); zend_string *const_SERIALIZER_JSON_name = zend_string_init_interned("SERIALIZER_JSON", sizeof("SERIALIZER_JSON") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SERIALIZER_JSON_name, &const_SERIALIZER_JSON_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SERIALIZER_JSON_name); zval const_COMPRESSION_NONE_value; ZVAL_LONG(&const_COMPRESSION_NONE_value, REDIS_COMPRESSION_NONE); zend_string *const_COMPRESSION_NONE_name = zend_string_init_interned("COMPRESSION_NONE", sizeof("COMPRESSION_NONE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_NONE_name, &const_COMPRESSION_NONE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_NONE_name); #if defined(HAVE_REDIS_LZF) zval const_COMPRESSION_LZF_value; ZVAL_LONG(&const_COMPRESSION_LZF_value, REDIS_COMPRESSION_LZF); zend_string *const_COMPRESSION_LZF_name = zend_string_init_interned("COMPRESSION_LZF", sizeof("COMPRESSION_LZF") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_LZF_name, &const_COMPRESSION_LZF_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_LZF_name); #endif #if defined(HAVE_REDIS_ZSTD) zval const_COMPRESSION_ZSTD_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_value, REDIS_COMPRESSION_ZSTD); zend_string *const_COMPRESSION_ZSTD_name = zend_string_init_interned("COMPRESSION_ZSTD", sizeof("COMPRESSION_ZSTD") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_name, &const_COMPRESSION_ZSTD_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_name); #endif #if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_DEFAULT) zval const_COMPRESSION_ZSTD_DEFAULT_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_DEFAULT_value, ZSTD_CLEVEL_DEFAULT); zend_string *const_COMPRESSION_ZSTD_DEFAULT_name = zend_string_init_interned("COMPRESSION_ZSTD_DEFAULT", sizeof("COMPRESSION_ZSTD_DEFAULT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); #endif #if defined(HAVE_REDIS_ZSTD) && !(defined(ZSTD_CLEVEL_DEFAULT)) zval const_COMPRESSION_ZSTD_DEFAULT_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_DEFAULT_value, 3); zend_string *const_COMPRESSION_ZSTD_DEFAULT_name = zend_string_init_interned("COMPRESSION_ZSTD_DEFAULT", sizeof("COMPRESSION_ZSTD_DEFAULT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); #endif #if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_MAX) zval const_COMPRESSION_ZSTD_MAX_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_CLEVEL_MAX); zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_MAX_name); #endif #if defined(HAVE_REDIS_ZSTD) zval const_COMPRESSION_ZSTD_MAX_value; ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_maxCLevel()); zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_MAX_name); #endif #if defined(HAVE_REDIS_LZ4) zval const_COMPRESSION_LZ4_value; ZVAL_LONG(&const_COMPRESSION_LZ4_value, REDIS_COMPRESSION_LZ4); zend_string *const_COMPRESSION_LZ4_name = zend_string_init_interned("COMPRESSION_LZ4", sizeof("COMPRESSION_LZ4") - 1, 1); zend_declare_class_constant_ex(class_entry, const_COMPRESSION_LZ4_name, &const_COMPRESSION_LZ4_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_LZ4_name); #endif zval const_OPT_SCAN_value; ZVAL_LONG(&const_OPT_SCAN_value, REDIS_OPT_SCAN); zend_string *const_OPT_SCAN_name = zend_string_init_interned("OPT_SCAN", sizeof("OPT_SCAN") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_SCAN_name, &const_OPT_SCAN_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_SCAN_name); zval const_SCAN_RETRY_value; ZVAL_LONG(&const_SCAN_RETRY_value, REDIS_SCAN_RETRY); zend_string *const_SCAN_RETRY_name = zend_string_init_interned("SCAN_RETRY", sizeof("SCAN_RETRY") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SCAN_RETRY_name, &const_SCAN_RETRY_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SCAN_RETRY_name); zval const_SCAN_NORETRY_value; ZVAL_LONG(&const_SCAN_NORETRY_value, REDIS_SCAN_NORETRY); zend_string *const_SCAN_NORETRY_name = zend_string_init_interned("SCAN_NORETRY", sizeof("SCAN_NORETRY") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SCAN_NORETRY_name, &const_SCAN_NORETRY_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SCAN_NORETRY_name); zval const_SCAN_PREFIX_value; ZVAL_LONG(&const_SCAN_PREFIX_value, REDIS_SCAN_PREFIX); zend_string *const_SCAN_PREFIX_name = zend_string_init_interned("SCAN_PREFIX", sizeof("SCAN_PREFIX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SCAN_PREFIX_name, &const_SCAN_PREFIX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SCAN_PREFIX_name); zval const_SCAN_NOPREFIX_value; ZVAL_LONG(&const_SCAN_NOPREFIX_value, REDIS_SCAN_NOPREFIX); zend_string *const_SCAN_NOPREFIX_name = zend_string_init_interned("SCAN_NOPREFIX", sizeof("SCAN_NOPREFIX") - 1, 1); zend_declare_class_constant_ex(class_entry, const_SCAN_NOPREFIX_name, &const_SCAN_NOPREFIX_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_SCAN_NOPREFIX_name); zval const_BEFORE_value; zend_string *const_BEFORE_value_str = zend_string_init("before", strlen("before"), 1); ZVAL_STR(&const_BEFORE_value, const_BEFORE_value_str); zend_string *const_BEFORE_name = zend_string_init_interned("BEFORE", sizeof("BEFORE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BEFORE_name, &const_BEFORE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BEFORE_name); zval const_AFTER_value; zend_string *const_AFTER_value_str = zend_string_init("after", strlen("after"), 1); ZVAL_STR(&const_AFTER_value, const_AFTER_value_str); zend_string *const_AFTER_name = zend_string_init_interned("AFTER", sizeof("AFTER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_AFTER_name, &const_AFTER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_AFTER_name); zval const_LEFT_value; zend_string *const_LEFT_value_str = zend_string_init("left", strlen("left"), 1); ZVAL_STR(&const_LEFT_value, const_LEFT_value_str); zend_string *const_LEFT_name = zend_string_init_interned("LEFT", sizeof("LEFT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_LEFT_name, &const_LEFT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_LEFT_name); zval const_RIGHT_value; zend_string *const_RIGHT_value_str = zend_string_init("right", strlen("right"), 1); ZVAL_STR(&const_RIGHT_value, const_RIGHT_value_str); zend_string *const_RIGHT_name = zend_string_init_interned("RIGHT", sizeof("RIGHT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_RIGHT_name, &const_RIGHT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_RIGHT_name); zval const_OPT_MAX_RETRIES_value; ZVAL_LONG(&const_OPT_MAX_RETRIES_value, REDIS_OPT_MAX_RETRIES); zend_string *const_OPT_MAX_RETRIES_name = zend_string_init_interned("OPT_MAX_RETRIES", sizeof("OPT_MAX_RETRIES") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_MAX_RETRIES_name, &const_OPT_MAX_RETRIES_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_MAX_RETRIES_name); zval const_OPT_BACKOFF_ALGORITHM_value; ZVAL_LONG(&const_OPT_BACKOFF_ALGORITHM_value, REDIS_OPT_BACKOFF_ALGORITHM); zend_string *const_OPT_BACKOFF_ALGORITHM_name = zend_string_init_interned("OPT_BACKOFF_ALGORITHM", sizeof("OPT_BACKOFF_ALGORITHM") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_ALGORITHM_name, &const_OPT_BACKOFF_ALGORITHM_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_BACKOFF_ALGORITHM_name); zval const_BACKOFF_ALGORITHM_DEFAULT_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_DEFAULT_value, REDIS_BACKOFF_ALGORITHM_DEFAULT); zend_string *const_BACKOFF_ALGORITHM_DEFAULT_name = zend_string_init_interned("BACKOFF_ALGORITHM_DEFAULT", sizeof("BACKOFF_ALGORITHM_DEFAULT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_DEFAULT_name, &const_BACKOFF_ALGORITHM_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_DEFAULT_name); zval const_BACKOFF_ALGORITHM_CONSTANT_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_CONSTANT_value, REDIS_BACKOFF_ALGORITHM_CONSTANT); zend_string *const_BACKOFF_ALGORITHM_CONSTANT_name = zend_string_init_interned("BACKOFF_ALGORITHM_CONSTANT", sizeof("BACKOFF_ALGORITHM_CONSTANT") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_CONSTANT_name, &const_BACKOFF_ALGORITHM_CONSTANT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_CONSTANT_name); zval const_BACKOFF_ALGORITHM_UNIFORM_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_UNIFORM_value, REDIS_BACKOFF_ALGORITHM_UNIFORM); zend_string *const_BACKOFF_ALGORITHM_UNIFORM_name = zend_string_init_interned("BACKOFF_ALGORITHM_UNIFORM", sizeof("BACKOFF_ALGORITHM_UNIFORM") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_UNIFORM_name, &const_BACKOFF_ALGORITHM_UNIFORM_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_UNIFORM_name); zval const_BACKOFF_ALGORITHM_EXPONENTIAL_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_EXPONENTIAL_value, REDIS_BACKOFF_ALGORITHM_EXPONENTIAL); zend_string *const_BACKOFF_ALGORITHM_EXPONENTIAL_name = zend_string_init_interned("BACKOFF_ALGORITHM_EXPONENTIAL", sizeof("BACKOFF_ALGORITHM_EXPONENTIAL") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_EXPONENTIAL_name, &const_BACKOFF_ALGORITHM_EXPONENTIAL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_EXPONENTIAL_name); zval const_BACKOFF_ALGORITHM_FULL_JITTER_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_FULL_JITTER_value, REDIS_BACKOFF_ALGORITHM_FULL_JITTER); zend_string *const_BACKOFF_ALGORITHM_FULL_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_FULL_JITTER", sizeof("BACKOFF_ALGORITHM_FULL_JITTER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_FULL_JITTER_name, &const_BACKOFF_ALGORITHM_FULL_JITTER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_FULL_JITTER_name); zval const_BACKOFF_ALGORITHM_EQUAL_JITTER_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_EQUAL_JITTER_value, REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER); zend_string *const_BACKOFF_ALGORITHM_EQUAL_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_EQUAL_JITTER", sizeof("BACKOFF_ALGORITHM_EQUAL_JITTER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_EQUAL_JITTER_name, &const_BACKOFF_ALGORITHM_EQUAL_JITTER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_EQUAL_JITTER_name); zval const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value; ZVAL_LONG(&const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value, REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER); zend_string *const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_DECORRELATED_JITTER", sizeof("BACKOFF_ALGORITHM_DECORRELATED_JITTER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name, &const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name); zval const_OPT_BACKOFF_BASE_value; ZVAL_LONG(&const_OPT_BACKOFF_BASE_value, REDIS_OPT_BACKOFF_BASE); zend_string *const_OPT_BACKOFF_BASE_name = zend_string_init_interned("OPT_BACKOFF_BASE", sizeof("OPT_BACKOFF_BASE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_BASE_name, &const_OPT_BACKOFF_BASE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_BACKOFF_BASE_name); zval const_OPT_BACKOFF_CAP_value; ZVAL_LONG(&const_OPT_BACKOFF_CAP_value, REDIS_OPT_BACKOFF_CAP); zend_string *const_OPT_BACKOFF_CAP_name = zend_string_init_interned("OPT_BACKOFF_CAP", sizeof("OPT_BACKOFF_CAP") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_CAP_name, &const_OPT_BACKOFF_CAP_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_BACKOFF_CAP_name); return class_entry; } static zend_class_entry *register_class_RedisException(zend_class_entry *class_entry_RuntimeException) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisException", class_RedisException_methods); class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException); return class_entry; } redis-6.0.2/redis_array.c0000644000175000000120000010747614515245367016107 0ustar pyatsukhnenkowheel/* +----------------------------------------------------------------------+ | 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. | +----------------------------------------------------------------------+ | Author: Nicolas Favre-Felix | | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "common.h" #include "library.h" #include "redis_array.h" #include "redis_array_impl.h" #include #include /* Simple macro to detect failure in a RedisArray call */ #define RA_CALL_FAILED(rv, cmd) ( \ (Z_TYPE_P(rv) == IS_FALSE) || \ (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \ (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE")) \ ) extern zend_class_entry *redis_ce; zend_class_entry *redis_array_ce; #if PHP_VERSION_ID < 80000 #include "redis_array_legacy_arginfo.h" #else #include "zend_attributes.h" #include "redis_array_arginfo.h" #endif PHP_MINIT_FUNCTION(redis_array) { /* RedisSentinel class */ redis_array_ce = register_class_RedisArray(); redis_array_ce->create_object = create_redis_array_object; return SUCCESS; } static void redis_array_free(RedisArray *ra) { int i; /* continuum */ if (ra->continuum) { efree(ra->continuum->points); efree(ra->continuum); } /* Redis objects */ for(i = 0; i< ra->count; i++) { zval_dtor(&ra->redis[i]); zend_string_release(ra->hosts[i]); } efree(ra->redis); efree(ra->hosts); /* delete hash function */ zval_dtor(&ra->z_fun); /* Distributor */ zval_dtor(&ra->z_dist); /* Hashing algorithm */ if (ra->algorithm) zend_string_release(ra->algorithm); /* Delete pur commands */ zend_hash_destroy(ra->pure_cmds); FREE_HASHTABLE(ra->pure_cmds); /* Free structure itself */ efree(ra); } typedef struct { RedisArray *ra; zend_object std; } redis_array_object; zend_object_handlers redis_array_object_handlers; void free_redis_array_object(zend_object *object) { redis_array_object *obj = PHPREDIS_GET_OBJECT(redis_array_object, object); if (obj->ra) { if (obj->ra->prev) redis_array_free(obj->ra->prev); redis_array_free(obj->ra); } zend_object_std_dtor(&obj->std); } zend_object * create_redis_array_object(zend_class_entry *ce) { redis_array_object *obj = ecalloc(1, sizeof(redis_array_object) + zend_object_properties_size(ce)); obj->ra = NULL; zend_object_std_init(&obj->std, ce); object_properties_init(&obj->std, ce); memcpy(&redis_array_object_handlers, zend_get_std_object_handlers(), sizeof(redis_array_object_handlers)); redis_array_object_handlers.offset = XtOffsetOf(redis_array_object, std); redis_array_object_handlers.free_obj = free_redis_array_object; obj->std.handlers = &redis_array_object_handlers; return &obj->std; } /** * redis_array_get */ PHP_REDIS_API RedisArray * redis_array_get(zval *id) { redis_array_object *obj; if (Z_TYPE_P(id) == IS_OBJECT) { obj = PHPREDIS_ZVAL_GET_OBJECT(redis_array_object, id); return obj->ra; } return NULL; } /* {{{ proto RedisArray RedisArray::__construct() Public constructor */ PHP_METHOD(RedisArray, __construct) { zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; HashTable *hPrev = NULL, *hOpts = NULL; zend_long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0, read_timeout = 0.0; zend_string *algorithm = NULL, *user = NULL, *pass = NULL; redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|a", &z0, &z_opts) == FAILURE) { RETURN_FALSE; } /* Bail if z0 isn't a string or an array. * Note: WRONG_PARAM_COUNT seems wrong but this is what we have been doing * for ages so we can't really change it until the next major version. */ if (Z_TYPE_P(z0) != IS_ARRAY && Z_TYPE_P(z0) != IS_STRING) { #if PHP_VERSION_ID < 80000 WRONG_PARAM_COUNT; #else zend_argument_type_error(1, "must be of type string|array, %s given", zend_zval_type_name(z0)); RETURN_THROWS(); #endif } /* If it's a string we want to load the array from ini information */ if (Z_TYPE_P(z0) == IS_STRING) { ra = ra_load_array(Z_STRVAL_P(z0)); goto finish; } ZVAL_NULL(&z_fun); ZVAL_NULL(&z_dist); /* extract options */ if(z_opts) { hOpts = Z_ARRVAL_P(z_opts); /* extract previous ring. */ zpData = REDIS_HASH_STR_FIND_STATIC(hOpts, "previous"); if (zpData && Z_TYPE_P(zpData) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(zpData)) > 0) { hPrev = Z_ARRVAL_P(zpData); } REDIS_CONF_AUTH_STATIC(hOpts, "auth", &user, &pass); REDIS_CONF_ZVAL_STATIC(hOpts, "function", &z_fun, 1, 0); REDIS_CONF_ZVAL_STATIC(hOpts, "distributor", &z_dist, 1, 0); REDIS_CONF_STRING_STATIC(hOpts, "algorithm", &algorithm); REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "index", &b_index); REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "autorehash", &b_autorehash); REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "pconnect", &b_pconnect); REDIS_CONF_LONG_STATIC(hOpts, "retry_interval", &l_retry_interval); REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "lazy_connect", &b_lazy_connect); REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "consistent", &consistent); REDIS_CONF_DOUBLE_STATIC(hOpts, "connect_timeout", &d_connect_timeout); REDIS_CONF_DOUBLE_STATIC(hOpts, "read_timeout", &read_timeout); } ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, user, pass); if (algorithm) zend_string_release(algorithm); if (user) zend_string_release(user); if (pass) zend_string_release(pass); zval_dtor(&z_dist); zval_dtor(&z_fun); finish: if(ra) { ra->auto_rehash = b_autorehash; ra->connect_timeout = d_connect_timeout; if(ra->prev) ra->prev->auto_rehash = b_autorehash; obj = PHPREDIS_ZVAL_GET_OBJECT(redis_array_object, getThis()); obj->ra = ra; } } static void ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, int cmd_len, zval *z_args, zval *z_new_target) { zval z_fun, *redis_inst, *z_callargs, *zp_tmp; char *key = NULL; /* set to avoid "unused-but-set-variable" */ int i, key_len = 0, argc; HashTable *h_args; zend_bool b_write_cmd = 0; h_args = Z_ARRVAL_P(z_args); if ((argc = zend_hash_num_elements(h_args)) == 0) { RETURN_FALSE; } if(ra->z_multi_exec) { redis_inst = ra->z_multi_exec; /* we already have the instance */ } else { /* extract key and hash it. */ if ((zp_tmp = zend_hash_index_find(h_args, 0)) == NULL || Z_TYPE_P(zp_tmp) != IS_STRING) { php_error_docref(NULL, E_ERROR, "Could not find key"); RETURN_FALSE; } key = Z_STRVAL_P(zp_tmp); key_len = Z_STRLEN_P(zp_tmp); /* find node */ redis_inst = ra_find_node(ra, key, key_len, NULL); if(!redis_inst) { php_error_docref(NULL, E_ERROR, "Could not find any redis servers for this key."); RETURN_FALSE; } } /* pass call through */ ZVAL_STRINGL(&z_fun, cmd, cmd_len); /* method name */ z_callargs = ecalloc(argc, sizeof(*z_callargs)); /* copy args to array */ i = 0; ZEND_HASH_FOREACH_VAL(h_args, zp_tmp) { ZVAL_ZVAL(&z_callargs[i], zp_tmp, 1, 0); i++; } ZEND_HASH_FOREACH_END(); /* multi/exec */ if(ra->z_multi_exec) { call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); zval_dtor(return_value); zval_dtor(&z_fun); for (i = 0; i < argc; ++i) { zval_dtor(&z_callargs[i]); } efree(z_callargs); RETURN_ZVAL(getThis(), 1, 0); } /* check if write cmd */ b_write_cmd = ra_is_write_cmd(ra, cmd, cmd_len); /* CALL! */ if(ra->index && b_write_cmd) { /* add MULTI + SADD */ ra_index_multi(redis_inst, MULTI); /* call using discarded temp value and extract exec results after. */ call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); zval_dtor(return_value); /* add keys to index. */ ra_index_key(key, key_len, redis_inst); /* call EXEC */ ra_index_exec(redis_inst, return_value, 0); } else { /* call directly through. */ call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); if (!b_write_cmd) { /* check if we have an error. */ if (ra->prev && RA_CALL_FAILED(return_value, cmd)) { /* there was an error reading, try with prev ring. */ /* Free previous return value */ zval_dtor(return_value); /* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */ ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra->prev, cmd, cmd_len, z_args, z_new_target ? z_new_target : redis_inst); } /* Autorehash if the key was found on the previous node if this is a read command and auto rehashing is on */ if (ra->auto_rehash && z_new_target && !RA_CALL_FAILED(return_value, cmd)) { /* move key from old ring to new ring */ ra_move_key(key, key_len, redis_inst, z_new_target); } } } /* cleanup */ zval_dtor(&z_fun); for (i = 0; i < argc; ++i) { zval_dtor(&z_callargs[i]); } efree(z_callargs); } PHP_METHOD(RedisArray, __call) { zval *object; RedisArray *ra; zval *z_args; char *cmd; size_t cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Osa", &object, redis_array_ce, &cmd, &cmd_len, &z_args) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmd_len, z_args, NULL); } PHP_METHOD(RedisArray, _hosts) { zval *object; int i; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } array_init(return_value); for(i = 0; i < ra->count; ++i) { add_next_index_stringl(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i])); } } PHP_METHOD(RedisArray, _target) { zval *object; RedisArray *ra; char *key; size_t key_len; zval *redis_inst; int i; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, redis_array_ce, &key, &key_len) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } redis_inst = ra_find_node(ra, key, key_len, &i); if(redis_inst) { RETURN_STRINGL(ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i])); } else { RETURN_NULL(); } } PHP_METHOD(RedisArray, _instance) { zval *object; RedisArray *ra; zend_string *host; zval *z_redis; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS", &object, redis_array_ce, &host) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } if ((z_redis = ra_find_node_by_name(ra, host)) == NULL) { RETURN_NULL(); } RETURN_ZVAL(z_redis, 1, 0); } PHP_METHOD(RedisArray, _function) { zval *object; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } RETURN_ZVAL(&ra->z_fun, 1, 0); } PHP_METHOD(RedisArray, _distributor) { zval *object; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } RETURN_ZVAL(&ra->z_dist, 1, 0); } PHP_METHOD(RedisArray, _rehash) { zval *object; RedisArray *ra; zend_fcall_info z_cb = {0}; zend_fcall_info_cache z_cb_cache = {0}; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|f", &object, redis_array_ce, &z_cb, &z_cb_cache) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } if (ZEND_NUM_ARGS() == 0) { ra_rehash(ra, NULL, NULL); } else { ra_rehash(ra, &z_cb, &z_cb_cache); } } PHP_METHOD(RedisArray, _continuum) { int i; zval *object, z_ret; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } array_init(return_value); if (ra->continuum) { for (i = 0; i < ra->continuum->nb_points; ++i) { array_init(&z_ret); add_assoc_long(&z_ret, "index", ra->continuum->points[i].index); add_assoc_long(&z_ret, "value", ra->continuum->points[i].value); add_next_index_zval(return_value, &z_ret); } } } static void multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int argc, zval *argv) { zval z_tmp; int i; /* Init our array return */ array_init(return_value); /* Iterate our RedisArray nodes */ for (i = 0; i < ra->count; ++i) { /* Call each node in turn */ call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_tmp, argc, argv); /* Add the result for this host */ add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), &z_tmp); } } static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) { zval *object, z_fun; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* prepare call */ ZVAL_STRING(&z_fun, method_name); multihost_distribute_call(ra, return_value, &z_fun, 0, NULL); zval_dtor(&z_fun); } static void multihost_distribute_flush(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) { zval *object, z_fun, z_args[1]; zend_bool async = 0; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|b", &object, redis_array_ce, &async) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* prepare call */ ZVAL_STRING(&z_fun, method_name); ZVAL_BOOL(&z_args[0], async); multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); zval_dtor(&z_fun); } PHP_METHOD(RedisArray, info) { multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INFO"); } PHP_METHOD(RedisArray, ping) { multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PING"); } PHP_METHOD(RedisArray, flushdb) { multihost_distribute_flush(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB"); } PHP_METHOD(RedisArray, flushall) { multihost_distribute_flush(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL"); } PHP_METHOD(RedisArray, save) { multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE"); } PHP_METHOD(RedisArray, bgsave) { multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE"); } PHP_METHOD(RedisArray, keys) { zval *object, z_fun, z_args[1]; RedisArray *ra; char *pattern; size_t pattern_len; /* Make sure the prototype is correct */ if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, redis_array_ce, &pattern, &pattern_len) == FAILURE) { RETURN_FALSE; } /* Make sure we can grab our RedisArray object */ if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* Set up our function call (KEYS) */ ZVAL_STRINGL(&z_fun, "KEYS", 4); /* We will be passing with one string argument (the pattern) */ ZVAL_STRINGL(z_args, pattern, pattern_len); multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); zval_dtor(&z_args[0]); zval_dtor(&z_fun); } PHP_METHOD(RedisArray, getOption) { zval *object, z_fun, z_args[1]; RedisArray *ra; zend_long opt; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, redis_array_ce, &opt) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* prepare call */ ZVAL_STRINGL(&z_fun, "getOption", 9); /* copy arg */ ZVAL_LONG(&z_args[0], opt); multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); zval_dtor(&z_fun); } PHP_METHOD(RedisArray, setOption) { zval *object, z_fun, z_args[2]; RedisArray *ra; zend_long opt; char *val_str; size_t val_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ols", &object, redis_array_ce, &opt, &val_str, &val_len) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* prepare call */ ZVAL_STRINGL(&z_fun, "setOption", 9); /* copy args */ ZVAL_LONG(&z_args[0], opt); ZVAL_STRINGL(&z_args[1], val_str, val_len); multihost_distribute_call(ra, return_value, &z_fun, 2, z_args); zval_dtor(&z_args[1]); zval_dtor(&z_fun); } PHP_METHOD(RedisArray, select) { zval *object, z_fun, z_args[1]; RedisArray *ra; zend_long opt; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, redis_array_ce, &opt) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* prepare call */ ZVAL_STRINGL(&z_fun, "select", 6); /* copy args */ ZVAL_LONG(&z_args[0], opt); multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); zval_dtor(&z_fun); } #define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \ if (ra && ra->z_multi_exec) { \ int i, num_varargs; \ zval *varargs = NULL, z_arg_array; \ if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O*", \ &object, redis_array_ce, &varargs, &num_varargs) == FAILURE) { \ RETURN_FALSE;\ } \ /* copy all args into a zval hash table */\ array_init(&z_arg_array); \ for (i = 0; i < num_varargs; i++) { \ zval z_tmp; \ ZVAL_ZVAL(&z_tmp, &varargs[i], 1, 0); \ add_next_index_zval(&z_arg_array, &z_tmp); \ } \ /* call */\ ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, &z_arg_array, NULL); \ zval_dtor(&z_arg_array); \ return; \ } \ } while(0) /* MGET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mget) { zval *object, *z_keys, *data, z_ret, *z_cur, z_tmp_array, z_fun, z_arg, **argv; int i, j, n, *pos, argc, *argc_each; HashTable *h_keys; RedisArray *ra; if ((ra = redis_array_get(getThis())) == NULL) { RETURN_FALSE; } /* Multi/exec support */ HANDLE_MULTI_EXEC(ra, "MGET", sizeof("MGET") - 1); if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", &object, redis_array_ce, &z_keys) == FAILURE) { RETURN_FALSE; } /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); if ((argc = zend_hash_num_elements(h_keys)) == 0) { RETURN_FALSE; } argv = ecalloc(argc, sizeof(*argv)); pos = ecalloc(argc, sizeof(*pos)); argc_each = ecalloc(ra->count, sizeof(*argc_each)); /* associate each key to a redis node */ i = 0; ZEND_HASH_FOREACH_VAL(h_keys, data) { /* If we need to represent a long key as a string */ unsigned int key_len; char kbuf[40], *key_lookup; /* Handle the possibility that we're a reference */ ZVAL_DEREF(data); /* Convert to a string for hash lookup if it isn't one */ if (Z_TYPE_P(data) == IS_STRING) { key_len = Z_STRLEN_P(data); key_lookup = Z_STRVAL_P(data); } else if (Z_TYPE_P(data) == IS_LONG) { key_len = snprintf(kbuf, sizeof(kbuf), ZEND_LONG_FMT, Z_LVAL_P(data)); key_lookup = (char*)kbuf; } else { /* phpredis proper can only use string or long keys, so restrict to that here */ php_error_docref(NULL, E_ERROR, "MGET: all keys must be strings or longs"); RETVAL_FALSE; goto cleanup; } /* Find our node */ if (ra_find_node(ra, key_lookup, key_len, &pos[i]) == NULL) { RETVAL_FALSE; goto cleanup; } argc_each[pos[i]]++; /* count number of keys per node */ argv[i++] = data; } ZEND_HASH_FOREACH_END(); /* prepare call */ array_init(&z_tmp_array); ZVAL_STRINGL(&z_fun, "MGET", sizeof("MGET") - 1); /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ /* We don't even need to make a call to this node if no keys go there */ if(!argc_each[n]) continue; /* copy args for MGET call on node. */ array_init(&z_arg); for(i = 0; i < argc; ++i) { if (pos[i] == n) { ZVAL_ZVAL(&z_ret, argv[i], 1, 0); add_next_index_zval(&z_arg, &z_ret); } } /* call MGET on the node */ call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_arg); /* cleanup args array */ zval_dtor(&z_arg); /* Error out if we didn't get a proper response */ if (Z_TYPE(z_ret) != IS_ARRAY) { /* cleanup */ zval_dtor(&z_ret); zval_dtor(&z_tmp_array); RETVAL_FALSE; goto cleanup; } for(i = 0, j = 0; i < argc; ++i) { if (pos[i] != n || (z_cur = zend_hash_index_find(Z_ARRVAL(z_ret), j++)) == NULL) continue; ZVAL_ZVAL(&z_arg, z_cur, 1, 0); add_index_zval(&z_tmp_array, i, &z_arg); } zval_dtor(&z_ret); } zval_dtor(&z_fun); array_init(return_value); /* copy temp array in the right order to return_value */ for(i = 0; i < argc; ++i) { if ((z_cur = zend_hash_index_find(Z_ARRVAL(z_tmp_array), i)) == NULL) continue; ZVAL_ZVAL(&z_arg, z_cur, 1, 0); add_next_index_zval(return_value, &z_arg); } /* cleanup */ zval_dtor(&z_tmp_array); cleanup: efree(argv); efree(pos); efree(argc_each); } /* MSET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mset) { zval *object, *z_keys, z_argarray, *data, z_fun, z_ret, **argv; int i = 0, n, *pos, argc, *argc_each, key_len; RedisArray *ra; HashTable *h_keys; char *key, kbuf[40]; zend_string **keys, *zkey; zend_ulong idx; if ((ra = redis_array_get(getThis())) == NULL) { RETURN_FALSE; } /* Multi/exec support */ HANDLE_MULTI_EXEC(ra, "MSET", sizeof("MSET") - 1); if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", &object, redis_array_ce, &z_keys) == FAILURE) { RETURN_FALSE; } /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); if ((argc = zend_hash_num_elements(h_keys)) == 0) { RETURN_FALSE; } argv = ecalloc(argc, sizeof(*argv)); pos = ecalloc(argc, sizeof(*pos)); keys = ecalloc(argc, sizeof(*keys)); argc_each = ecalloc(ra->count, sizeof(*argc_each)); /* associate each key to a redis node */ ZEND_HASH_FOREACH_KEY_VAL(h_keys, idx, zkey, data) { /* If the key isn't a string, make a string representation of it */ if (zkey) { key_len = ZSTR_LEN(zkey); key = ZSTR_VAL(zkey); } else { key_len = snprintf(kbuf, sizeof(kbuf), ZEND_ULONG_FMT, idx); key = kbuf; } if (ra_find_node(ra, key, (int)key_len, &pos[i]) == NULL) { for (n = 0; n < i; ++n) { zend_string_release(keys[n]); } efree(keys); efree(argv); efree(pos); efree(argc_each); RETURN_FALSE; } argc_each[pos[i]]++; /* count number of keys per node */ keys[i] = zkey ? zend_string_copy(zkey) : zend_string_init(key, key_len, 0); argv[i] = data; i++; } ZEND_HASH_FOREACH_END(); /* prepare call */ ZVAL_STRINGL(&z_fun, "MSET", sizeof("MSET") - 1); /* calls */ for (n = 0; n < ra->count; ++n) { /* for each node */ /* We don't even need to make a call to this node if no keys go there */ if(!argc_each[n]) continue; int found = 0; /* copy args */ array_init(&z_argarray); for(i = 0; i < argc; ++i) { if(pos[i] != n) continue; if (argv[i] == NULL) { ZVAL_NULL(&z_ret); } else { ZVAL_ZVAL(&z_ret, argv[i], 1, 0); } add_assoc_zval_ex(&z_argarray, ZSTR_VAL(keys[i]), ZSTR_LEN(keys[i]), &z_ret); found++; } if(!found) { zval_dtor(&z_argarray); continue; /* don't run empty MSETs */ } if(ra->index) { /* add MULTI */ ra_index_multi(&ra->redis[n], MULTI); call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); ra_index_keys(&z_argarray, &ra->redis[n]); /* use SADD to add keys to node index */ ra_index_exec(&ra->redis[n], NULL, 0); /* run EXEC */ } else { call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); } zval_dtor(&z_argarray); zval_dtor(&z_ret); } zval_dtor(&z_fun); /* Free any keys that we needed to allocate memory for, because they weren't strings */ for(i = 0; i < argc; i++) { zend_string_release(keys[i]); } /* cleanup */ efree(keys); efree(argv); efree(pos); efree(argc_each); RETURN_TRUE; } /* Generic handler for DEL or UNLINK which behave identically to phpredis */ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { zval *object, z_keys, z_fun, *data, z_ret, *z_args, **argv; int i, n, *pos, argc = ZEND_NUM_ARGS(), *argc_each, free_zkeys = 0; HashTable *h_keys; RedisArray *ra; long total = 0; if ((ra = redis_array_get(getThis())) == NULL) { RETURN_FALSE; } /* Multi/exec support */ HANDLE_MULTI_EXEC(ra, kw, kw_len); /* get all args in z_args */ z_args = ecalloc(argc, sizeof(*z_args)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); RETURN_FALSE; } /* if single array arg, point z_keys to it. */ if (argc == 1 && Z_TYPE(z_args[0]) == IS_ARRAY) { z_keys = z_args[0]; } else { /* copy all elements to z_keys */ array_init(&z_keys); for (i = 0; i < argc; ++i) { ZVAL_ZVAL(&z_ret, &z_args[i], 1, 0); add_next_index_zval(&z_keys, &z_ret); } free_zkeys = 1; } /* init data structures */ h_keys = Z_ARRVAL(z_keys); if ((argc = zend_hash_num_elements(h_keys)) == 0) { if (free_zkeys) zval_dtor(&z_keys); efree(z_args); RETURN_FALSE; } argv = ecalloc(argc, sizeof(*argv)); pos = ecalloc(argc, sizeof(*pos)); argc_each = ecalloc(ra->count, sizeof(*argc_each)); /* associate each key to a redis node */ i = 0; ZEND_HASH_FOREACH_VAL(h_keys, data) { if (Z_TYPE_P(data) != IS_STRING) { php_error_docref(NULL, E_ERROR, "DEL: all keys must be string."); RETVAL_FALSE; goto cleanup; } if (ra_find_node(ra, Z_STRVAL_P(data), Z_STRLEN_P(data), &pos[i]) == NULL) { RETVAL_FALSE; goto cleanup; } argc_each[pos[i]]++; /* count number of keys per node */ argv[i++] = data; } ZEND_HASH_FOREACH_END(); /* prepare call */ ZVAL_STRINGL(&z_fun, kw, kw_len); /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ /* We don't even need to make a call to this node if no keys go there */ if(!argc_each[n]) continue; int found = 0; zval z_argarray; /* copy args */ array_init(&z_argarray); for(i = 0; i < argc; ++i) { if (pos[i] == n) { ZVAL_ZVAL(&z_ret, argv[i], 1, 0); add_next_index_zval(&z_argarray, &z_ret); found++; } } if(!found) { /* don't run empty DEL or UNLINK commands */ zval_dtor(&z_argarray); continue; } if(ra->index) { /* add MULTI */ ra_index_multi(&ra->redis[n], MULTI); call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); ra_index_del(&z_argarray, &ra->redis[n]); /* use SREM to remove keys from node index */ ra_index_exec(&ra->redis[n], &z_ret, 0); /* run EXEC */ } else { call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); } total += Z_LVAL(z_ret); /* increment total */ zval_dtor(&z_argarray); zval_dtor(&z_ret); } zval_dtor(&z_fun); RETVAL_LONG(total); cleanup: efree(argv); efree(pos); efree(argc_each); if(free_zkeys) { zval_dtor(&z_keys); } efree(z_args); } /* DEL will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, del) { ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL")-1); } PHP_METHOD(RedisArray, unlink) { ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1); } static void ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, int kw_len) { RedisArray *ra; zend_string *key, *pattern = NULL; zval *object, *redis_inst, *z_iter, z_fun, z_args[4]; zend_long count = 0; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OSz/|S!l", &object, redis_array_ce, &key, &z_iter, &pattern, &count) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } if ((redis_inst = ra_find_node(ra, ZSTR_VAL(key), ZSTR_LEN(key), NULL)) == NULL) { php_error_docref(NULL, E_ERROR, "Could not find any redis servers for this key."); RETURN_FALSE; } ZVAL_STR(&z_args[0], key); ZVAL_NEW_REF(&z_args[1], z_iter); if (pattern) ZVAL_STR(&z_args[2], pattern); ZVAL_LONG(&z_args[3], count); ZVAL_STRINGL(&z_fun, kw, kw_len); call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, ZEND_NUM_ARGS(), z_args); zval_dtor(&z_fun); ZVAL_ZVAL(z_iter, &z_args[1], 0, 1); } PHP_METHOD(RedisArray, hscan) { ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HSCAN", sizeof("HSCAN") - 1); } PHP_METHOD(RedisArray, sscan) { ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SSCAN", sizeof("SSCAN") - 1); } PHP_METHOD(RedisArray, zscan) { ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZSCAN", sizeof("ZSCAN") - 1); } PHP_METHOD(RedisArray, scan) { RedisArray *ra; zend_string *host, *pattern = NULL; zval *object, *redis_inst, *z_iter, z_fun, z_args[3]; zend_long count = 0; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oz/S|S!l", &object, redis_array_ce, &z_iter, &host, &pattern, &count) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } if ((redis_inst = ra_find_node_by_name(ra, host)) == NULL) { RETURN_FALSE; } ZVAL_NEW_REF(&z_args[0], z_iter); if (pattern) ZVAL_STR(&z_args[1], pattern); ZVAL_LONG(&z_args[2], count); ZVAL_STRING(&z_fun, "SCAN"); call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, ZEND_NUM_ARGS() - 1, z_args); zval_dtor(&z_fun); ZVAL_ZVAL(z_iter, &z_args[0], 0, 1); } PHP_METHOD(RedisArray, multi) { zval *object; RedisArray *ra; zval *z_redis; zend_string *host; zend_long multi_value = MULTI; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS|l", &object, redis_array_ce, &host, &multi_value) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* find node */ if ((z_redis = ra_find_node_by_name(ra, host)) == NULL) { RETURN_FALSE; } if(multi_value != MULTI && multi_value != PIPELINE) { RETURN_FALSE; } /* save multi object */ ra->z_multi_exec = z_redis; /* switch redis instance to multi/exec mode. */ ra_index_multi(z_redis, multi_value); /* return this. */ RETURN_ZVAL(object, 1, 0); } PHP_METHOD(RedisArray, exec) { zval *object; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } /* switch redis instance out of multi/exec mode. */ ra_index_exec(ra->z_multi_exec, return_value, 1); /* remove multi object */ ra->z_multi_exec = NULL; } PHP_METHOD(RedisArray, discard) { zval *object; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } /* switch redis instance out of multi/exec mode. */ ra_index_discard(ra->z_multi_exec, return_value); /* remove multi object */ ra->z_multi_exec = NULL; } PHP_METHOD(RedisArray, unwatch) { zval *object; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } if ((ra = redis_array_get(object)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } /* unwatch keys, stay in multi/exec mode. */ ra_index_unwatch(ra->z_multi_exec, return_value); } redis-6.0.2/redis_array.h0000644000175000000120000000242714515245367016102 0ustar pyatsukhnenkowheel#ifndef REDIS_ARRAY_H #define REDIS_ARRAY_H #if (defined(_MSC_VER) && _MSC_VER <= 1920) #include "win32/php_stdint.h" #else #include #endif #include "common.h" typedef struct { uint32_t value; int index; } ContinuumPoint; typedef struct { size_t nb_points; ContinuumPoint *points; } Continuum; typedef struct RedisArray_ { int count; zend_string **hosts; /* array of host:port strings */ zval *redis; /* array of Redis instances */ zval *z_multi_exec; /* Redis instance to be used in multi-exec */ zend_bool index; /* use per-node index */ zend_bool auto_rehash; /* migrate keys on read operations */ zend_bool pconnect; /* should we use pconnect */ zval z_fun; /* key extractor, callable */ zval z_dist; /* key distributor, callable */ zend_string *algorithm; /* key hashing algorithm name */ HashTable *pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ double read_timeout; /* socket read timeout */ Continuum *continuum; struct RedisArray_ *prev; } RedisArray; extern zend_class_entry *redis_array_ce; extern PHP_MINIT_FUNCTION(redis_array); extern zend_object *create_redis_array_object(zend_class_entry *ce); #endif redis-6.0.2/redis_array.stub.php0000644000175000000120000000400214515245367017405 0ustar pyatsukhnenkowheel | | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ #include "redis_array_impl.h" #include "php_redis.h" #include "library.h" #include "php_variables.h" #include "SAPI.h" #include "ext/standard/url.h" #include "ext/standard/crc32.h" #include "ext/standard/md5.h" #include "ext/hash/php_hash.h" #define PHPREDIS_INDEX_NAME "__phpredis_array_index__" extern zend_class_entry *redis_ce; static RedisArray * ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *user, zend_string *pass, long retry_interval, zend_bool b_lazy_connect) { int i = 0, host_len; char *host, *p; short port; zval *zpData; redis_object *redis; /* init connections */ ZEND_HASH_FOREACH_VAL(hosts, zpData) { if (Z_TYPE_P(zpData) != IS_STRING) { return NULL; } /* default values */ host = Z_STRVAL_P(zpData); host_len = Z_STRLEN_P(zpData); ra->hosts[i] = zend_string_init(host, host_len, 0); port = 6379; if((p = strrchr(host, ':'))) { /* found port */ host_len = p - host; port = (short)atoi(p+1); } else if(strchr(host,'/') != NULL) { /* unix socket */ port = -1; } /* create Redis object */ object_init_ex(&ra->redis[i], redis_ce); redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, &ra->redis[i]); /* create socket */ redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval); redis_sock_set_auth(redis->sock, user, pass); if (!b_lazy_connect) { if (redis_sock_server_open(redis->sock) < 0) { ra->count = ++i; return NULL; } } ra->count = ++i; } ZEND_HASH_FOREACH_END(); return ra; } /* List pure functions */ void ra_init_function_table(RedisArray *ra) { ALLOC_HASHTABLE(ra->pure_cmds); zend_hash_init(ra->pure_cmds, 0, NULL, NULL, 0); zend_hash_str_update_ptr(ra->pure_cmds, "EXISTS", sizeof("EXISTS") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "GET", sizeof("GET") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "GETBIT", sizeof("GETBIT") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "GETRANGE", sizeof("GETRANGE") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "HEXISTS", sizeof("HEXISTS") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "HGET", sizeof("HGET") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "HGETALL", sizeof("HGETALL") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "HKEYS", sizeof("HKEYS") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "HLEN", sizeof("HLEN") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "HMGET", sizeof("HMGET") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "HVALS", sizeof("HVALS") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "LINDEX", sizeof("LINDEX") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "LLEN", sizeof("LLEN") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "LRANGE", sizeof("LRANGE") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "OBJECT", sizeof("OBJECT") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "SCARD", sizeof("SCARD") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "SDIFF", sizeof("SDIFF") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "SINTER", sizeof("SINTER") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "SISMEMBER", sizeof("SISMEMBER") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "SMEMBERS", sizeof("SMEMBERS") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "SRANDMEMBER", sizeof("SRANDMEMBER") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "STRLEN", sizeof("STRLEN") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "SUNION", sizeof("SUNION") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "TYPE", sizeof("TYPE") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "ZCARD", sizeof("ZCARD") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "ZCOUNT", sizeof("ZCOUNT") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "ZRANGE", sizeof("ZRANGE") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "ZRANK", sizeof("ZRANK") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANGE", sizeof("ZREVRANGE") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANGEBYSCORE", sizeof("ZREVRANGEBYSCORE") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANK", sizeof("ZREVRANK") - 1, NULL); zend_hash_str_update_ptr(ra->pure_cmds, "ZSCORE", sizeof("ZSCORE") - 1, NULL); } static int ra_find_name(const char *name) { const char *ini_names, *p, *next; /* php_printf("Loading redis array with name=[%s]\n", name); */ ini_names = INI_STR("redis.arrays.names"); for(p = ini_names; p;) { next = strchr(p, ','); if(next) { if(strncmp(p, name, next - p) == 0) { return 1; } } else { if(strcmp(p, name) == 0) { return 1; } break; } p = next + 1; } return 0; } /* load array from INI settings */ RedisArray *ra_load_array(const char *name) { zval *z_data, z_tmp, z_fun, z_dist; zval z_params_hosts; zval z_params_prev; RedisArray *ra = NULL; zend_string *algorithm = NULL, *user = NULL, *pass = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; zend_long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0, read_timeout = 0.0; HashTable *hHosts = NULL, *hPrev = NULL; size_t name_len = strlen(name); char *iptr; /* find entry */ if(!ra_find_name(name)) return ra; ZVAL_NULL(&z_fun); ZVAL_NULL(&z_dist); /* find hosts */ array_init(&z_params_hosts); if ((iptr = INI_STR("redis.arrays.hosts")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_hosts); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_hosts), name, name_len)) != NULL) { hHosts = Z_ARRVAL_P(z_data); } } /* find previous hosts */ array_init(&z_params_prev); if ((iptr = INI_STR("redis.arrays.previous")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_prev); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_prev), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_ARRAY) { hPrev = Z_ARRVAL_P(z_data); } } } /* find function */ if ((iptr = INI_STR("redis.arrays.functions")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zval(Z_ARRVAL(z_tmp), name, name_len, &z_fun, 1, 0); zval_dtor(&z_tmp); } /* find distributor */ if ((iptr = INI_STR("redis.arrays.distributor")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zval(Z_ARRVAL(z_tmp), name, name_len, &z_dist, 1, 0); zval_dtor(&z_tmp); } /* find hash algorithm */ if ((iptr = INI_STR("redis.arrays.algorithm")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_string(Z_ARRVAL(z_tmp), name, name_len, &algorithm); zval_dtor(&z_tmp); } /* find index option */ if ((iptr = INI_STR("redis.arrays.index")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_index); zval_dtor(&z_tmp); } /* find autorehash option */ if ((iptr = INI_STR("redis.arrays.autorehash")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_autorehash); zval_dtor(&z_tmp); } /* find retry interval option */ if ((iptr = INI_STR("redis.arrays.retryinterval")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_long(Z_ARRVAL(z_tmp), name, name_len, &l_retry_interval); zval_dtor(&z_tmp); } /* find pconnect option */ if ((iptr = INI_STR("redis.arrays.pconnect")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_pconnect); zval_dtor(&z_tmp); } /* find lazy connect option */ if ((iptr = INI_STR("redis.arrays.lazyconnect")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_lazy_connect); zval_dtor(&z_tmp); } /* find connect timeout option */ if ((iptr = INI_STR("redis.arrays.connecttimeout")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &d_connect_timeout); zval_dtor(&z_tmp); } /* find read timeout option */ if ((iptr = INI_STR("redis.arrays.readtimeout")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &read_timeout); zval_dtor(&z_tmp); } /* find consistent option */ if ((iptr = INI_STR("redis.arrays.consistent")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_tmp), name, name_len)) != NULL) { consistent = Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0; } zval_dtor(&z_tmp); } /* find auth option */ if ((iptr = INI_STR("redis.arrays.auth")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_auth(Z_ARRVAL(z_tmp), name, name_len, &user, &pass); zval_dtor(&z_tmp); } /* create RedisArray object */ ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, user, pass); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; } if (algorithm) zend_string_release(algorithm); if (user) zend_string_release(user); if (pass) zend_string_release(pass); zval_dtor(&z_params_hosts); zval_dtor(&z_params_prev); zval_dtor(&z_dist); zval_dtor(&z_fun); return ra; } static int ra_points_cmp(const void *v1, const void *v2) { const ContinuumPoint *p1 = v1, *p2 = v2; return p1->value < p2->value ? - 1 : p1->value > p2->value; } static Continuum * ra_make_continuum(zend_string **hosts, int nb_hosts) { int i, j, k, len, idx = 0; char host[HOST_NAME_MAX]; unsigned char digest[16]; PHP_MD5_CTX ctx; Continuum *c; c = ecalloc(1, sizeof(*c)); c->nb_points = nb_hosts * 160; /* 40 hashes, 4 numbers per hash = 160 points per server */ c->points = ecalloc(c->nb_points, sizeof(*c->points)); for (i = 0; i < nb_hosts; ++i) { for (j = 0; j < 40; ++j) { len = snprintf(host, sizeof(host), "%.*s-%u", (int)ZSTR_LEN(hosts[i]), ZSTR_VAL(hosts[i]), j); PHP_MD5Init(&ctx); PHP_MD5Update(&ctx, host, len); PHP_MD5Final(digest, &ctx); for (k = 0; k < 4; ++k) { c->points[idx].index = i; c->points[idx++].value = (digest[3 + k * 4] << 24) | (digest[2 + k * 4] << 16) | (digest[1 + k * 4] << 8) | (digest[k * 4]); } } } qsort(c->points, c->nb_points, sizeof(*c->points), ra_points_cmp); return c; } RedisArray * ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *user, zend_string *pass) { int i, count; RedisArray *ra; if (!hosts || (count = zend_hash_num_elements(hosts)) == 0) return NULL; /* create object */ ra = emalloc(sizeof(RedisArray)); ra->hosts = ecalloc(count, sizeof(*ra->hosts)); ra->redis = ecalloc(count, sizeof(*ra->redis)); ra->count = 0; ra->z_multi_exec = NULL; ra->index = b_index; ra->auto_rehash = 0; ra->pconnect = b_pconnect; ra->connect_timeout = connect_timeout; ra->read_timeout = read_timeout; ra->continuum = NULL; ra->algorithm = NULL; if (ra_load_hosts(ra, hosts, user, pass, retry_interval, b_lazy_connect) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { zval_dtor(&ra->redis[i]); zend_string_release(ra->hosts[i]); } efree(ra->redis); efree(ra->hosts); efree(ra); return NULL; } ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm, user, pass) : NULL; /* init array data structures */ ra_init_function_table(ra); /* Set hash function and distribtor if provided */ ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); ZVAL_ZVAL(&ra->z_dist, z_dist, 1, 0); if (algorithm) ra->algorithm = zend_string_copy(algorithm); /* init continuum */ if (consistent) { ra->continuum = ra_make_continuum(ra->hosts, ra->count); } return ra; } /* call userland key extraction function */ zend_string * ra_call_extractor(RedisArray *ra, const char *key, int key_len) { zend_string *out = NULL; zval z_ret, z_argv; /* check that we can call the extractor function */ if (!zend_is_callable_ex(&ra->z_fun, NULL, 0, NULL, NULL, NULL)) { php_error_docref(NULL, E_ERROR, "Could not call extractor function"); return NULL; } ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); if (Z_TYPE(z_ret) == IS_STRING) { out = zval_get_string(&z_ret); } zval_dtor(&z_argv); zval_dtor(&z_ret); return out; } static zend_string * ra_extract_key(RedisArray *ra, const char *key, int key_len) { char *start, *end; if (Z_TYPE(ra->z_fun) != IS_NULL) { return ra_call_extractor(ra, key, key_len); } else if ((start = strchr(key, '{')) == NULL || (end = strchr(start + 1, '}')) == NULL) { return zend_string_init(key, key_len, 0); } /* found substring */ return zend_string_init(start + 1, end - start - 1, 0); } /* call userland key distributor function */ int ra_call_distributor(RedisArray *ra, const char *key, int key_len) { int ret; zval z_ret, z_argv; /* check that we can call the extractor function */ if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL)) { php_error_docref(NULL, E_ERROR, "Could not call distributor function"); return -1; } ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv); ret = (Z_TYPE(z_ret) == IS_LONG) ? Z_LVAL(z_ret) : -1; zval_dtor(&z_argv); zval_dtor(&z_ret); return ret; } zval * ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos) { int pos; zend_string *out; /* extract relevant part of the key */ if ((out = ra_extract_key(ra, key, key_len)) == NULL) { return NULL; } if (Z_TYPE(ra->z_dist) == IS_NULL) { int i; unsigned long ret = 0xffffffff; const php_hash_ops *ops; /* hash */ if (ra->algorithm && (ops = redis_hash_fetch_ops(ra->algorithm))) { void *ctx = emalloc(ops->context_size); unsigned char *digest = emalloc(ops->digest_size); #if PHP_VERSION_ID >= 80100 ops->hash_init(ctx,NULL); #else ops->hash_init(ctx); #endif ops->hash_update(ctx, (const unsigned char *)ZSTR_VAL(out), ZSTR_LEN(out)); ops->hash_final(digest, ctx); memcpy(&ret, digest, MIN(sizeof(ret), ops->digest_size)); ret %= 0xffffffff; efree(digest); efree(ctx); } else { for (i = 0; i < ZSTR_LEN(out); ++i) { CRC32(ret, ZSTR_VAL(out)[i]); } } /* get position on ring */ if (ra->continuum) { int left = 0, right = ra->continuum->nb_points; while (left < right) { i = (int)((left + right) / 2); if (ra->continuum->points[i].value < ret) { left = i + 1; } else { right = i; } } if (right == ra->continuum->nb_points) { right = 0; } pos = ra->continuum->points[right].index; } else { pos = (int)((ret ^ 0xffffffff) * ra->count / 0xffffffff); } } else { pos = ra_call_distributor(ra, key, key_len); if (pos < 0 || pos >= ra->count) { zend_string_release(out); return NULL; } } zend_string_release(out); if(out_pos) *out_pos = pos; return &ra->redis[pos]; } zval * ra_find_node_by_name(RedisArray *ra, zend_string *host) { int i; for(i = 0; i < ra->count; ++i) { if (zend_string_equals(host, ra->hosts[i])) { return &ra->redis[i]; } } return NULL; } void ra_index_multi(zval *z_redis, long multi_value) { zval z_fun_multi, z_ret; zval z_args[1]; /* run MULTI */ ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); ZVAL_LONG(&z_args[0], multi_value); call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); zval_dtor(&z_fun_multi); zval_dtor(&z_ret); } static void ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis) { int i, argc; zval z_fun, z_ret, *z_args; /* alloc */ argc = 1 + zend_hash_num_elements(Z_ARRVAL_P(z_keys)); z_args = ecalloc(argc, sizeof(zval)); /* prepare first parameters */ ZVAL_STRING(&z_fun, cmd); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); /* prepare keys */ for(i = 0; i < argc - 1; ++i) { zval *zv = zend_hash_index_find(Z_ARRVAL_P(z_keys), i); if (zv == NULL) { ZVAL_NULL(&z_args[i+1]); } else { z_args[i+1] = *zv; } } /* run cmd */ call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); zval_dtor(&z_args[0]); zval_dtor(&z_fun); zval_dtor(&z_ret); efree(z_args); /* free container */ } void ra_index_del(zval *z_keys, zval *z_redis) { ra_index_change_keys("SREM", z_keys, z_redis); } void ra_index_keys(zval *z_pairs, zval *z_redis) { zval z_keys, *z_val; zend_string *zkey; zend_ulong idx; /* Initialize key array */ array_init_size(&z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); /* Go through input array and add values to the key array */ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_pairs), idx, zkey, z_val) { zval z_new; PHPREDIS_NOTUSED(z_val); if (zkey) { ZVAL_STRINGL(&z_new, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); } else { ZVAL_LONG(&z_new, idx); } zend_hash_next_index_insert(Z_ARRVAL(z_keys), &z_new); } ZEND_HASH_FOREACH_END(); /* add keys to index */ ra_index_change_keys("SADD", &z_keys, z_redis); /* cleanup */ zval_dtor(&z_keys); } void ra_index_key(const char *key, int key_len, zval *z_redis) { zval z_fun_sadd, z_ret, z_args[2]; /* prepare args */ ZVAL_STRINGL(&z_fun_sadd, "SADD", 4); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); ZVAL_STRINGL(&z_args[1], key, key_len); /* run SADD */ call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args); zval_dtor(&z_fun_sadd); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); zval_dtor(&z_ret); } void ra_index_exec(zval *z_redis, zval *return_value, int keep_all) { zval z_fun_exec, z_ret, *zp_tmp; /* run EXEC */ ZVAL_STRINGL(&z_fun_exec, "EXEC", 4); call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); zval_dtor(&z_fun_exec); /* extract first element of exec array and put into return_value. */ if(Z_TYPE(z_ret) == IS_ARRAY) { if(return_value) { if(keep_all) { zp_tmp = &z_ret; RETVAL_ZVAL(zp_tmp, 1, 0); } else if ((zp_tmp = zend_hash_index_find(Z_ARRVAL(z_ret), 0)) != NULL) { RETVAL_ZVAL(zp_tmp, 1, 0); } } } zval_dtor(&z_ret); /* zval *zptr = &z_ret; */ /* php_var_dump(&zptr, 0); */ } void ra_index_discard(zval *z_redis, zval *return_value) { zval z_fun_discard, z_ret; /* run DISCARD */ ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7); call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); zval_dtor(&z_fun_discard); zval_dtor(&z_ret); } void ra_index_unwatch(zval *z_redis, zval *return_value) { zval z_fun_unwatch, z_ret; /* run UNWATCH */ ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7); call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); zval_dtor(&z_fun_unwatch); zval_dtor(&z_ret); } zend_bool ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len) { zend_bool ret; int i; char *cmd_up = emalloc(1 + cmd_len); /* convert to uppercase */ for(i = 0; i < cmd_len; ++i) cmd_up[i] = toupper(cmd[i]); cmd_up[cmd_len] = 0; ret = zend_hash_str_exists(ra->pure_cmds, cmd_up, cmd_len); efree(cmd_up); return !ret; } /* run TYPE to find the type */ static zend_bool ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long *res) { int i = 0; zval z_fun, z_ret, z_arg, *z_data; long success = 1; /* Pipelined */ ra_index_multi(z_from, PIPELINE); /* prepare args */ ZVAL_STRINGL(&z_arg, key, key_len); /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TYPE", 4); call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); zval_dtor(&z_fun); zval_dtor(&z_ret); /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TTL", 3); call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); zval_dtor(&z_fun); zval_dtor(&z_ret); /* Get the result from the pipeline. */ ra_index_exec(z_from, &z_ret, 1); if (Z_TYPE(z_ret) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_ret), z_data) { if (z_data == NULL || Z_TYPE_P(z_data) != IS_LONG) { success = 0; break; } /* Get the result - Might change in the future to handle doubles as well */ res[i++] = Z_LVAL_P(z_data); } ZEND_HASH_FOREACH_END(); } zval_dtor(&z_arg); zval_dtor(&z_ret); return success; } /* delete key from source server index during rehashing */ static void ra_remove_from_index(zval *z_redis, const char *key, int key_len) { zval z_fun_srem, z_ret, z_args[2]; /* run SREM on source index */ ZVAL_STRINGL(&z_fun_srem, "SREM", 4); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); ZVAL_STRINGL(&z_args[1], key, key_len); call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args); /* cleanup */ zval_dtor(&z_fun_srem); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); zval_dtor(&z_ret); } /* delete key from source server during rehashing */ static zend_bool ra_del_key(const char *key, int key_len, zval *z_from) { zval z_fun_del, z_ret, z_args[1]; /* in a transaction */ ra_index_multi(z_from, MULTI); /* run DEL on source */ ZVAL_STRINGL(&z_fun_del, "DEL", 3); ZVAL_STRINGL(&z_args[0], key, key_len); call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); zval_dtor(&z_fun_del); zval_dtor(&z_args[0]); zval_dtor(&z_ret); /* remove key from index */ ra_remove_from_index(z_from, key, key_len); /* close transaction */ ra_index_exec(z_from, NULL, 0); return 1; } static zend_bool ra_expire_key(const char *key, int key_len, zval *z_to, long ttl) { zval z_fun_expire, z_ret, z_args[2]; if (ttl > 0) { /* run EXPIRE on target */ ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6); ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_LONG(&z_args[1], ttl); call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); zval_dtor(&z_fun_expire); zval_dtor(&z_args[0]); zval_dtor(&z_ret); } return 1; } static zend_bool ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { zval z_fun_zrange, z_fun_zadd, z_ret, z_ret_dest, z_args[4], *z_zadd_args, *z_score_p; int i, count; HashTable *h_zset_vals; zend_string *zkey; zend_ulong idx; /* run ZRANGE key 0 -1 WITHSCORES on source */ ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6); ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_args[1], "0", 1); ZVAL_STRINGL(&z_args[2], "-1", 2); ZVAL_BOOL(&z_args[3], 1); call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); zval_dtor(&z_fun_zrange); zval_dtor(&z_args[2]); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ zval_dtor(&z_ret); return 0; } /* we now have an array of value → score pairs in z_ret. */ h_zset_vals = Z_ARRVAL(z_ret); /* allocate argument array for ZADD */ count = zend_hash_num_elements(h_zset_vals); z_zadd_args = ecalloc((1 + 2*count), sizeof(zval)); ZVAL_STRINGL(&z_zadd_args[0], key, key_len); i = 1; ZEND_HASH_FOREACH_KEY_VAL(h_zset_vals, idx, zkey, z_score_p) { /* add score */ ZVAL_DOUBLE(&z_zadd_args[i], Z_DVAL_P(z_score_p)); /* add value */ if (zkey) { ZVAL_STRINGL(&z_zadd_args[i+1], ZSTR_VAL(zkey), ZSTR_LEN(zkey)); } else { ZVAL_LONG(&z_zadd_args[i+1], (long)idx); } i += 2; } ZEND_HASH_FOREACH_END(); /* run ZADD on target */ ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4); call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl); /* cleanup */ zval_dtor(&z_fun_zadd); zval_dtor(&z_ret_dest); zval_dtor(&z_ret); /* Free the array itself */ for (i = 0; i < 1 + 2 * count; i++) { zval_dtor(&z_zadd_args[i]); } efree(z_zadd_args); return 1; } static zend_bool ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { zval z_fun_get, z_fun_set, z_ret, z_args[3]; /* run GET on source */ ZVAL_STRINGL(&z_fun_get, "GET", 3); ZVAL_STRINGL(&z_args[0], key, key_len); call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args); zval_dtor(&z_fun_get); if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ /* TODO: report? */ zval_dtor(&z_args[0]); zval_dtor(&z_ret); return 0; } /* run SET on target */ if (ttl > 0) { ZVAL_STRINGL(&z_fun_set, "SETEX", 5); ZVAL_LONG(&z_args[1], ttl); ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous call */ call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args); /* cleanup */ zval_dtor(&z_args[2]); } else { ZVAL_STRINGL(&z_fun_set, "SET", 3); ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous return value */ call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args); /* cleanup */ zval_dtor(&z_args[1]); } zval_dtor(&z_fun_set); zval_dtor(&z_args[0]); zval_dtor(&z_ret); return 1; } static zend_bool ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { zval z_fun_hgetall, z_fun_hmset, z_ret_dest, z_args[2]; /* run HGETALL on source */ ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); zval_dtor(&z_fun_hgetall); if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); return 0; } /* run HMSET on target */ ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); zval_dtor(&z_fun_hmset); zval_dtor(&z_ret_dest); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl); /* cleanup */ zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); return 1; } static zend_bool ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, int list_count, const char **cmd_list, int add_count, const char **cmd_add, long ttl) { zval z_fun_retrieve, z_fun_sadd, z_ret, *z_retrieve_args, *z_sadd_args, *z_data_p; int count, i; HashTable *h_set_vals; /* run retrieval command on source */ ZVAL_STRING(&z_fun_retrieve, cmd_list[0]); /* set the command */ z_retrieve_args = ecalloc(list_count, sizeof(zval)); /* set the key */ ZVAL_STRINGL(&z_retrieve_args[0], key, key_len); /* possibly add some other args if they were provided. */ for(i = 1; i < list_count; ++i) { ZVAL_STRING(&z_retrieve_args[i], cmd_list[i]); } call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args); /* cleanup */ zval_dtor(&z_fun_retrieve); for(i = 0; i < list_count; ++i) { zval_dtor(&z_retrieve_args[i]); } efree(z_retrieve_args); if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ zval_dtor(&z_ret); return 0; } /* run SADD/RPUSH on target */ h_set_vals = Z_ARRVAL(z_ret); count = 1 + zend_hash_num_elements(h_set_vals); ZVAL_STRING(&z_fun_sadd, cmd_add[0]); z_sadd_args = ecalloc(count, sizeof(zval)); ZVAL_STRINGL(&z_sadd_args[0], key, key_len); i = 1; ZEND_HASH_FOREACH_VAL(h_set_vals, z_data_p) { /* add set elements */ ZVAL_ZVAL(&z_sadd_args[i], z_data_p, 1, 0); i++; } ZEND_HASH_FOREACH_END(); /* Clean up our input return value */ zval_dtor(&z_ret); call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args); /* cleanup */ zval_dtor(&z_fun_sadd); for (i = 0; i < count; i++) { zval_dtor(&z_sadd_args[i]); } efree(z_sadd_args); /* Clean up our output return value */ zval_dtor(&z_ret); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl); return 1; } static zend_bool ra_move_set(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { const char *cmd_list[] = {"SMEMBERS"}; const char *cmd_add[] = {"SADD"}; return ra_move_collection(key, key_len, z_from, z_to, 1, cmd_list, 1, cmd_add, ttl); } static zend_bool ra_move_list(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { const char *cmd_list[] = {"LRANGE", "0", "-1"}; const char *cmd_add[] = {"RPUSH"}; return ra_move_collection(key, key_len, z_from, z_to, 3, cmd_list, 1, cmd_add, ttl); } void ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to) { long res[2] = {0}, type, ttl; zend_bool success = 0; if (ra_get_key_type(z_from, key, key_len, z_from, res)) { type = res[0]; ttl = res[1]; /* open transaction on target server */ ra_index_multi(z_to, MULTI); switch(type) { case REDIS_STRING: success = ra_move_string(key, key_len, z_from, z_to, ttl); break; case REDIS_SET: success = ra_move_set(key, key_len, z_from, z_to, ttl); break; case REDIS_LIST: success = ra_move_list(key, key_len, z_from, z_to, ttl); break; case REDIS_ZSET: success = ra_move_zset(key, key_len, z_from, z_to, ttl); break; case REDIS_HASH: success = ra_move_hash(key, key_len, z_from, z_to, ttl); break; default: /* TODO: report? */ break; } } if(success) { ra_del_key(key, key_len, z_from); ra_index_key(key, key_len, z_to); } /* close transaction */ ra_index_exec(z_to, NULL, 0); } /* callback with the current progress, with hostname and count */ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, zend_string *hostname, long count) { zval zv, *z_ret = &zv; ZVAL_NULL(z_ret); zval z_args[2]; ZVAL_STRINGL(&z_args[0], ZSTR_VAL(hostname), ZSTR_LEN(hostname)); ZVAL_LONG(&z_args[1], count); z_cb->params = z_args; z_cb->retval = z_ret; z_cb->param_count = 2; /* run cb(hostname, count) */ zend_call_function(z_cb, z_cb_cache); /* cleanup */ zval_dtor(&z_args[0]); zval_dtor(z_ret); } static void ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool b_index, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache) { HashTable *h_keys; long count = 0; zval z_fun, z_ret, z_argv, *z_ele; /* list all keys */ if (b_index) { ZVAL_STRING(&z_fun, "SMEMBERS"); ZVAL_STRING(&z_argv, PHPREDIS_INDEX_NAME); } else { ZVAL_STRING(&z_fun, "KEYS"); ZVAL_STRING(&z_argv, "*"); } ZVAL_NULL(&z_ret); call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv); zval_dtor(&z_argv); zval_dtor(&z_fun); if (Z_TYPE(z_ret) == IS_ARRAY) { h_keys = Z_ARRVAL(z_ret); count = zend_hash_num_elements(h_keys); } if (!count) { zval_dtor(&z_ret); return; } /* callback */ if(z_cb && z_cb_cache) { zval_rehash_callback(z_cb, z_cb_cache, hostname, count); } /* for each key, redistribute */ ZEND_HASH_FOREACH_VAL(h_keys, z_ele) { int pos = 0; /* check that we're not moving to the same node. */ zval *z_target = ra_find_node(ra, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &pos); if (z_target && !zend_string_equals(hostname, ra->hosts[pos])) { /* different host */ ra_move_key(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), z_redis, z_target); } } ZEND_HASH_FOREACH_END(); /* cleanup */ zval_dtor(&z_ret); } void ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache) { int i; /* redistribute the data, server by server. */ if(!ra->prev) return; /* TODO: compare the two rings for equality */ for(i = 0; i < ra->prev->count; ++i) { ra_rehash_server(ra, &ra->prev->redis[i], ra->prev->hosts[i], ra->index, z_cb, z_cb_cache); } } redis-6.0.2/redis_array_impl.h0000644000175000000120000000300514515245367017114 0ustar pyatsukhnenkowheel#ifndef REDIS_ARRAY_IMPL_H #define REDIS_ARRAY_IMPL_H #if (defined(_MSC_VER) && _MSC_VER <= 1920) #include #else #include #endif #include "redis_array.h" RedisArray *ra_load_array(const char *name); RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth, zend_string *pass); zval *ra_find_node_by_name(RedisArray *ra, zend_string *host); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos); void ra_init_function_table(RedisArray *ra); void ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to); void ra_index_multi(zval *z_redis, long multi_value); void ra_index_key(const char *key, int key_len, zval *z_redis); void ra_index_keys(zval *z_pairs, zval *z_redis); void ra_index_del(zval *z_keys, zval *z_redis); void ra_index_exec(zval *z_redis, zval *return_value, int keep_all); void ra_index_discard(zval *z_redis, zval *return_value); void ra_index_unwatch(zval *z_redis, zval *return_value); zend_bool ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len); void ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache); #endif redis-6.0.2/redis_cluster.c0000644000175000000120000027363014515245367016446 0ustar pyatsukhnenkowheel/* +----------------------------------------------------------------------+ | 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. | +----------------------------------------------------------------------+ | Author: Michael Grunder | | Maintainer: Nicolas Favre-Felix | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "common.h" #include "php_redis.h" #include "ext/standard/info.h" #include "crc16.h" #include "redis_cluster.h" #include "redis_commands.h" #include #include #include "library.h" #include #include zend_class_entry *redis_cluster_ce; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; #if PHP_VERSION_ID < 80000 #include "redis_cluster_legacy_arginfo.h" #else #include "zend_attributes.h" #include "redis_cluster_arginfo.h" #endif PHP_MINIT_FUNCTION(redis_cluster) { redis_cluster_ce = register_class_RedisCluster(); redis_cluster_ce->create_object = create_cluster_context; redis_cluster_exception_ce = register_class_RedisClusterException(spl_ce_RuntimeException); return SUCCESS; } /* Handlers for RedisCluster */ zend_object_handlers RedisCluster_handlers; /* Our context seeds will be a hash table with RedisSock* pointers */ static void ht_free_seed(zval *data) { RedisSock *redis_sock = *(RedisSock**)data; if (redis_sock) redis_free_socket(redis_sock); } /* Free redisClusterNode objects we've stored */ static void ht_free_node(zval *data) { redisClusterNode *node = *(redisClusterNode**)data; cluster_free_node(node); } /* Create redisCluster context */ zend_object * create_cluster_context(zend_class_entry *class_type) { redisCluster *cluster; // Allocate our actual struct cluster = ecalloc(1, sizeof(redisCluster) + zend_object_properties_size(class_type)); // We're not currently subscribed anywhere cluster->subscribed_slot = -1; // Allocate our RedisSock we'll use to store prefix/serialization flags cluster->flags = ecalloc(1, sizeof(RedisSock)); // Allocate our hash table for seeds ALLOC_HASHTABLE(cluster->seeds); zend_hash_init(cluster->seeds, 0, NULL, ht_free_seed, 0); // Allocate our hash table for connected Redis objects ALLOC_HASHTABLE(cluster->nodes); zend_hash_init(cluster->nodes, 0, NULL, ht_free_node, 0); // Initialize it zend_object_std_init(&cluster->std, class_type); object_properties_init(&cluster->std, class_type); memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std); RedisCluster_handlers.free_obj = free_cluster_context; cluster->std.handlers = &RedisCluster_handlers; return &cluster->std; } /* Free redisCluster context */ void free_cluster_context(zend_object *object) { redisCluster *cluster = PHPREDIS_GET_OBJECT(redisCluster, object); cluster_free(cluster, 0); zend_object_std_dtor(&cluster->std); } /* Take user provided seeds and return unique and valid ones */ /* Attempt to connect to a Redis cluster provided seeds and timeout options */ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, double read_timeout, int persistent, zend_string *user, zend_string *pass, zval *context) { zend_string *hash = NULL, **seeds; redisCachedCluster *cc; uint32_t nseeds; char *err; /* Validate our arguments and get a sanitized seed array */ seeds = cluster_validate_args(timeout, read_timeout, ht_seeds, &nseeds, &err); if (seeds == NULL) { CLUSTER_THROW_EXCEPTION(err, 0); return; } if (user && ZSTR_LEN(user)) c->flags->user = zend_string_copy(user); if (pass && ZSTR_LEN(pass)) c->flags->pass = zend_string_copy(pass); if (context) { redis_sock_set_stream_context(c->flags, context); } c->flags->timeout = timeout; c->flags->read_timeout = read_timeout; c->flags->persistent = persistent; c->waitms = timeout * 1000L; /* Attempt to load slots from cache if caching is enabled */ if (CLUSTER_CACHING_ENABLED()) { /* Exit early if we can load from cache */ hash = cluster_hash_seeds(seeds, nseeds); if ((cc = cluster_cache_load(hash))) { cluster_init_cache(c, cc); goto cleanup; } } /* Initialize seeds and attempt to map keyspace */ cluster_init_seeds(c, seeds, nseeds); if (cluster_map_keyspace(c) == SUCCESS && hash) cluster_cache_store(hash, c->nodes); cleanup: if (hash) zend_string_release(hash); free_seed_array(seeds, nseeds); } /* Attempt to load a named cluster configured in php.ini */ void redis_cluster_load(redisCluster *c, char *name, int name_len) { zval z_seeds, z_tmp, *z_value; zend_string *user = NULL, *pass = NULL; double timeout = 0, read_timeout = 0; int persistent = 0; char *iptr; HashTable *ht_seeds = NULL; /* Seeds */ array_init(&z_seeds); if ((iptr = INI_STR("redis.clusters.seeds")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_seeds); } if ((z_value = zend_hash_str_find(Z_ARRVAL(z_seeds), name, name_len)) != NULL) { ht_seeds = Z_ARRVAL_P(z_value); } else { zval_dtor(&z_seeds); CLUSTER_THROW_EXCEPTION("Couldn't find seeds for cluster", 0); return; } /* Connection timeout */ if ((iptr = INI_STR("redis.clusters.timeout")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &timeout); zval_dtor(&z_tmp); } /* Read timeout */ if ((iptr = INI_STR("redis.clusters.read_timeout")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &read_timeout); zval_dtor(&z_tmp); } /* Persistent connections */ if ((iptr = INI_STR("redis.clusters.persistent")) != NULL) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_bool(Z_ARRVAL(z_tmp), name, name_len, &persistent); zval_dtor(&z_tmp); } if ((iptr = INI_STR("redis.clusters.auth"))) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_auth(Z_ARRVAL(z_tmp), name, name_len, &user, &pass); zval_dtor(&z_tmp); } /* Attempt to create/connect to the cluster */ redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, user, pass, NULL); /* Clean up */ zval_dtor(&z_seeds); if (user) zend_string_release(user); if (pass) zend_string_release(pass); } /* * PHP Methods */ /* Create a RedisCluster Object */ PHP_METHOD(RedisCluster, __construct) { zval *object, *z_seeds = NULL, *z_auth = NULL, *context = NULL; zend_string *user = NULL, *pass = NULL; double timeout = 0.0, read_timeout = 0.0; size_t name_len; zend_bool persistent = 0; redisCluster *c = GET_CONTEXT(); char *name; // Parse arguments if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os!|addbza!", &object, redis_cluster_ce, &name, &name_len, &z_seeds, &timeout, &read_timeout, &persistent, &z_auth, &context) == FAILURE) { RETURN_FALSE; } /* If we've got a string try to load from INI */ if (ZEND_NUM_ARGS() < 2) { if (name_len == 0) { // Require a name CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0); } redis_cluster_load(c, name, name_len); return; } /* The normal case, loading from arguments */ redis_extract_auth_info(z_auth, &user, &pass); redis_cluster_init(c, Z_ARRVAL_P(z_seeds), timeout, read_timeout, persistent, user, pass, context); if (user) zend_string_release(user); if (pass) zend_string_release(pass); } /* * RedisCluster method implementation */ /* {{{ proto bool RedisCluster::close() */ PHP_METHOD(RedisCluster, close) { cluster_disconnect(GET_CONTEXT(), 1); RETURN_TRUE; } /* {{{ proto string RedisCluster::get(string key) */ PHP_METHOD(RedisCluster, get) { CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp, 1); } /* }}} */ /* {{{ proto bool RedisCluster::set(string key, string value) */ PHP_METHOD(RedisCluster, set) { CLUSTER_PROCESS_CMD(set, cluster_set_resp, 0); } /* }}} */ /* Generic handler for MGET/MSET/MSETNX */ static int distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, clusterMultiCmd *mc, zval *z_ret, int last, cluster_cb cb) { clusterMultiCtx *ctx; // Finalize multi command cluster_multi_fini(mc); // Spin up multi context ctx = emalloc(sizeof(clusterMultiCtx)); ctx->z_multi = z_ret; ctx->count = mc->argc; ctx->last = last; // Attempt to send the command if (cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len) < 0 || c->err != NULL) { efree(ctx); return -1; } if (CLUSTER_IS_ATOMIC(c)) { // Process response now cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx); } else { CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); } // Clear out our command but retain allocated memory CLUSTER_MULTI_CLEAR(mc); return 0; } /* Container struct for a key/value pair pulled from an array */ typedef struct clusterKeyValHT { char kbuf[22]; char *key; size_t key_len; int key_free; short slot; char *val; size_t val_len; int val_free; } clusterKeyValHT; /* Helper to pull a key/value pair from a HashTable */ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, clusterKeyValHT *kv) { zval *z_val; zend_ulong idx; // Grab the key, convert it to a string using provided kbuf buffer if it's // a LONG style key zend_string *zkey; switch (zend_hash_get_current_key_ex(ht, &zkey, &idx, ptr)) { case HASH_KEY_IS_STRING: kv->key_len = ZSTR_LEN(zkey); kv->key = ZSTR_VAL(zkey); break; case HASH_KEY_IS_LONG: kv->key_len = snprintf(kv->kbuf,sizeof(kv->kbuf),"%ld",(long)idx); kv->key = kv->kbuf; break; default: CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0); return -1; } // Prefix our key if we need to, set the slot kv->key_free = redis_key_prefix(c->flags, &(kv->key), &(kv->key_len)); kv->slot = cluster_hash_key(kv->key, kv->key_len); // Now grab our value if ((z_val = zend_hash_get_current_data_ex(ht, ptr)) == NULL) { CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0); return -1; } // Serialize our value if required kv->val_free = redis_pack(c->flags,z_val,&(kv->val),&(kv->val_len)); // Success return 0; } /* Helper to pull, prefix, and hash a key from a HashTable value */ static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, clusterKeyValHT *kv) { zval *z_key; if ((z_key = zend_hash_get_current_data_ex(ht, ptr)) == NULL) { // Shouldn't happen, but check anyway CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0); return -1; } // Always want to work with strings convert_to_string(z_key); kv->key = Z_STRVAL_P(z_key); kv->key_len = Z_STRLEN_P(z_key); kv->key_free = redis_key_prefix(c->flags, &(kv->key), &(kv->key_len)); // Hash our key kv->slot = cluster_hash_key(kv->key, kv->key_len); // Success return 0; } /* Turn variable arguments into a HashTable for processing */ static HashTable *method_args_to_ht(zval *z_args, int argc) { HashTable *ht_ret; int i; /* Allocate our hash table */ ALLOC_HASHTABLE(ht_ret); zend_hash_init(ht_ret, argc, NULL, NULL, 0); /* Populate our return hash table with our arguments */ for (i = 0; i < argc; i++) { zend_hash_next_index_insert(ht_ret, &z_args[i]); } /* Return our hash table */ return ht_ret; } /* Convenience handler for commands that take multiple keys such as * MGET, DEL, and UNLINK */ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zval *z_ret, cluster_cb cb) { redisCluster *c = GET_CONTEXT(); clusterMultiCmd mc = {0}; clusterKeyValHT kv; zval *z_args; HashTable *ht_arr; HashPosition ptr; int i = 1, argc = ZEND_NUM_ARGS(), ht_free = 0; short slot; /* If we don't have any arguments we're invalid */ if (!argc) return -1; /* Extract our arguments into an array */ z_args = ecalloc(argc, sizeof(zval)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return -1; } /* Determine if we're working with a single array or variadic args */ if (argc == 1 && Z_TYPE(z_args[0]) == IS_ARRAY) { ht_arr = Z_ARRVAL(z_args[0]); argc = zend_hash_num_elements(ht_arr); if (!argc) { efree(z_args); return -1; } } else { ht_arr = method_args_to_ht(z_args, argc); ht_free = 1; } /* MGET is readonly, DEL is not */ c->readonly = kw_len == 4 && CLUSTER_IS_ATOMIC(c); // Initialize our "multi" command handler with command/len CLUSTER_MULTI_INIT(mc, kw, kw_len); // Process the first key outside of our loop, so we don't have to check if // it's the first iteration every time, needlessly zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); if (get_key_ht(c, ht_arr, &ptr, &kv) < 0) { efree(z_args); return -1; } // Process our key and add it to the command cluster_multi_add(&mc, kv.key, kv.key_len); // Free key if we prefixed if (kv.key_free) efree(kv.key); // Move to the next key zend_hash_move_forward_ex(ht_arr, &ptr); // Iterate over keys 2...N slot = kv.slot; while (zend_hash_has_more_elements_ex(ht_arr, &ptr) ==SUCCESS) { if (get_key_ht(c, ht_arr, &ptr, &kv) < 0) { cluster_multi_free(&mc); if (ht_free) { zend_hash_destroy(ht_arr); efree(ht_arr); } efree(z_args); return -1; } // If the slots have changed, kick off the keys we've aggregated if (slot != kv.slot) { // Process this batch of MGET keys if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, i == argc, cb) < 0) { cluster_multi_free(&mc); if (ht_free) { zend_hash_destroy(ht_arr); efree(ht_arr); } efree(z_args); return -1; } } // Add this key to the command cluster_multi_add(&mc, kv.key, kv.key_len); // Free key if we prefixed if (kv.key_free) efree(kv.key); // Update the last slot we encountered, and the key we're on slot = kv.slot; i++; zend_hash_move_forward_ex(ht_arr, &ptr); } efree(z_args); // If we've got straggler(s) process them if (mc.argc > 0) { if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, 1, cb) < 0) { cluster_multi_free(&mc); if (ht_free) { zend_hash_destroy(ht_arr); efree(ht_arr); } return -1; } } // Free our command cluster_multi_free(&mc); /* Clean up our hash table if we constructed it from variadic args */ if (ht_free) { zend_hash_destroy(ht_arr); efree(ht_arr); } /* Return our object if we're in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) RETVAL_ZVAL(getThis(), 1, 0); // Success return 0; } /* Handler for both MSET and MSETNX */ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zval *z_ret, cluster_cb cb) { redisCluster *c = GET_CONTEXT(); clusterKeyValHT kv; clusterMultiCmd mc = {0}; zval *z_arr; HashTable *ht_arr; HashPosition ptr; int i = 1, argc; short slot; // Parse our arguments if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) { return -1; } // No reason to send zero args ht_arr = Z_ARRVAL_P(z_arr); if ((argc = zend_hash_num_elements(ht_arr)) == 0) { return -1; } /* This is a write command */ c->readonly = 0; // Set up our multi command handler CLUSTER_MULTI_INIT(mc, kw, kw_len); // Process the first key/value pair outside of our loop zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); if (get_key_val_ht(c, ht_arr, &ptr, &kv) ==-1) return -1; zend_hash_move_forward_ex(ht_arr, &ptr); // Add this to our multi cmd, set slot, free key if we prefixed cluster_multi_add(&mc, kv.key, kv.key_len); cluster_multi_add(&mc, kv.val, kv.val_len); if (kv.key_free) efree(kv.key); if (kv.val_free) efree(kv.val); // While we've got more keys to set slot = kv.slot; while (zend_hash_has_more_elements_ex(ht_arr, &ptr) ==SUCCESS) { // Pull the next key/value pair if (get_key_val_ht(c, ht_arr, &ptr, &kv) ==-1) { return -1; } // If the slots have changed, process responses if (slot != kv.slot) { if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, i == argc, cb) < 0) { cluster_multi_free(&mc); return -1; } } // Add this key and value to our command cluster_multi_add(&mc, kv.key, kv.key_len); cluster_multi_add(&mc, kv.val, kv.val_len); // Free our key and value if we need to if (kv.key_free) efree(kv.key); if (kv.val_free) efree(kv.val); // Update our slot, increment position slot = kv.slot; i++; // Move on zend_hash_move_forward_ex(ht_arr, &ptr); } // If we've got stragglers, process them too if (mc.argc > 0) { if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, 1, cb) < 0) { cluster_multi_free(&mc); return -1; } } // Free our command cluster_multi_free(&mc); /* Return our object if we're in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) RETVAL_ZVAL(getThis(), 1, 0); // Success return 0; } /* Generic passthru for DEL and UNLINK which act identically */ static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { zval *z_ret = emalloc(sizeof(*z_ret)); // Initialize a LONG value to zero for our return ZVAL_LONG(z_ret, 0); // Parse args, process if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, kw_len, z_ret, cluster_del_resp) < 0) { efree(z_ret); RETURN_FALSE; } } /* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */ PHP_METHOD(RedisCluster, del) { cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL") - 1); } /* {{{ proto array RedisCluster::unlink(string key1, string key2, ... keyN) */ PHP_METHOD(RedisCluster, unlink) { cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1); } /* {{{ proto array RedisCluster::mget(array keys) */ PHP_METHOD(RedisCluster, mget) { zval *z_ret = emalloc(sizeof(*z_ret)); array_init(z_ret); // Parse args, process if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MGET", sizeof("MGET")-1, z_ret, cluster_mbulk_mget_resp) < 0) { zval_dtor(z_ret); efree(z_ret); RETURN_FALSE; } } /* {{{ proto bool RedisCluster::mset(array keyvalues) */ PHP_METHOD(RedisCluster, mset) { zval *z_ret = emalloc(sizeof(*z_ret)); ZVAL_TRUE(z_ret); // Parse args and process. If we get a failure, free zval and return FALSE. if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", sizeof("MSET")-1, z_ret, cluster_mset_resp) ==-1) { efree(z_ret); RETURN_FALSE; } } /* {{{ proto array RedisCluster::msetnx(array keyvalues) */ PHP_METHOD(RedisCluster, msetnx) { zval *z_ret = emalloc(sizeof(*z_ret)); array_init(z_ret); // Parse args and process. If we get a failure, free mem and return FALSE if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp) ==-1) { zval_dtor(z_ret); efree(z_ret); RETURN_FALSE; } } /* }}} */ /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, psetex) { CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::setnx(string key, string value) */ PHP_METHOD(RedisCluster, setnx) { CLUSTER_PROCESS_KW_CMD("SETNX", redis_kv_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::getSet(string key, string value) */ PHP_METHOD(RedisCluster, getset) { CLUSTER_PROCESS_KW_CMD("GETSET", redis_kv_cmd, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto int RedisCluster::exists(string $key, string ...$more_keys) */ PHP_METHOD(RedisCluster, exists) { CLUSTER_PROCESS_KW_CMD("EXISTS", redis_varkey_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto int RedisCluster::exists(string $key, string ...$more_keys) */ PHP_METHOD(RedisCluster, touch) { CLUSTER_PROCESS_KW_CMD("TOUCH", redis_varkey_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array Redis::keys(string pattern) */ PHP_METHOD(RedisCluster, keys) { redisCluster *c = GET_CONTEXT(); redisClusterNode *node; size_t pat_len; char *pat, *cmd; clusterReply *resp; int i, cmd_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pat, &pat_len) == FAILURE) { RETURN_FALSE; } /* Prefix and then build our command */ cmd_len = redis_spprintf(c->flags, NULL, &cmd, "KEYS", "k", pat, pat_len); array_init(return_value); /* Treat as readonly */ c->readonly = CLUSTER_IS_ATOMIC(c); /* Iterate over our known nodes */ ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) continue; if (cluster_send_slot(c, node->slot, cmd, cmd_len, TYPE_MULTIBULK ) < 0) { php_error_docref(0, E_ERROR, "Can't send KEYS to %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); zval_dtor(return_value); efree(cmd); RETURN_FALSE; } /* Ensure we can get a response */ resp = cluster_read_resp(c, 0); if (!resp) { php_error_docref(0, E_WARNING, "Can't read response from %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); continue; } /* Iterate keys, adding to our big array */ for(i = 0; i < resp->elements; i++) { /* Skip non bulk responses, they should all be bulk */ if (resp->element[i]->type != TYPE_BULK) { continue; } add_next_index_stringl(return_value, resp->element[i]->str, resp->element[i]->len); } /* Free response, don't free data */ cluster_free_reply(resp, 1); } ZEND_HASH_FOREACH_END(); efree(cmd); } /* }}} */ /* {{{ proto int RedisCluster::type(string key) */ PHP_METHOD(RedisCluster, type) { CLUSTER_PROCESS_KW_CMD("TYPE", redis_key_cmd, cluster_type_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::pop(string key, [int count = 0]) */ PHP_METHOD(RedisCluster, lpop) { CLUSTER_PROCESS_KW_CMD("LPOP", redis_pop_cmd, cluster_pop_resp, 0); } /* }}} */ PHP_METHOD(RedisCluster, lpos) { CLUSTER_PROCESS_CMD(lpos, cluster_lpos_resp, 1); } /* {{{ proto string RedisCluster::rpop(string key, [int count = 0]) */ PHP_METHOD(RedisCluster, rpop) { CLUSTER_PROCESS_KW_CMD("RPOP", redis_pop_cmd, cluster_pop_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::lset(string key, long index, string val) */ PHP_METHOD(RedisCluster, lset) { CLUSTER_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::spop(string key) */ PHP_METHOD(RedisCluster, spop) { if (ZEND_NUM_ARGS() == 1) { CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_cmd, cluster_bulk_resp, 0); } else if (ZEND_NUM_ARGS() == 2) { CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, cluster_mbulk_resp, 0); } else { ZEND_WRONG_PARAM_COUNT(); } } /* }}} */ /* {{{ proto string|array RedisCluster::srandmember(string key, [long count]) */ PHP_METHOD(RedisCluster, srandmember) { CLUSTER_PROCESS_CMD(srandmember, cluster_srandmember_resp, 1); } /* {{{ proto string RedisCluster::strlen(string key) */ PHP_METHOD(RedisCluster, strlen) { CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_long_resp, 1); } /* {{{ proto long RedisCluster::lpush(string key, string val1, ... valN) */ PHP_METHOD(RedisCluster, lpush) { CLUSTER_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::rpush(string key, string val1, ... valN) */ PHP_METHOD(RedisCluster, rpush) { CLUSTER_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::blpop(string key1, ... keyN, long timeout) */ PHP_METHOD(RedisCluster, blpop) { CLUSTER_PROCESS_KW_CMD("BLPOP", redis_blocking_pop_cmd, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::brpop(string key1, ... keyN, long timeout */ PHP_METHOD(RedisCluster, brpop) { CLUSTER_PROCESS_KW_CMD("BRPOP", redis_blocking_pop_cmd, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::rpushx(string key, mixed value) */ PHP_METHOD(RedisCluster, rpushx) { CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::lpushx(string key, mixed value) */ PHP_METHOD(RedisCluster, lpushx) { CLUSTER_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::linsert(string k,string pos,mix pvt,mix val) */ PHP_METHOD(RedisCluster, linsert) { CLUSTER_PROCESS_CMD(linsert, cluster_long_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::lindex(string key, long index) */ PHP_METHOD(RedisCluster, lindex) { CLUSTER_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::lrem(string key, long count, string val) */ PHP_METHOD(RedisCluster, lrem) { CLUSTER_PROCESS_CMD(lrem, cluster_long_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::rpoplpush(string key, string key) */ PHP_METHOD(RedisCluster, rpoplpush) { CLUSTER_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::brpoplpush(string key, string key, long tm) */ PHP_METHOD(RedisCluster, brpoplpush) { CLUSTER_PROCESS_CMD(brpoplpush, cluster_bulk_resp, 0); } /* }}} */ PHP_METHOD(RedisCluster, lmove) { CLUSTER_PROCESS_KW_CMD("LMOVE", redis_lmove_cmd, cluster_bulk_resp, 0); } PHP_METHOD(RedisCluster, blmove) { CLUSTER_PROCESS_KW_CMD("BLMOVE", redis_lmove_cmd, cluster_bulk_resp, 0); } /* {{{ proto long RedisCluster::llen(string key) */ PHP_METHOD(RedisCluster, llen) { CLUSTER_PROCESS_KW_CMD("LLEN", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::scard(string key) */ PHP_METHOD(RedisCluster, scard) { CLUSTER_PROCESS_KW_CMD("SCARD", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::smembers(string key) */ PHP_METHOD(RedisCluster, smembers) { CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::sismember(string key) */ PHP_METHOD(RedisCluster, sismember) { CLUSTER_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, cluster_1_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::smismember(string key, string member0, ...memberN) */ PHP_METHOD(RedisCluster, smismember) { CLUSTER_PROCESS_KW_CMD("SMISMEMBER", redis_key_varval_cmd, cluster_variant_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::sadd(string key, string val1 [, ...]) */ PHP_METHOD(RedisCluster, sadd) { CLUSTER_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::saddarray(string key, array values) */ PHP_METHOD(RedisCluster, saddarray) { CLUSTER_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::srem(string key, string val1 [, ...]) */ PHP_METHOD(RedisCluster, srem) { CLUSTER_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::sunion(string key1, ... keyN) */ PHP_METHOD(RedisCluster, sunion) { CLUSTER_PROCESS_KW_CMD("SUNION", redis_varkey_cmd, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::sunionstore(string dst, string k1, ... kN) */ PHP_METHOD(RedisCluster, sunionstore) { CLUSTER_PROCESS_KW_CMD("SUNIONSTORE", redis_varkey_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ ptoto array RedisCluster::sinter(string k1, ... kN) */ PHP_METHOD(RedisCluster, sinter) { CLUSTER_PROCESS_KW_CMD("SINTER", redis_varkey_cmd, cluster_mbulk_resp, 0); } /* {{{ proto RedisCluster::sintercard(array $keys, int $count = -1) */ PHP_METHOD(RedisCluster, sintercard) { CLUSTER_PROCESS_KW_CMD("SINTERCARD", redis_intercard_cmd, cluster_long_resp, 0); } /* }}} */ /* }}} */ /* {{{ ptoto long RedisCluster::sinterstore(string dst, string k1, ... kN) */ PHP_METHOD(RedisCluster, sinterstore) { CLUSTER_PROCESS_KW_CMD("SINTERSTORE", redis_varkey_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::sdiff(string k1, ... kN) */ PHP_METHOD(RedisCluster, sdiff) { CLUSTER_PROCESS_KW_CMD("SDIFF", redis_varkey_cmd, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::sdiffstore(string dst, string k1, ... kN) */ PHP_METHOD(RedisCluster, sdiffstore) { CLUSTER_PROCESS_KW_CMD("SDIFFSTORE", redis_varkey_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::smove(sting src, string dst, string mem) */ PHP_METHOD(RedisCluster, smove) { CLUSTER_PROCESS_CMD(smove, cluster_1_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::persist(string key) */ PHP_METHOD(RedisCluster, persist) { CLUSTER_PROCESS_KW_CMD("PERSIST", redis_key_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::ttl(string key) */ PHP_METHOD(RedisCluster, ttl) { CLUSTER_PROCESS_KW_CMD("TTL", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::pttl(string key) */ PHP_METHOD(RedisCluster, pttl) { CLUSTER_PROCESS_KW_CMD("PTTL", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zcard(string key) */ PHP_METHOD(RedisCluster, zcard) { CLUSTER_PROCESS_KW_CMD("ZCARD", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto double RedisCluster::zscore(string key) */ PHP_METHOD(RedisCluster, zscore) { CLUSTER_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, cluster_dbl_resp, 1); } /* }}} */ PHP_METHOD(RedisCluster, zmscore) { CLUSTER_PROCESS_KW_CMD("ZMSCORE", redis_key_varval_cmd, cluster_mbulk_dbl_resp, 1); } /* {{{ proto long RedisCluster::zadd(string key,double score,string mem, ...) */ PHP_METHOD(RedisCluster, zadd) { CLUSTER_PROCESS_CMD(zadd, cluster_zadd_resp, 0); } /* }}} */ /* {{{ proto double RedisCluster::zincrby(string key, double by, string mem) */ PHP_METHOD(RedisCluster, zincrby) { CLUSTER_PROCESS_CMD(zincrby, cluster_dbl_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zremrangebyscore(string k, string s, string e) */ PHP_METHOD(RedisCluster, zremrangebyscore) { CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zcount(string key, string s, string e) */ PHP_METHOD(RedisCluster, zcount) { CLUSTER_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zrank(string key, mixed member) */ PHP_METHOD(RedisCluster, zrank) { CLUSTER_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zrevrank(string key, mixed member) */ PHP_METHOD(RedisCluster, zrevrank) { CLUSTER_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::hlen(string key) */ PHP_METHOD(RedisCluster, hlen) { CLUSTER_PROCESS_KW_CMD("HLEN", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::hkeys(string key) */ PHP_METHOD(RedisCluster, hkeys) { CLUSTER_PROCESS_KW_CMD("HKEYS", redis_key_cmd, cluster_mbulk_raw_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::hvals(string key) */ PHP_METHOD(RedisCluster, hvals) { CLUSTER_PROCESS_KW_CMD("HVALS", redis_key_cmd, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::hget(string key, string mem) */ PHP_METHOD(RedisCluster, hget) { CLUSTER_PROCESS_KW_CMD("HGET", redis_key_str_cmd, cluster_bulk_resp, 1); } /* }}} */ /* {{{ proto bool RedisCluster::hset(string key, string mem, string val) */ PHP_METHOD(RedisCluster, hset) { CLUSTER_PROCESS_CMD(hset, cluster_long_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::hsetnx(string key, string mem, string val) */ PHP_METHOD(RedisCluster, hsetnx) { CLUSTER_PROCESS_CMD(hsetnx, cluster_1_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::hgetall(string key) */ PHP_METHOD(RedisCluster, hgetall) { CLUSTER_PROCESS_KW_CMD("HGETALL", redis_key_cmd, cluster_mbulk_zipstr_resp, 1); } /* }}} */ /* {{{ proto bool RedisCluster::hexists(string key, string member) */ PHP_METHOD(RedisCluster, hexists) { CLUSTER_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, cluster_1_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::hincr(string key, string mem, long val) */ PHP_METHOD(RedisCluster, hincrby) { CLUSTER_PROCESS_CMD(hincrby, cluster_long_resp, 0); } /* }}} */ /* {{{ proto double RedisCluster::hincrbyfloat(string k, string m, double v) */ PHP_METHOD(RedisCluster, hincrbyfloat) { CLUSTER_PROCESS_CMD(hincrbyfloat, cluster_dbl_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::hmset(string key, array key_vals) */ PHP_METHOD(RedisCluster, hmset) { CLUSTER_PROCESS_CMD(hmset, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::hrandfield(string key, [array $options]) */ PHP_METHOD(RedisCluster, hrandfield) { CLUSTER_PROCESS_CMD(hrandfield, cluster_hrandfield_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::hdel(string key, string mem1, ... memN) */ PHP_METHOD(RedisCluster, hdel) { CLUSTER_PROCESS_CMD(hdel, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::hmget(string key, array members) */ PHP_METHOD(RedisCluster, hmget) { CLUSTER_PROCESS_CMD(hmget, cluster_mbulk_assoc_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::hstrlen(string key, string field) */ PHP_METHOD(RedisCluster, hstrlen) { CLUSTER_PROCESS_CMD(hstrlen, cluster_long_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp, 1); } /* {{{ proto long RedisCluster::incr(string key) */ PHP_METHOD(RedisCluster, incr) { CLUSTER_PROCESS_CMD(incr, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::incrby(string key, long byval) */ PHP_METHOD(RedisCluster, incrby) { CLUSTER_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::decr(string key) */ PHP_METHOD(RedisCluster, decr) { CLUSTER_PROCESS_CMD(decr, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::decrby(string key, long byval) */ PHP_METHOD(RedisCluster, decrby) { CLUSTER_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto double RedisCluster::incrbyfloat(string key, double val) */ PHP_METHOD(RedisCluster, incrbyfloat) { CLUSTER_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, cluster_dbl_resp, 0); } /* }}} */ /* {{{ proto double RedisCluster::decrbyfloat(string key, double val) */ PHP_METHOD(RedisCluster, decrbyfloat) { CLUSTER_PROCESS_KW_CMD("DECRBYFLOAT", redis_key_dbl_cmd, cluster_dbl_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::expire(string key, long sec) */ PHP_METHOD(RedisCluster, expire) { CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_expire_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::expireat(string key, long ts) */ PHP_METHOD(RedisCluster, expireat) { CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_expire_cmd, cluster_1_resp, 0); } /* {{{ proto bool RedisCluster::pexpire(string key, long ms) */ PHP_METHOD(RedisCluster, pexpire) { CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_expire_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::pexpireat(string key, long ts) */ PHP_METHOD(RedisCluster, pexpireat) { CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_expire_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ Redis::expiretime(string $key): int */ PHP_METHOD(RedisCluster, expiretime) { CLUSTER_PROCESS_KW_CMD("EXPIRETIME", redis_key_cmd, cluster_long_resp, 1); } /* {{{ Redis::pexpiretime(string $key): int */ PHP_METHOD(RedisCluster, pexpiretime) { CLUSTER_PROCESS_KW_CMD("PEXPIRETIME", redis_key_cmd, cluster_long_resp, 1); } /* {{{ proto long RedisCluster::append(string key, string val) */ PHP_METHOD(RedisCluster, append) { CLUSTER_PROCESS_KW_CMD("APPEND", redis_kv_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::getbit(string key, long val) */ PHP_METHOD(RedisCluster, getbit) { CLUSTER_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::setbit(string key, long offset, bool onoff) */ PHP_METHOD(RedisCluster, setbit) { CLUSTER_PROCESS_CMD(setbit, cluster_long_resp, 0); } /* {{{ proto long RedisCluster::bitop(string op,string key,[string key2,...]) */ PHP_METHOD(RedisCluster, bitop) { CLUSTER_PROCESS_CMD(bitop, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::bitcount(string key, [int start, int end]) */ PHP_METHOD(RedisCluster, bitcount) { CLUSTER_PROCESS_CMD(bitcount, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::bitpos(string key, int bit, [int s, int end]) */ PHP_METHOD(RedisCluster, bitpos) { CLUSTER_PROCESS_CMD(bitpos, cluster_long_resp, 1); } /* }}} */ /* {{{ proto string Redis::lget(string key, long index) */ PHP_METHOD(RedisCluster, lget) { CLUSTER_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, cluster_bulk_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::getrange(string key, long start, long end) */ PHP_METHOD(RedisCluster, getrange) { CLUSTER_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd, cluster_bulk_resp, 1); } /* }}} */ /* {{{ prot RedisCluster::lcs(string $key1, string $key2, ?array $options = NULL): mixed; */ PHP_METHOD(RedisCluster, lcs) { CLUSTER_PROCESS_CMD(lcs, cluster_variant_resp, 1); } /* {{{ proto Redis|array|false Redis::lmpop(array $keys, string $from, int $count = 1) */ PHP_METHOD(RedisCluster, lmpop) { CLUSTER_PROCESS_KW_CMD("LMPOP", redis_mpop_cmd, cluster_mpop_resp, 0); } /* }}} */ /* {{{ proto Redis|array|false Redis::blmpop(double $timeout, array $keys, string $from, int $count = 1) */ PHP_METHOD(RedisCluster, blmpop) { CLUSTER_PROCESS_KW_CMD("BLMPOP", redis_mpop_cmd, cluster_mpop_resp, 0); } /* }}} */ /* {{{ proto Redis|array|false Redis::zmpop(array $keys, string $from, int $count = 1) */ PHP_METHOD(RedisCluster, zmpop) { CLUSTER_PROCESS_KW_CMD("ZMPOP", redis_mpop_cmd, cluster_mpop_resp, 0); } /* }}} */ /* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, sring $from, int $count = 1) */ PHP_METHOD(RedisCluster, bzmpop) { CLUSTER_PROCESS_KW_CMD("BZMPOP", redis_mpop_cmd, cluster_mpop_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::ltrim(string key, long start, long end) */ PHP_METHOD(RedisCluster, ltrim) { CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::lrange(string key, long start, long end) */ PHP_METHOD(RedisCluster, lrange) { CLUSTER_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zremrangebyrank(string k, long s, long e) */ PHP_METHOD(RedisCluster, zremrangebyrank) { CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::publish(string key, string msg) */ PHP_METHOD(RedisCluster, publish) { CLUSTER_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::rename(string key1, string key2) */ PHP_METHOD(RedisCluster, rename) { CLUSTER_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::renamenx(string key1, string key2) */ PHP_METHOD(RedisCluster, renamenx) { CLUSTER_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::pfcount(string key) */ PHP_METHOD(RedisCluster, pfcount) { CLUSTER_PROCESS_CMD(pfcount, cluster_long_resp, 1); } /* }}} */ /* {{{ proto bool RedisCluster::pfadd(string key, array vals) */ PHP_METHOD(RedisCluster, pfadd) { CLUSTER_PROCESS_CMD(pfadd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::pfmerge(string key, array keys) */ PHP_METHOD(RedisCluster, pfmerge) { CLUSTER_PROCESS_CMD(pfmerge, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto boolean RedisCluster::restore(string key, long ttl, string val) */ PHP_METHOD(RedisCluster, restore) { CLUSTER_PROCESS_CMD(restore, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::setrange(string key, long offset, string val) */ PHP_METHOD(RedisCluster, setrange) { CLUSTER_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto * array RedisCluster::zrange(string k, long s, long e, bool score = 0) */ PHP_METHOD(RedisCluster, zrange) { CLUSTER_PROCESS_KW_CMD("ZRANGE", redis_zrange_cmd, cluster_zrange_resp, 1); } /* }}} */ /* {{{ proto * array RedisCluster::zrange(string $dstkey, string $srckey, long s, long e, array|bool $options = false) */ PHP_METHOD(RedisCluster, zrangestore) { CLUSTER_PROCESS_KW_CMD("ZRANGESTORE", redis_zrange_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto * array RedisCluster::zrevrange(string k,long s,long e,bool scores = 0) */ PHP_METHOD(RedisCluster, zrevrange) { CLUSTER_PROCESS_KW_CMD("ZREVRANGE", redis_zrange_cmd, cluster_zrange_resp, 1); } /* }}} */ /* {{{ proto array * RedisCluster::zrangebyscore(string k, long s, long e, array opts) */ PHP_METHOD(RedisCluster, zrangebyscore) { CLUSTER_PROCESS_KW_CMD("ZRANGEBYSCORE", redis_zrange_cmd, cluster_zrange_resp, 1); } /* }}} */ /* {{{ proto RedisCluster::zunionstore(string dst, array keys, [array weights, * string agg]) */ PHP_METHOD(RedisCluster, zunionstore) { CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinterunionstore_cmd, cluster_long_resp, 0); } /* }}} */ PHP_METHOD(RedisCluster, zdiff) { CLUSTER_PROCESS_CMD(zdiff, cluster_zdiff_resp, 1); } PHP_METHOD(RedisCluster, zdiffstore) { CLUSTER_PROCESS_CMD(zdiffstore, cluster_long_resp, 0); } PHP_METHOD(RedisCluster, zinter) { CLUSTER_PROCESS_KW_CMD("ZUNION", redis_zinterunion_cmd, cluster_zdiff_resp, 1); } PHP_METHOD(RedisCluster, zunion) { CLUSTER_PROCESS_KW_CMD("ZINTER", redis_zinterunion_cmd, cluster_zdiff_resp, 1); } /* {{{ proto array RedisCluster::zrandmember(string key, array options) */ PHP_METHOD(RedisCluster, zrandmember) { CLUSTER_PROCESS_CMD(zrandmember, cluster_zrandmember_resp, 1); } /* }}} */ /* {{{ proto RedisCluster::zinterstore(string dst, array keys, [array weights, * string agg]) */ PHP_METHOD(RedisCluster, zinterstore) { CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinterunionstore_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zintercard(array $keys, int $count = -1) */ PHP_METHOD(RedisCluster, zintercard) { CLUSTER_PROCESS_KW_CMD("ZINTERCARD", redis_intercard_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zrem(string key, string val1, ... valN) */ PHP_METHOD(RedisCluster, zrem) { CLUSTER_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array * RedisCluster::zrevrangebyscore(string k, long s, long e, array opts) */ PHP_METHOD(RedisCluster, zrevrangebyscore) { CLUSTER_PROCESS_KW_CMD("ZREVRANGEBYSCORE", redis_zrange_cmd, cluster_zrange_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::zrangebylex(string key, string min, string max, * [offset, count]) */ PHP_METHOD(RedisCluster, zrangebylex) { CLUSTER_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::zrevrangebylex(string key, string min, * string min, [long off, long limit) */ PHP_METHOD(RedisCluster, zrevrangebylex) { CLUSTER_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zlexcount(string key, string min, string max) */ PHP_METHOD(RedisCluster, zlexcount) { CLUSTER_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zremrangebylex(string key, string min, string max) */ PHP_METHOD(RedisCluster, zremrangebylex) { CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::zpopmax(string key) */ PHP_METHOD(RedisCluster, zpopmax) { if (ZEND_NUM_ARGS() == 1) { CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, cluster_mbulk_zipdbl_resp, 0); } else if (ZEND_NUM_ARGS() == 2) { CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, cluster_mbulk_zipdbl_resp, 0); } else { ZEND_WRONG_PARAM_COUNT(); } } /* }}} */ /* {{{ proto array RedisCluster::zpopmin(string key) */ PHP_METHOD(RedisCluster, zpopmin) { if (ZEND_NUM_ARGS() == 1) { CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, cluster_mbulk_zipdbl_resp, 0); } else if (ZEND_NUM_ARGS() == 2) { CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, cluster_mbulk_zipdbl_resp, 0); } else { ZEND_WRONG_PARAM_COUNT(); } } /* }}} */ /* {{{ proto array RedisCluster::bzPopMin(Array keys [, timeout]) }}} */ PHP_METHOD(RedisCluster, bzpopmax) { CLUSTER_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, cluster_mbulk_resp, 0); } /* {{{ proto array RedisCluster::bzPopMax(Array keys [, timeout]) }}} */ PHP_METHOD(RedisCluster, bzpopmin) { CLUSTER_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, cluster_mbulk_resp, 0); } /* {{{ proto RedisCluster::sort(string key, array options) */ PHP_METHOD(RedisCluster, sort) { CLUSTER_PROCESS_KW_CMD("SORT", redis_sort_cmd, cluster_variant_resp, 0); } /* {{{ proto RedisCluster::sort_ro(string key, array options) */ PHP_METHOD(RedisCluster, sort_ro) { CLUSTER_PROCESS_KW_CMD("SORT_RO", redis_sort_cmd, cluster_variant_resp, 1); } /* {{{ proto RedisCluster::object(string subcmd, string key) */ PHP_METHOD(RedisCluster, object) { CLUSTER_PROCESS_CMD(object, cluster_object_resp, 1); } /* {{{ proto null RedisCluster::subscribe(array chans, callable cb) */ PHP_METHOD(RedisCluster, subscribe) { CLUSTER_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp, 0); } /* }}} */ /* {{{ proto null RedisCluster::psubscribe(array pats, callable cb) */ PHP_METHOD(RedisCluster, psubscribe) { CLUSTER_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp, 0); } /* }}} */ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, char *kw) { char *cmd; int cmd_len; void *ctx; short slot; // There is not reason to unsubscribe outside of a subscribe loop if (c->subscribed_slot == -1) { php_error_docref(0, E_WARNING, "You can't unsubscribe outside of a subscribe loop"); RETURN_FALSE; } // Call directly because we're going to set the slot manually if (redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, &slot, &ctx) == FAILURE) { RETURN_FALSE; } // This has to operate on our subscribe slot if (cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK ) == FAILURE) { CLUSTER_THROW_EXCEPTION("Failed to UNSUBSCRIBE within our subscribe loop!", 0); RETURN_FALSE; } // Now process response from the slot we're subscribed on cluster_unsub_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); // Cleanup our command efree(cmd); } /* {{{ proto array RedisCluster::unsubscribe(array chans) */ PHP_METHOD(RedisCluster, unsubscribe) { generic_unsub_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), "UNSUBSCRIBE"); } /* }}} */ /* {{{ proto array RedisCluster::punsubscribe(array pats) */ PHP_METHOD(RedisCluster, punsubscribe) { generic_unsub_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), "PUNSUBSCRIBE"); } /* }}} */ /* {{{ proto mixed RedisCluster::eval(string script, [array args, int numkeys) */ PHP_METHOD(RedisCluster, eval) { CLUSTER_PROCESS_KW_CMD("EVAL", redis_eval_cmd, cluster_variant_raw_resp, 0); } /* }}} */ /* {{{ proto mixed RedisCluster::eval_ro(string script, [array args, int numkeys) */ PHP_METHOD(RedisCluster, eval_ro) { CLUSTER_PROCESS_KW_CMD("EVAL_RO", redis_eval_cmd, cluster_variant_raw_resp, 1); } /* }}} */ /* {{{ proto mixed RedisCluster::evalsha(string sha, [array args, int numkeys]) */ PHP_METHOD(RedisCluster, evalsha) { CLUSTER_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, cluster_variant_raw_resp, 0); } /* }}} */ /* {{{ proto mixed RedisCluster::evalsha_ro(string sha, [array args, int numkeys]) */ PHP_METHOD(RedisCluster, evalsha_ro) { CLUSTER_PROCESS_KW_CMD("EVALSHA_RO", redis_eval_cmd, cluster_variant_raw_resp, 1); } /* }}} */ /* Commands that do not interact with Redis, but just report stuff about * various options, etc */ /* {{{ proto string RedisCluster::getmode() */ PHP_METHOD(RedisCluster, getmode) { redisCluster *c = GET_CONTEXT(); RETURN_LONG(c->flags->mode); } /* }}} */ /* {{{ proto string RedisCluster::getlasterror() */ PHP_METHOD(RedisCluster, getlasterror) { redisCluster *c = GET_CONTEXT(); if (c->err) { RETURN_STRINGL(ZSTR_VAL(c->err), ZSTR_LEN(c->err)); } RETURN_NULL(); } /* }}} */ /* {{{ proto bool RedisCluster::clearlasterror() */ PHP_METHOD(RedisCluster, clearlasterror) { redisCluster *c = GET_CONTEXT(); if (c->err) { zend_string_release(c->err); c->err = NULL; } RETURN_TRUE; } static void redisSumNodeBytes(redisClusterNode *node, zend_long *tx, zend_long *rx) { struct redisClusterNode *slave; *tx += node->sock->txBytes; *rx += node->sock->rxBytes; if (node->slaves) { ZEND_HASH_FOREACH_PTR(node->slaves, slave) { *tx += slave->sock->txBytes; *rx += slave->sock->rxBytes; } ZEND_HASH_FOREACH_END(); } } static void redisClearNodeBytes(redisClusterNode *node) { struct redisClusterNode *slave; node->sock->txBytes = 0; node->sock->rxBytes = 0; if (node->slaves) { ZEND_HASH_FOREACH_PTR(node->slaves, slave) { slave->sock->txBytes = 0; slave->sock->rxBytes = 0; } ZEND_HASH_FOREACH_END(); } } PHP_METHOD(RedisCluster, gettransferredbytes) { redisCluster *c = GET_CONTEXT(); zend_long rx = 0, tx = 0; redisClusterNode *node; ZEND_HASH_FOREACH_PTR(c->nodes, node) { redisSumNodeBytes(node, &tx, &rx); } ZEND_HASH_FOREACH_END(); array_init_size(return_value, 2); add_next_index_long(return_value, tx); add_next_index_long(return_value, rx); } /* }}} */ PHP_METHOD(RedisCluster, cleartransferredbytes) { redisCluster *c = GET_CONTEXT(); redisClusterNode *node; ZEND_HASH_FOREACH_PTR(c->nodes, node) { redisClearNodeBytes(node); } ZEND_HASH_FOREACH_END(); } /* {{{ proto long RedisCluster::getOption(long option */ PHP_METHOD(RedisCluster, getoption) { redisCluster *c = GET_CONTEXT(); redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, c); } /* }}} */ /* {{{ proto bool RedisCluster::setOption(long option, mixed value) */ PHP_METHOD(RedisCluster, setoption) { redisCluster *c = GET_CONTEXT(); redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, c); } /* }}} */ /* {{{ proto string RedisCluster::_prefix(string key) */ PHP_METHOD(RedisCluster, _prefix) { redisCluster *c = GET_CONTEXT(); redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags); } /* }}} */ /* {{{ proto string RedisCluster::_serialize(mixed val) */ PHP_METHOD(RedisCluster, _serialize) { redisCluster *c = GET_CONTEXT(); redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags); } /* }}} */ /* {{{ proto mixed RedisCluster::_unserialize(string val) */ PHP_METHOD(RedisCluster, _unserialize) { redisCluster *c = GET_CONTEXT(); redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, redis_cluster_exception_ce); } /* }}} */ PHP_METHOD(RedisCluster, _compress) { redisCluster *c = GET_CONTEXT(); redis_compress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags); } PHP_METHOD(RedisCluster, _uncompress) { redisCluster *c = GET_CONTEXT(); redis_uncompress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, redis_cluster_exception_ce); } PHP_METHOD(RedisCluster, _pack) { redisCluster *c = GET_CONTEXT(); redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags); } PHP_METHOD(RedisCluster, _unpack) { redisCluster *c = GET_CONTEXT(); redis_unpack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags); } /* {{{ proto array RedisCluster::_masters() */ PHP_METHOD(RedisCluster, _masters) { redisCluster *c = GET_CONTEXT(); redisClusterNode *node; array_init(return_value); ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) break; zval z_sub; array_init(&z_sub); add_next_index_stringl(&z_sub, ZSTR_VAL(node->sock->host), ZSTR_LEN(node->sock->host)); add_next_index_long(&z_sub, node->sock->port); add_next_index_zval(return_value, &z_sub); } ZEND_HASH_FOREACH_END(); } PHP_METHOD(RedisCluster, _redir) { redisCluster *c = GET_CONTEXT(); char buf[255]; size_t len; len = snprintf(buf, sizeof(buf), "%s:%d", c->redir_host, c->redir_port); if (*c->redir_host && c->redir_host_len) { RETURN_STRINGL(buf, len); } else { RETURN_NULL(); } } /* * Transaction handling */ /* {{{ proto bool RedisCluster::multi() */ PHP_METHOD(RedisCluster, multi) { redisCluster *c = GET_CONTEXT(); zend_long value = MULTI; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(value) ZEND_PARSE_PARAMETERS_END(); if (value != MULTI) { php_error_docref(NULL, E_WARNING, "RedisCluster does not support PIPELINING"); } if (c->flags->mode == MULTI) { php_error_docref(NULL, E_WARNING, "RedisCluster is already in MULTI mode, ignoring"); RETURN_FALSE; } /* Flag that we're in MULTI mode */ c->flags->mode = MULTI; c->flags->txBytes = 0; c->flags->rxBytes = 0; /* Return our object so we can chain MULTI calls */ RETVAL_ZVAL(getThis(), 1, 0); } /* {{{ proto bool RedisCluster::watch() */ PHP_METHOD(RedisCluster, watch) { redisCluster *c = GET_CONTEXT(); HashTable *ht_dist; clusterDistList *dl; smart_string cmd = {0}; zval *z_args; int argc = ZEND_NUM_ARGS(), i; zend_ulong slot; zend_string *zstr; // Disallow in MULTI mode if (c->flags->mode == MULTI) { php_error_docref(NULL, E_WARNING, "WATCH command not allowed in MULTI mode"); RETURN_FALSE; } // Don't need to process zero arguments if (!argc) RETURN_FALSE; // Create our distribution HashTable ht_dist = cluster_dist_create(); // Allocate args, and grab them z_args = emalloc(sizeof(zval) * argc); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); cluster_dist_free(ht_dist); RETURN_FALSE; } // Loop through arguments, prefixing if needed for(i = 0 ; i < argc; i++) { // We'll need the key as a string zstr = zval_get_string(&z_args[i]); // Add this key to our distribution handler if (cluster_dist_add_key(c, ht_dist, ZSTR_VAL(zstr), ZSTR_LEN(zstr), NULL) == FAILURE) { CLUSTER_THROW_EXCEPTION("Can't issue WATCH command as the keyspace isn't fully mapped", 0); zend_string_release(zstr); RETURN_FALSE; } zend_string_release(zstr); } // Iterate over each node we'll be sending commands to ZEND_HASH_FOREACH_PTR(ht_dist, dl) { // Grab the clusterDistList pointer itself if (dl == NULL) { CLUSTER_THROW_EXCEPTION("Internal error in a PHP HashTable", 0); cluster_dist_free(ht_dist); efree(z_args); efree(cmd.c); RETURN_FALSE; } else if (zend_hash_get_current_key(ht_dist, NULL, &slot) != HASH_KEY_IS_LONG) { break; } // Construct our watch command for this node redis_cmd_init_sstr(&cmd, dl->len, "WATCH", sizeof("WATCH")-1); for (i = 0; i < dl->len; i++) { redis_cmd_append_sstr(&cmd, dl->entry[i].key, dl->entry[i].key_len); } // If we get a failure from this, we have to abort if (cluster_send_command(c,(short)slot,cmd.c,cmd.len) ==-1) { RETURN_FALSE; } // This node is watching SLOT_SOCK(c, (short)slot)->watching = 1; // Zero out our command buffer cmd.len = 0; } ZEND_HASH_FOREACH_END(); // Cleanup cluster_dist_free(ht_dist); efree(z_args); efree(cmd.c); RETURN_TRUE; } /* {{{ proto bool RedisCluster::unwatch() */ PHP_METHOD(RedisCluster, unwatch) { redisCluster *c = GET_CONTEXT(); short slot; // Send UNWATCH to nodes that need it for(slot = 0; slot < REDIS_CLUSTER_SLOTS; slot++) { if (c->master[slot] && SLOT_SOCK(c,slot)->watching) { if (cluster_send_slot(c, slot, RESP_UNWATCH_CMD, sizeof(RESP_UNWATCH_CMD)-1, TYPE_LINE) ==-1) { CLUSTER_RETURN_BOOL(c, 0); } // No longer watching SLOT_SOCK(c,slot)->watching = 0; } } CLUSTER_RETURN_BOOL(c, 1); } /* {{{ proto array RedisCluster::exec() */ PHP_METHOD(RedisCluster, exec) { redisCluster *c = GET_CONTEXT(); clusterFoldItem *fi; // Verify we are in fact in multi mode if (CLUSTER_IS_ATOMIC(c)) { php_error_docref(NULL, E_WARNING, "RedisCluster is not in MULTI mode"); RETURN_FALSE; } // First pass, send EXEC and abort on failure fi = c->multi_head; while (fi) { if (SLOT_SOCK(c, fi->slot)->mode == MULTI) { if ( cluster_send_exec(c, fi->slot) < 0) { cluster_abort_exec(c); CLUSTER_THROW_EXCEPTION("Error processing EXEC across the cluster", 0); // Free our queue, reset MULTI state CLUSTER_FREE_QUEUE(c); CLUSTER_RESET_MULTI(c); RETURN_FALSE; } SLOT_SOCK(c, fi->slot)->mode = ATOMIC; SLOT_SOCK(c, fi->slot)->watching = 0; } fi = fi->next; } // MULTI multi-bulk response handler cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); // Free our callback queue, any enqueued distributed command context items // and reset our MULTI state. CLUSTER_FREE_QUEUE(c); CLUSTER_RESET_MULTI(c); } /* {{{ proto bool RedisCluster::discard() */ PHP_METHOD(RedisCluster, discard) { redisCluster *c = GET_CONTEXT(); if (CLUSTER_IS_ATOMIC(c)) { php_error_docref(NULL, E_WARNING, "Cluster is not in MULTI mode"); RETURN_FALSE; } if (cluster_abort_exec(c) < 0) { CLUSTER_RESET_MULTI(c); } CLUSTER_FREE_QUEUE(c); RETURN_TRUE; } /* Get a slot either by key (string) or host/port array */ static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg) { size_t key_len; int key_free; zval *z_host, *z_port; short slot; char *key; zend_string *zstr; /* If it's a string, treat it as a key. Otherwise, look for a two * element array */ if (Z_TYPE_P(z_arg) ==IS_STRING || Z_TYPE_P(z_arg) ==IS_LONG || Z_TYPE_P(z_arg) ==IS_DOUBLE) { /* Allow for any scalar here */ zstr = zval_get_string(z_arg); key = ZSTR_VAL(zstr); key_len = ZSTR_LEN(zstr); /* Hash it */ key_free = redis_key_prefix(c->flags, &key, &key_len); slot = cluster_hash_key(key, key_len); zend_string_release(zstr); if (key_free) efree(key); } else if (Z_TYPE_P(z_arg) == IS_ARRAY && (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL && (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL && Z_TYPE_P(z_host) == IS_STRING && Z_TYPE_P(z_port) == IS_LONG ) { /* Attempt to find this specific node by host:port */ slot = cluster_find_slot(c,(const char *)Z_STRVAL_P(z_host), (unsigned short)Z_LVAL_P(z_port)); /* Inform the caller if they've passed bad data */ if (slot < 0) { php_error_docref(0, E_WARNING, "Unknown node %s:" ZEND_LONG_FMT, Z_STRVAL_P(z_host), Z_LVAL_P(z_port)); } } else { php_error_docref(0, E_WARNING, "Directed commands must be passed a key or [host,port] array"); return -1; } return slot; } /* Generic handler for things we want directed at a given node, like SAVE, * BGSAVE, FLUSHDB, FLUSHALL, etc */ static void cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply_type, cluster_cb cb) { redisCluster *c = GET_CONTEXT(); char *cmd; int cmd_len; zval *z_arg; short slot; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z_arg) == FAILURE) { RETURN_FALSE; } // One argument means find the node (treated like a key), and two means // send the command to a specific host and port slot = cluster_cmd_get_slot(c, z_arg); if (slot < 0) { RETURN_FALSE; } // Construct our command cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, ""); // Kick off our command if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0); efree(cmd); RETURN_FALSE; } // Our response callback cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); // Free our command efree(cmd); } static void cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply_type, cluster_cb cb) { redisCluster *c = GET_CONTEXT(); char *cmd; int cmd_len; zval *z_arg; zend_bool async = 0; short slot; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &z_arg, &async) == FAILURE) { RETURN_FALSE; } // One argument means find the node (treated like a key), and two means // send the command to a specific host and port slot = cluster_cmd_get_slot(c, z_arg); if (slot < 0) { RETURN_FALSE; } // Construct our command if (async) { cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1); } else { cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, ""); } // Kick off our command if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0); efree(cmd); RETURN_FALSE; } // Our response callback cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); // Free our command efree(cmd); } /* Generic routine for handling various commands which need to be directed at * a node, but have complex syntax. We simply parse out the arguments and send * the command as constructed by the caller */ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { redisCluster *c = GET_CONTEXT(); smart_string cmd = {0}; zval *z_args; short slot; int i, argc = ZEND_NUM_ARGS(); /* Commands using this pass-thru don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { php_error_docref(0, E_WARNING, "Command can't be issued in MULTI mode"); RETURN_FALSE; } /* We at least need the key or [host,port] argument */ if (argc < 1) { php_error_docref(0, E_WARNING, "Command requires at least an argument to direct to a node"); RETURN_FALSE; } /* Allocate an array to process arguments */ z_args = emalloc(argc * sizeof(zval)); /* Grab args */ if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); RETURN_FALSE; } /* First argument needs to be the "where" */ if ((slot = cluster_cmd_get_slot(c, &z_args[0])) < 0) { efree(z_args); RETURN_FALSE; } /* Initialize our command */ redis_cmd_init_sstr(&cmd, argc-1, kw, kw_len); /* Iterate, appending args */ for(i = 1; i < argc; i++) { zend_string *zstr = zval_get_string(&z_args[i]); redis_cmd_append_sstr(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } /* Send it off */ if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0); efree(cmd.c); efree(z_args); RETURN_FALSE; } /* Read the response variant */ cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); efree(cmd.c); efree(z_args); } /* Generic method for HSCAN, SSCAN, and ZSCAN */ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { redisCluster *c = GET_CONTEXT(); char *cmd, *pat = NULL, *key = NULL; size_t key_len = 0, pat_len = 0, pat_free = 0; int cmd_len, key_free = 0; short slot; zval *z_it; HashTable *hash; long it, num_ele; zend_long count = 0; // Can't be in MULTI mode if (!CLUSTER_IS_ATOMIC(c)) { CLUSTER_THROW_EXCEPTION("SCAN type commands can't be called in MULTI mode!", 0); RETURN_FALSE; } /* Parse arguments */ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/|s!l", &key, &key_len, &z_it, &pat, &pat_len, &count) == FAILURE) { RETURN_FALSE; } /* Treat as readonly */ c->readonly = 1; // Convert iterator to long if it isn't, update our long iterator if it's // set and >0, and finish if it's back to zero if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) { convert_to_long(z_it); it = 0; } else if (Z_LVAL_P(z_it) != 0) { it = Z_LVAL_P(z_it); } else { RETURN_FALSE; } // Apply any key prefix we have, get the slot key_free = redis_key_prefix(c->flags, &key, &key_len); slot = cluster_hash_key(key, key_len); if (c->flags->scan & REDIS_SCAN_PREFIX) { pat_free = redis_key_prefix(c->flags, &pat, &pat_len); } // If SCAN_RETRY is set, loop until we get a zero iterator or until // we get non-zero elements. Otherwise we just send the command once. do { /* Free our return value if we're back in the loop */ if (Z_TYPE_P(return_value) == IS_ARRAY) { zval_dtor(return_value); ZVAL_NULL(return_value); } // Create command cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, it, pat, pat_len, count); // Send it off if (cluster_send_command(c, slot, cmd, cmd_len) == FAILURE) { CLUSTER_THROW_EXCEPTION("Couldn't send SCAN command", 0); if (key_free) efree(key); efree(cmd); RETURN_FALSE; } // Read response if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, &it) == FAILURE) { CLUSTER_THROW_EXCEPTION("Couldn't read SCAN response", 0); if (key_free) efree(key); efree(cmd); RETURN_FALSE; } // Count the elements we got back hash = Z_ARRVAL_P(return_value); num_ele = zend_hash_num_elements(hash); // Free our command efree(cmd); } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0); // Free our pattern if (pat_free) efree(pat); // Free our key if (key_free) efree(key); // Update iterator reference Z_LVAL_P(z_it) = it; } static int redis_acl_op_readonly(zend_string *op) { /* Only return read-only for operations we know to be */ if (ZSTR_STRICMP_STATIC(op, "LIST") || ZSTR_STRICMP_STATIC(op, "USERS") || ZSTR_STRICMP_STATIC(op, "GETUSER") || ZSTR_STRICMP_STATIC(op, "CAT") || ZSTR_STRICMP_STATIC(op, "GENPASS") || ZSTR_STRICMP_STATIC(op, "WHOAMI") || ZSTR_STRICMP_STATIC(op, "LOG")) return 1; return 0; } PHP_METHOD(RedisCluster, acl) { redisCluster *c = GET_CONTEXT(); smart_string cmdstr = {0}; int argc = ZEND_NUM_ARGS(), i, readonly; cluster_cb cb; zend_string *zs; zval *zargs; void *ctx = NULL; short slot; /* ACL in cluster needs a slot argument, and then at least the op */ if (argc < 2) { WRONG_PARAM_COUNT; RETURN_FALSE; } /* Grab all our arguments and determine the command slot */ zargs = emalloc(argc * sizeof(*zargs)); if (zend_get_parameters_array(ht, argc, zargs) == FAILURE || (slot = cluster_cmd_get_slot(c, &zargs[0]) < 0)) { efree(zargs); RETURN_FALSE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc - 1, "ACL"); /* Read the op, determin if it's readonly, and add it */ zs = zval_get_string(&zargs[1]); readonly = redis_acl_op_readonly(zs); redis_cmd_append_sstr_zstr(&cmdstr, zs); /* We have specialized handlers for GETUSER and LOG, whereas every * other ACL command can be handled generically */ if (zend_string_equals_literal_ci(zs, "GETUSER")) { cb = cluster_acl_getuser_resp; } else if (zend_string_equals_literal_ci(zs, "LOG")) { cb = cluster_acl_log_resp; } else { cb = cluster_variant_resp; } zend_string_release(zs); /* Process remaining args */ for (i = 2; i < argc; i++) { zs = zval_get_string(&zargs[i]); redis_cmd_append_sstr_zstr(&cmdstr, zs); zend_string_release(zs); } /* Can we use replicas? */ c->readonly = readonly && CLUSTER_IS_ATOMIC(c); /* Kick off our command */ if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, TYPE_EOF) < 0) { CLUSTER_THROW_EXCEPTION("Unabler to send ACL command", 0); efree(zargs); RETURN_FALSE; } if (CLUSTER_IS_ATOMIC(c)) { cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); } efree(cmdstr.c); efree(zargs); } /* {{{ proto RedisCluster::scan(string master, long it [, string pat, long cnt]) */ PHP_METHOD(RedisCluster, scan) { redisCluster *c = GET_CONTEXT(); char *cmd, *pat = NULL; size_t pat_len = 0; int cmd_len; short slot; zval *z_it, *z_node; long it, num_ele, pat_free = 0; zend_long count = 0; /* Treat as read-only */ c->readonly = CLUSTER_IS_ATOMIC(c); /* Can't be in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { CLUSTER_THROW_EXCEPTION("SCAN type commands can't be called in MULTI mode", 0); RETURN_FALSE; } /* Parse arguments */ if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/z|s!l", &z_it, &z_node, &pat, &pat_len, &count) == FAILURE) { RETURN_FALSE; } /* Convert or update iterator */ if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) { convert_to_long(z_it); it = 0; } else if (Z_LVAL_P(z_it) != 0) { it = Z_LVAL_P(z_it); } else { RETURN_FALSE; } if (c->flags->scan & REDIS_SCAN_PREFIX) { pat_free = redis_key_prefix(c->flags, &pat, &pat_len); } /* With SCAN_RETRY on, loop until we get some keys, otherwise just return * what Redis does, as it does */ do { /* Free our return value if we're back in the loop */ if (Z_TYPE_P(return_value) == IS_ARRAY) { zval_dtor(return_value); ZVAL_NULL(return_value); } /* Construct our command */ cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, count); if ((slot = cluster_cmd_get_slot(c, z_node)) < 0) { RETURN_FALSE; } // Send it to the node in question if (cluster_send_command(c, slot, cmd, cmd_len) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send SCAN to node", 0); efree(cmd); RETURN_FALSE; } if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, TYPE_SCAN, &it) == FAILURE || Z_TYPE_P(return_value)!=IS_ARRAY) { CLUSTER_THROW_EXCEPTION("Couldn't process SCAN response from node", 0); efree(cmd); RETURN_FALSE; } efree(cmd); num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value)); } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0); if (pat_free) efree(pat); Z_LVAL_P(z_it) = it; } /* }}} */ /* {{{ proto RedisCluster::sscan(string key, long it [string pat, long cnt]) */ PHP_METHOD(RedisCluster, sscan) { cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN); } /* }}} */ /* {{{ proto RedisCluster::zscan(string key, long it [string pat, long cnt]) */ PHP_METHOD(RedisCluster, zscan) { cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN); } /* }}} */ /* {{{ proto RedisCluster::hscan(string key, long it [string pat, long cnt]) */ PHP_METHOD(RedisCluster, hscan) { cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN); } /* }}} */ /* {{{ proto RedisCluster::save(string key) * proto RedisCluster::save(array host_port) */ PHP_METHOD(RedisCluster, save) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE", TYPE_LINE, cluster_bool_resp); } /* }}} */ /* {{{ proto RedisCluster::bgsave(string key) * proto RedisCluster::bgsave(array host_port) */ PHP_METHOD(RedisCluster, bgsave) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE", TYPE_LINE, cluster_bool_resp); } /* }}} */ /* {{{ proto RedisCluster::flushdb(string key, [bool async]) * proto RedisCluster::flushdb(array host_port, [bool async]) */ PHP_METHOD(RedisCluster, flushdb) { cluster_flush_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB", TYPE_LINE, cluster_bool_resp); } /* }}} */ /* {{{ proto RedisCluster::flushall(string key, [bool async]) * proto RedisCluster::flushall(array host_port, [bool async]) */ PHP_METHOD(RedisCluster, flushall) { cluster_flush_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL", TYPE_LINE, cluster_bool_resp); } /* }}} */ /* {{{ proto RedisCluster::dbsize(string key) * proto RedisCluster::dbsize(array host_port) */ PHP_METHOD(RedisCluster, dbsize) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DBSIZE", TYPE_INT, cluster_long_resp); } /* }}} */ /* {{{ proto RedisCluster::bgrewriteaof(string key) * proto RedisCluster::bgrewriteaof(array host_port) */ PHP_METHOD(RedisCluster, bgrewriteaof) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGREWRITEAOF", TYPE_LINE, cluster_bool_resp); } /* }}} */ /* {{{ proto RedisCluster::lastsave(string key) * proto RedisCluster::lastsave(array $host_port) */ PHP_METHOD(RedisCluster, lastsave) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LASTSAVE", TYPE_INT, cluster_long_resp); } /* }}} */ /* {{{ proto array RedisCluster::info(string key, [string $arg]) * proto array RedisCluster::info(array host_port, [string $arg]) */ PHP_METHOD(RedisCluster, info) { redisCluster *c = GET_CONTEXT(); zval *node = NULL, *args = NULL; smart_string cmdstr = {0}; REDIS_REPLY_TYPE rtype; zend_string *section; void *ctx = NULL; int i, argc; short slot; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_ZVAL(node) Z_PARAM_OPTIONAL Z_PARAM_VARIADIC('*', args, argc) ZEND_PARSE_PARAMETERS_END(); if ((slot = cluster_cmd_get_slot(c, node)) < 0) RETURN_FALSE; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "INFO"); /* Direct this command at the master */ c->readonly = 0; for (i = 0; i < argc; i++) { section = zval_get_string(&args[i]); redis_cmd_append_sstr_zstr(&cmdstr, section); zend_string_release(section); } rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send INFO command to specific node", 0); efree(cmdstr.c); RETURN_FALSE; } if (CLUSTER_IS_ATOMIC(c)) { cluster_info_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_info_resp, ctx); } efree(cmdstr.c); } /* }}} */ /* {{{ proto array RedisCluster::client('list') * proto bool RedisCluster::client('kill', $ipport) * proto bool RedisCluster::client('setname', $name) * proto string RedisCluster::client('getname') */ PHP_METHOD(RedisCluster, client) { redisCluster *c = GET_CONTEXT(); char *cmd, *opt = NULL, *arg = NULL; int cmd_len; size_t opt_len, arg_len = 0; REDIS_REPLY_TYPE rtype; zval *z_node; short slot; cluster_cb cb; /* Parse args */ if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs|s", &z_node, &opt, &opt_len, &arg, &arg_len) == FAILURE) { RETURN_FALSE; } /* Make sure we can properly resolve the slot */ slot = cluster_cmd_get_slot(c, z_node); if (slot < 0) RETURN_FALSE; /* Our return type and reply callback is different for all subcommands */ if (opt_len == 4 && !strncasecmp(opt, "list", 4)) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; cb = cluster_client_list_resp; } else if ((opt_len == 4 && !strncasecmp(opt, "kill", 4)) || (opt_len == 7 && !strncasecmp(opt, "setname", 7))) { rtype = TYPE_LINE; cb = cluster_bool_resp; } else if (opt_len == 7 && !strncasecmp(opt, "getname", 7)) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; cb = cluster_bulk_resp; } else { php_error_docref(NULL, E_WARNING, "Invalid CLIENT subcommand (LIST, KILL, GETNAME, and SETNAME are valid"); RETURN_FALSE; } /* Construct the command */ if (ZEND_NUM_ARGS() == 3) { cmd_len = redis_spprintf(NULL, NULL, &cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len); } else if (ZEND_NUM_ARGS() == 2) { cmd_len = redis_spprintf(NULL, NULL, &cmd, "CLIENT", "s", opt, opt_len); } else { zend_wrong_param_count(); RETURN_FALSE; } /* Attempt to write our command */ if (cluster_send_slot(c, slot, cmd, cmd_len, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send CLIENT command to specific node", 0); efree(cmd); RETURN_FALSE; } /* Now enqueue or process response */ if (CLUSTER_IS_ATOMIC(c)) { cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { void *ctx = NULL; CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); } efree(cmd); } /* {{{ proto mixed RedisCluster::cluster(variant) */ PHP_METHOD(RedisCluster, cluster) { cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CLUSTER", sizeof("CLUSTER")-1); } /* }}} */ /* }}} */ /* {{{ proto mixed RedisCluster::config(string key, ...) * proto mixed RedisCluster::config(array host_port, ...) */ PHP_METHOD(RedisCluster, config) { cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CONFIG", sizeof("CONFIG")-1); } /* }}} */ /* {{{ proto mixed RedisCluster::pubsub(string key, ...) * proto mixed RedisCluster::pubsub(array host_port, ...) */ PHP_METHOD(RedisCluster, pubsub) { cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUBSUB", sizeof("PUBSUB")-1); } /* }}} */ /* {{{ proto mixed RedisCluster::script(string key, ...) * proto mixed RedisCluster::script(array host_port, ...) */ PHP_METHOD(RedisCluster, script) { redisCluster *c = GET_CONTEXT(); smart_string cmd = {0}; zval *z_args; short slot; int argc = ZEND_NUM_ARGS(); /* Commands using this pass-thru don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { php_error_docref(0, E_WARNING, "Command can't be issued in MULTI mode"); RETURN_FALSE; } /* We at least need the key or [host,port] argument */ if (argc < 2) { php_error_docref(0, E_WARNING, "Command requires at least an argument to direct to a node"); RETURN_FALSE; } /* Allocate an array to process arguments */ z_args = ecalloc(argc, sizeof(zval)); /* Grab args */ if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || (slot = cluster_cmd_get_slot(c, &z_args[0])) < 0 || redis_build_script_cmd(&cmd, argc - 1, &z_args[1]) == NULL ) { efree(z_args); RETURN_FALSE; } /* Send it off */ if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0); efree(cmd.c); efree(z_args); RETURN_FALSE; } /* Read the response variant */ cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); efree(cmd.c); efree(z_args); } /* }}} */ /* {{{ proto mixed RedisCluster::slowlog(string key, ...) * proto mixed RedisCluster::slowlog(array host_port, ...) */ PHP_METHOD(RedisCluster, slowlog) { cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SLOWLOG", sizeof("SLOWLOG")-1); } /* }}} */ /* {{{ proto int RedisCluster::geoadd(string key, float long float lat string mem, ...) */ PHP_METHOD(RedisCluster, geoadd) { CLUSTER_PROCESS_CMD(geoadd, cluster_long_resp, 0); } /* {{{ proto array RedisCluster::geohash(string key, string mem1, [string mem2...]) */ PHP_METHOD(RedisCluster, geohash) { CLUSTER_PROCESS_KW_CMD("GEOHASH", redis_key_varval_cmd, cluster_mbulk_raw_resp, 1); } /* {{{ proto array RedisCluster::geopos(string key, string mem1, [string mem2...]) */ PHP_METHOD(RedisCluster, geopos) { CLUSTER_PROCESS_KW_CMD("GEOPOS", redis_key_varval_cmd, cluster_variant_resp, 1); } /* {{{ proto array RedisCluster::geodist(string key, string mem1, string mem2 [string unit]) */ PHP_METHOD(RedisCluster, geodist) { CLUSTER_PROCESS_CMD(geodist, cluster_dbl_resp, 1); } /* {{{ proto array RedisCluster::georadius() }}} */ PHP_METHOD(RedisCluster, georadius) { CLUSTER_PROCESS_KW_CMD("GEORADIUS", redis_georadius_cmd, cluster_variant_resp, 1); } /* {{{ proto array RedisCluster::georadius() }}} */ PHP_METHOD(RedisCluster, georadius_ro) { CLUSTER_PROCESS_KW_CMD("GEORADIUS_RO", redis_georadius_cmd, cluster_variant_resp, 1); } /* {{{ proto array RedisCluster::georadiusbymember() }}} */ PHP_METHOD(RedisCluster, georadiusbymember) { CLUSTER_PROCESS_KW_CMD("GEORADIUSBYMEMBER", redis_georadiusbymember_cmd, cluster_variant_resp, 1); } /* {{{ proto array RedisCluster::georadiusbymember() }}} */ PHP_METHOD(RedisCluster, georadiusbymember_ro) { CLUSTER_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, cluster_variant_resp, 1); } PHP_METHOD(RedisCluster, geosearch) { CLUSTER_PROCESS_CMD(geosearch, cluster_geosearch_resp, 1); } PHP_METHOD(RedisCluster, geosearchstore) { CLUSTER_PROCESS_CMD(geosearchstore, cluster_long_resp, 0); } /* {{{ proto array RedisCluster::role(string key) * proto array RedisCluster::role(array host_port) */ PHP_METHOD(RedisCluster, role) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ROLE", TYPE_MULTIBULK, cluster_variant_resp); } /* {{{ proto array RedisCluster::time(string key) * proto array RedisCluster::time(array host_port) */ PHP_METHOD(RedisCluster, time) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "TIME", TYPE_MULTIBULK, cluster_variant_resp); } /* }}} */ /* {{{ proto string RedisCluster::randomkey(string key) * proto string RedisCluster::randomkey(array host_port) */ PHP_METHOD(RedisCluster, randomkey) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RANDOMKEY", TYPE_BULK, cluster_bulk_resp); } /* }}} */ /* {{{ proto bool RedisCluster::ping(string key| string msg) * proto bool RedisCluster::ping(array host_port| string msg) */ PHP_METHOD(RedisCluster, ping) { redisCluster *c = GET_CONTEXT(); REDIS_REPLY_TYPE rtype; void *ctx = NULL; zval *z_node; char *cmd, *arg = NULL; int cmdlen; size_t arglen; short slot; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!", &z_node, &arg, &arglen) == FAILURE) { RETURN_FALSE; } /* Treat this as a readonly command */ c->readonly = CLUSTER_IS_ATOMIC(c); /* Grab slot either by key or host/port */ slot = cluster_cmd_get_slot(c, z_node); if (slot < 0) { RETURN_FALSE; } /* Construct our command */ if (arg != NULL) { cmdlen = redis_spprintf(NULL, NULL, &cmd, "PING", "s", arg, arglen); } else { cmdlen = redis_spprintf(NULL, NULL, &cmd, "PING", ""); } /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) && arg != NULL ? TYPE_BULK : TYPE_LINE; if (cluster_send_slot(c, slot, cmd, cmdlen, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0); efree(cmd); RETURN_FALSE; } /* We're done with our command */ efree(cmd); /* Process response */ if (CLUSTER_IS_ATOMIC(c)) { if (arg != NULL) { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { /* If we're atomic and didn't send an argument then we have already * processed the reply (which must have been successful. */ RETURN_TRUE; } } else { if (arg != NULL) { CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx); } else { CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); } RETURN_ZVAL(getThis(), 1, 0); } } /* }}} */ /* {{{ proto long RedisCluster::xack(string key, string group, array ids) }}} */ PHP_METHOD(RedisCluster, xack) { CLUSTER_PROCESS_CMD(xack, cluster_long_resp, 0); } /* {{{ proto string RedisCluster::xadd(string key, string id, array field_values) }}} */ PHP_METHOD(RedisCluster, xadd) { CLUSTER_PROCESS_CMD(xadd, cluster_bulk_raw_resp, 0); } /* {{{ proto array RedisCluster::xclaim(string key, string group, string consumer, * long min_idle_time, array ids, array options) */ PHP_METHOD(RedisCluster, xclaim) { CLUSTER_PROCESS_CMD(xclaim, cluster_xclaim_resp, 0); } PHP_METHOD(RedisCluster, xautoclaim) { CLUSTER_PROCESS_CMD(xautoclaim, cluster_xclaim_resp, 0); } PHP_METHOD(RedisCluster, xdel) { CLUSTER_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, cluster_long_resp, 0); } /* {{{ proto variant RedisCluster::xgroup(string op, [string key, string arg1, string arg2]) }}} */ PHP_METHOD(RedisCluster, xgroup) { CLUSTER_PROCESS_CMD(xgroup, cluster_variant_resp, 0); } /* {{{ proto variant RedisCluster::xinfo(string op, [string arg1, string arg2]); */ PHP_METHOD(RedisCluster, xinfo) { CLUSTER_PROCESS_CMD(xinfo, cluster_xinfo_resp, 0); } /* {{{ proto string RedisCluster::xlen(string key) }}} */ PHP_METHOD(RedisCluster, xlen) { CLUSTER_PROCESS_KW_CMD("XLEN", redis_key_cmd, cluster_long_resp, 1); } PHP_METHOD(RedisCluster, xpending) { CLUSTER_PROCESS_CMD(xpending, cluster_variant_resp_strings, 1); } PHP_METHOD(RedisCluster, xrange) { CLUSTER_PROCESS_KW_CMD("XRANGE", redis_xrange_cmd, cluster_xrange_resp, 1); } PHP_METHOD(RedisCluster, xrevrange) { CLUSTER_PROCESS_KW_CMD("XREVRANGE", redis_xrange_cmd, cluster_xrange_resp, 1); } PHP_METHOD(RedisCluster, xread) { CLUSTER_PROCESS_CMD(xread, cluster_xread_resp, 1); } PHP_METHOD(RedisCluster, xreadgroup) { CLUSTER_PROCESS_CMD(xreadgroup, cluster_xread_resp, 0); } PHP_METHOD(RedisCluster, xtrim) { CLUSTER_PROCESS_CMD(xtrim, cluster_long_resp, 0); } /* {{{ proto string RedisCluster::echo(string key, string msg) * proto string RedisCluster::echo(array host_port, string msg) */ PHP_METHOD(RedisCluster, echo) { redisCluster *c = GET_CONTEXT(); REDIS_REPLY_TYPE rtype; zval *z_arg; char *cmd, *msg; int cmd_len; size_t msg_len; short slot; if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs", &z_arg, &msg, &msg_len) == FAILURE) { RETURN_FALSE; } /* Treat this as a readonly command */ c->readonly = CLUSTER_IS_ATOMIC(c); /* Grab slot either by key or host/port */ slot = cluster_cmd_get_slot(c, z_arg); if (slot < 0) { RETURN_FALSE; } /* Construct our command */ cmd_len = redis_spprintf(NULL, NULL, &cmd, "ECHO", "s", msg, msg_len); /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0); efree(cmd); RETURN_FALSE; } /* Process bulk response */ if (CLUSTER_IS_ATOMIC(c)) { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { void *ctx = NULL; CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx); } efree(cmd); } /* }}} */ /* {{{ proto mixed RedisCluster::rawcommand(string $key, string $cmd, [ $argv1 .. $argvN]) * proto mixed RedisCluster::rawcommand(array $host_port, string $cmd, [ $argv1 .. $argvN]) */ PHP_METHOD(RedisCluster, rawcommand) { REDIS_REPLY_TYPE rtype; int argc = ZEND_NUM_ARGS(), cmd_len; redisCluster *c = GET_CONTEXT(); char *cmd = NULL; zval *z_args; short slot; /* Sanity check on our arguments */ if (argc < 2) { php_error_docref(NULL, E_WARNING, "You must pass at least node information as well as at least a command."); 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 method parameters."); efree(z_args); RETURN_FALSE; } else if (redis_build_raw_cmd(&z_args[1], argc-1, &cmd, &cmd_len) || (slot = cluster_cmd_get_slot(c, &z_args[0])) < 0) { if (cmd) efree(cmd); efree(z_args); RETURN_FALSE; } /* Free argument array */ efree(z_args); /* Direct the command */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE; if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command to the specified node", 0); efree(cmd); RETURN_FALSE; } /* Process variant response */ if (CLUSTER_IS_ATOMIC(c)) { cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { void *ctx = NULL; CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_raw_resp, ctx); } efree(cmd); } /* }}} */ /* {{{ proto array RedisCluster::command() * proto array RedisCluster::command('INFO', string cmd) * proto array RedisCluster::command('GETKEYS', array cmd_args) */ PHP_METHOD(RedisCluster, command) { CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); } PHP_METHOD(RedisCluster, copy) { CLUSTER_PROCESS_CMD(copy, cluster_1_resp, 0) } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ redis-6.0.2/redis_cluster.h0000644000175000000120000000663014515245367016445 0ustar pyatsukhnenkowheel#ifndef REDIS_CLUSTER_H #define REDIS_CLUSTER_H #include "cluster_library.h" #include #include /* Get attached object context */ #define GET_CONTEXT() PHPREDIS_ZVAL_GET_OBJECT(redisCluster, getThis()) /* Command building/processing is identical for every command */ #define CLUSTER_BUILD_CMD(name, c, cmd, cmd_len, slot) \ redis_##name##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, \ &cmd_len, &slot) /* Append information required to handle MULTI commands to the tail of our MULTI * linked list. */ #define CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx) \ clusterFoldItem *_item; \ _item = emalloc(sizeof(clusterFoldItem)); \ _item->callback = cb; \ _item->slot = slot; \ _item->ctx = ctx; \ _item->next = NULL; \ if(c->multi_head == NULL) { \ c->multi_head = _item; \ c->multi_curr = _item; \ } else { \ c->multi_curr->next = _item; \ c->multi_curr = _item; \ } \ /* Simple macro to free our enqueued callbacks after we EXEC */ #define CLUSTER_FREE_QUEUE(c) \ clusterFoldItem *_item = c->multi_head, *_tmp; \ while(_item) { \ _tmp = _item->next; \ efree(_item); \ _item = _tmp; \ } \ c->multi_head = c->multi_curr = NULL; \ /* Reset anything flagged as MULTI */ #define CLUSTER_RESET_MULTI(c) \ redisClusterNode *_node; \ ZEND_HASH_FOREACH_PTR(c->nodes, _node) { \ if (_node == NULL) break; \ _node->sock->watching = 0; \ _node->sock->mode = ATOMIC; \ } ZEND_HASH_FOREACH_END(); \ c->flags->watching = 0; \ c->flags->mode = ATOMIC; \ /* Simple 1-1 command -> response macro */ #define CLUSTER_PROCESS_CMD(cmdname, resp_func, readcmd) \ redisCluster *c = GET_CONTEXT(); \ c->readonly = CLUSTER_IS_ATOMIC(c) && readcmd; \ char *cmd; int cmd_len; short slot; void *ctx=NULL; \ if(redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,c->flags, &cmd, \ &cmd_len, &slot, &ctx)==FAILURE) { \ RETURN_FALSE; \ } \ if(cluster_send_command(c,slot,cmd,cmd_len)<0 || c->err!=NULL) {\ efree(cmd); \ RETURN_FALSE; \ } \ efree(cmd); \ if(c->flags->mode == MULTI) { \ CLUSTER_ENQUEUE_RESPONSE(c, slot, resp_func, ctx); \ RETURN_ZVAL(getThis(), 1, 0); \ } \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); /* More generic processing, where only the keyword differs */ #define CLUSTER_PROCESS_KW_CMD(kw, cmdfunc, resp_func, readcmd) \ redisCluster *c = GET_CONTEXT(); \ c->readonly = CLUSTER_IS_ATOMIC(c) && readcmd; \ char *cmd; int cmd_len; short slot; void *ctx=NULL; \ if(cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,\ &slot,&ctx)==FAILURE) { \ RETURN_FALSE; \ } \ if(cluster_send_command(c,slot,cmd,cmd_len)<0 || c->err!=NULL) { \ efree(cmd); \ RETURN_FALSE; \ } \ efree(cmd); \ if(c->flags->mode == MULTI) { \ CLUSTER_ENQUEUE_RESPONSE(c, slot, resp_func, ctx); \ RETURN_ZVAL(getThis(), 1, 0); \ } \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); extern zend_class_entry *redis_cluster_ce; extern zend_class_entry *redis_cluster_exception_ce; extern PHP_MINIT_FUNCTION(redis_cluster); extern zend_object * create_cluster_context(zend_class_entry *class_type); extern void free_cluster_context(zend_object *object); #endif redis-6.0.2/redis_cluster.stub.php0000644000175000000120000010110614515245367017753 0ustar pyatsukhnenkowheel= 7.0.0 you may pass multiple optional sections. * * @see https://redis.io/commands/info/ * * @param string|array $key_or_address Either a key name or array with host and port indicating * which cluster node we want to send the command to. * @param string $sections Optional section(s) you wish Redis server to return. * * @return RedisCluster|array|false */ public function info(string|array $key_or_address, string ...$sections): RedisCluster|array|false; /** * @see Redis::keys */ public function keys(string $pattern): RedisCluster|array|false; /** * @see Redis::lastsave */ public function lastsave(string|array $key_or_address): RedisCluster|int|false; /** * @see Redis::lget */ public function lget(string $key, int $index): RedisCluster|string|bool; /** * @see Redis::lindex */ public function lindex(string $key, int $index): mixed; /** * @see Redis::linsert */ public function linsert(string $key, string $pos, mixed $pivot, mixed $value): RedisCluster|int|false; /** * @see Redis::llen */ public function llen(string $key): RedisCluster|int|bool; /** * @see Redis::lpop */ public function lpop(string $key, int $count = 0): RedisCluster|bool|string|array; /** * @see Redis::lpos */ public function lpos(string $key, mixed $value, ?array $options = null): Redis|null|bool|int|array; /** * @see Redis::lpush */ public function lpush(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|bool; /** * @see Redis::lpushx */ public function lpushx(string $key, mixed $value): RedisCluster|int|bool; /** * @see Redis::lrange */ public function lrange(string $key, int $start, int $end): RedisCluster|array|false; /** * @see Redis::lrem */ public function lrem(string $key, mixed $value, int $count = 0): RedisCluster|int|bool; /** * @see Redis::lset */ public function lset(string $key, int $index, mixed $value): RedisCluster|bool; /** * @see Redis::ltrim */ public function ltrim(string $key, int $start, int $end): RedisCluster|bool; /** * @see Redis::mget */ public function mget(array $keys): RedisCluster|array|false; /** * @see Redis::mset */ public function mset(array $key_values): RedisCluster|bool; /** * @see Redis::msetnx */ public function msetnx(array $key_values): RedisCluster|array|false; /* We only support Redis::MULTI in RedisCluster but take the argument so we can test MULTI..EXEC with RedisTest.php and in the event we add pipeline support in the future. */ public function multi(int $value = Redis::MULTI): RedisCluster|bool; /** * @see Redis::object */ public function object(string $subcommand, string $key): RedisCluster|int|string|false; /** * @see Redis::persist */ public function persist(string $key): RedisCluster|bool; /** * @see Redis::pexpire */ public function pexpire(string $key, int $timeout, ?string $mode = null): RedisCluster|bool; /** * @see Redis::pexpireat */ public function pexpireat(string $key, int $timestamp, ?string $mode = null): RedisCluster|bool; /** * @see Redis::pfadd() */ public function pfadd(string $key, array $elements): RedisCluster|bool; /** * @see Redis::pfcount() */ public function pfcount(string $key): RedisCluster|int|false; /** * @see Redis::pfmerge() */ public function pfmerge(string $key, array $keys): RedisCluster|bool; /** * PING an instance in the redis cluster. * * @see Redis::ping() * * @param string|array $key_or_address Either a key name or a two element array with host and * address, informing RedisCluster which node to ping. * * @param string $message An optional message to send. * * @return mixed This method always returns `true` if no message was sent, and the message itself * if one was. */ public function ping(string|array $key_or_address, ?string $message = null): mixed; /** * @see Redis::psetex */ public function psetex(string $key, int $timeout, string $value): RedisCluster|bool; /** * @see Redis::psubscribe */ public function psubscribe(array $patterns, callable $callback): void; /** * @see Redis::pttl */ public function pttl(string $key): RedisCluster|int|false; /** * @see Redis::publish */ public function publish(string $channel, string $message): RedisCluster|bool; /** * @see Redis::pubsub */ public function pubsub(string|array $key_or_address, string ...$values): mixed; /** * @see Redis::punsubscribe */ public function punsubscribe(string $pattern, string ...$other_patterns): bool|array; /** * @see Redis::randomkey */ public function randomkey(string|array $key_or_address): RedisCluster|bool|string; /** * @see Redis::rawcommand */ public function rawcommand(string|array $key_or_address, string $command, mixed ...$args): mixed; /** * @see Redis::rename */ public function rename(string $key_src, string $key_dst): RedisCluster|bool; /** * @see Redis::renamenx */ public function renamenx(string $key, string $newkey): RedisCluster|bool; /** * @see Redis::restore */ public function restore(string $key, int $timeout, string $value, ?array $options = null): RedisCluster|bool; /** * @see Redis::role */ public function role(string|array $key_or_address): mixed; /** * @see Redis::rpop() */ public function rpop(string $key, int $count = 0): RedisCluster|bool|string|array; /** * @see Redis::rpoplpush() */ public function rpoplpush(string $src, string $dst): RedisCluster|bool|string; /** * @see Redis::rpush */ public function rpush(string $key, mixed ...$elements): RedisCluster|int|false; /** * @see Redis::rpushx */ public function rpushx(string $key, string $value): RedisCluster|bool|int; /** * @see Redis::sadd() */ public function sadd(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false; /** * @see Redis::saddarray() */ public function saddarray(string $key, array $values): RedisCluster|bool|int; /** * @see Redis::save */ public function save(string|array $key_or_address): RedisCluster|bool; /** * @see Redis::scan */ public function scan(?int &$iterator, string|array $key_or_address, ?string $pattern = null, int $count = 0): bool|array; /** * @see Redis::scard */ public function scard(string $key): RedisCluster|int|false; /** * @see Redis::script */ public function script(string|array $key_or_address, mixed ...$args): mixed; /** * @see Redis::sdiff() */ public function sdiff(string $key, string ...$other_keys): RedisCluster|array|false; /** * @see Redis::sdiffstore() */ public function sdiffstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false; /** * @see https://redis.io/commands/set */ public function set(string $key, mixed $value, mixed $options = null): RedisCluster|string|bool; /** * @see Redis::setbit */ public function setbit(string $key, int $offset, bool $onoff): RedisCluster|int|false; /** * @see Redis::setex */ public function setex(string $key, int $expire, mixed $value): RedisCluster|bool; /** * @see Redis::setnx */ public function setnx(string $key, mixed $value): RedisCluster|bool; /** * @see Redis::setoption */ public function setoption(int $option, mixed $value): bool; /** * @see Redis::setrange */ public function setrange(string $key, int $offset, string $value): RedisCluster|int|false; /** * @see Redis::sinter() */ public function sinter(array|string $key, string ...$other_keys): RedisCluster|array|false; /** * @see Redis::sintercard */ public function sintercard(array $keys, int $limit = -1): RedisCluster|int|false; /** * @see Redis::sinterstore() */ public function sinterstore(array|string $key, string ...$other_keys): RedisCluster|int|false; /** * @see Redis::sismember */ public function sismember(string $key, mixed $value): RedisCluster|bool; /** * @see Redis::smismember */ public function smismember(string $key, string $member, string ...$other_members): RedisCluster|array|false; /** * @see Redis::slowlog */ public function slowlog(string|array $key_or_address, mixed ...$args): mixed; /** * @see Redis::smembers() */ public function smembers(string $key): RedisCluster|array|false; /** * @see Redis::smove() */ public function smove(string $src, string $dst, string $member): RedisCluster|bool; /** * @see Redis::sort() */ public function sort(string $key, ?array $options = null): RedisCluster|array|bool|int|string; /** * @see Redis::sort_ro() */ public function sort_ro(string $key, ?array $options = null): RedisCluster|array|bool|int|string; /** * @see Redis::spop */ public function spop(string $key, int $count = 0): RedisCluster|string|array|false; /** * @see Redis::srandmember */ public function srandmember(string $key, int $count = 0): RedisCluster|string|array|false; /** * @see Redis::srem */ public function srem(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false; /** * @see Redis::sscan */ public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false; /** * @see Redis::strlen */ public function strlen(string $key): RedisCluster|int|false; /** * @see Redis::subscribe */ public function subscribe(array $channels, callable $cb): void; /** * @see Redis::sunion() */ public function sunion(string $key, string ...$other_keys): RedisCluster|bool|array; /** * @see Redis::sunionstore() */ public function sunionstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false; /** * @see Redis::time */ public function time(string|array $key_or_address): RedisCluster|bool|array; /** * @see Redis::ttl */ public function ttl(string $key): RedisCluster|int|false; /** * @see Redis::type */ public function type(string $key): RedisCluster|int|false; /** * @see Redis::unsubscribe */ public function unsubscribe(array $channels): bool|array; /** * @see Redis::unlink */ public function unlink(array|string $key, string ...$other_keys): RedisCluster|int|false; /** * @see Redis::unwatch */ public function unwatch(): bool; /** * @see Redis::watch */ public function watch(string $key, string ...$other_keys): RedisCluster|bool; /** * @see Redis::xack */ public function xack(string $key, string $group, array $ids): RedisCluster|int|false; /** * @see Redis::xadd */ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false): RedisCluster|string|false; /** * @see Redis::xclaim */ public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): RedisCluster|string|array|false; /** * @see Redis::xdel */ public function xdel(string $key, array $ids): RedisCluster|int|false; /** * @see Redis::xgroup */ public function xgroup(string $operation, ?string $key = null, ?string $group = null, ?string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2): mixed; /** * @see Redis::xautoclaim */ public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): RedisCluster|bool|array; /** * @see Redis::xinfo */ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed; /** * @see Redis::xlen */ public function xlen(string $key): RedisCluster|int|false; /** * @see Redis::xpending */ public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): RedisCluster|array|false; /** * @see Redis::xrange */ public function xrange(string $key, string $start, string $end, int $count = -1): RedisCluster|bool|array; /** * @see Redis::xread */ public function xread(array $streams, int $count = -1, int $block = -1): RedisCluster|bool|array; /** * @see Redis::xreadgroup */ public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): RedisCluster|bool|array; /** * @see Redis::xrevrange */ public function xrevrange(string $key, string $start, string $end, int $count = -1): RedisCluster|bool|array; /** * @see Redis::xtrim */ public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): RedisCluster|int|false; /** * @see Redis::zadd */ public function zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): RedisCluster|int|float|false; /** * @see Redis::zcard */ public function zcard(string $key): RedisCluster|int|false; /** * @see Redis::zcount */ public function zcount(string $key, string $start, string $end): RedisCluster|int|false; /** * @see Redis::zincrby */ public function zincrby(string $key, float $value, string $member): RedisCluster|float|false; /** * @see Redis::zinterstore */ public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): RedisCluster|int|false; /** * @see Redis::zintercard */ public function zintercard(array $keys, int $limit = -1): RedisCluster|int|false; /** * @see Redis::zlexcount */ public function zlexcount(string $key, string $min, string $max): RedisCluster|int|false; /** * @see Redis::zpopmax */ public function zpopmax(string $key, ?int $value = null): RedisCluster|bool|array; /** * @see Redis::zpopmin */ public function zpopmin(string $key, ?int $value = null): RedisCluster|bool|array; /** * @see Redis::zrange */ public function zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null): RedisCluster|array|bool; /** * @see Redis::zrangestore */ public function zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null): RedisCluster|int|false; /** * @see https://redis.io/commands/zRandMember */ public function zrandmember(string $key, ?array $options = null): RedisCluster|string|array; /** * @see Redis::zrangebylex */ public function zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1): RedisCluster|array|false; /** * @see Redis::zrangebyscore */ public function zrangebyscore(string $key, string $start, string $end, array $options = []): RedisCluster|array|false; /** * @see Redis::zrank */ public function zrank(string $key, mixed $member): RedisCluster|int|false; /** * @see Redis::zrem */ public function zrem(string $key, string $value, string ...$other_values): RedisCluster|int|false; /** * @see Redis::zremrangebylex */ public function zremrangebylex(string $key, string $min, string $max): RedisCluster|int|false; /** * @see Redis::zremrangebyrank */ public function zremrangebyrank(string $key, string $min, string $max): RedisCluster|int|false; /** * @see Redis::zremrangebyscore */ public function zremrangebyscore(string $key, string $min, string $max): RedisCluster|int|false; /** * @see Redis::zrevrange */ public function zrevrange(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrangebylex */ public function zrevrangebylex(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrangebyscore */ public function zrevrangebyscore(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrank */ public function zrevrank(string $key, mixed $member): RedisCluster|int|false; /** * @see Redis::zscan */ public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): RedisCluster|bool|array; /** * @see Redis::zscore */ public function zscore(string $key, mixed $member): RedisCluster|float|false; /** * @see https://redis.io/commands/zMscore */ public function zmscore(string $key, mixed $member, mixed ...$other_members): Redis|array|false; /** * @see Redis::zunionstore */ public function zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): RedisCluster|int|false; /** * @see https://redis.io/commands/zinter */ public function zinter(array $keys, ?array $weights = null, ?array $options = null): RedisCluster|array|false; /** * @see https://redis.io/commands/zdiffstore */ public function zdiffstore(string $dst, array $keys): RedisCluster|int|false; /** * @see https://redis.io/commands/zunion */ public function zunion(array $keys, ?array $weights = null, ?array $options = null): RedisCluster|array|false; /** * @see https://redis.io/commands/zdiff */ public function zdiff(array $keys, ?array $options = null): RedisCluster|array|false; } class RedisClusterException extends RuntimeException {} redis-6.0.2/redis_cluster_arginfo.h0000644000175000000120000023351114515245367020152 0ustar pyatsukhnenkowheel/* This is a generated file, edit the .stub.php file instead. * Stub hash: 32b24ce215ff4f2299dd838fab7cbc36b81b65eb */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seeds, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_MASK(0, timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0") ZEND_ARG_TYPE_MASK(0, read_timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__compress, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster__serialize, 0, 1, MAY_BE_BOOL|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__unserialize, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__pack, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster__unpack arginfo_class_RedisCluster__unserialize ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster__prefix, 0, 1, MAY_BE_BOOL|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__redir, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_acl, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_append, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bgrewriteaof, 0, 1, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_bgsave arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitcount, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitop, 0, 3, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, deskey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, otherkeys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitpos, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, bit, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_blpop, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_LONG, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_brpop arginfo_class_RedisCluster_blpop ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_brpoplpush, 0, 3, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, deskey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lmove, 0, 4, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_blmove, 0, 5, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_bzpopmax, 0, 2, IS_ARRAY, 0) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_bzpopmax ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bzmpop, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmpop, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_blmpop arginfo_class_RedisCluster_bzmpop #define arginfo_class_RedisCluster_lmpop arginfo_class_RedisCluster_zmpop ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_clearlasterror, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_client, 0, 2, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 1, "NULL") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_close arginfo_class_RedisCluster_clearlasterror ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_cluster, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_command, 0, 0, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_config, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_dbsize, 0, 1, RedisCluster, MAY_BE_LONG) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_copy, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decr, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decrby, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_decrbyfloat, 0, 2, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_del, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_discard arginfo_class_RedisCluster_clearlasterror ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_dump, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_echo, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, msg, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_eval, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, script, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_eval_ro arginfo_class_RedisCluster_eval ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_evalsha, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, script_sha, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_evalsha_ro arginfo_class_RedisCluster_evalsha ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_exec, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_exists, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_touch arginfo_class_RedisCluster_exists ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expire, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expireat, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiretime, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_pexpiretime arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_flushall, 0, 1, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, async, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geoadd, 0, 4, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples_and_options, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geodist, 0, 3, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dest, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geohash, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_geopos arginfo_class_RedisCluster_geohash ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_georadius, 0, 5, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_georadius_ro arginfo_class_RedisCluster_georadius ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_georadiusbymember, 0, 4, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_georadiusbymember_ro arginfo_class_RedisCluster_georadiusbymember ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geosearch, 0, 4, RedisCluster, MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geosearchstore, 0, 5, RedisCluster, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_get, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_decrby #define arginfo_class_RedisCluster_getlasterror arginfo_class_RedisCluster__redir ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getmode, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getoption, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getrange, 0, 3, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lcs, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getset, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_gettransferredbytes arginfo_class_RedisCluster_exec ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_cleartransferredbytes, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hdel, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hexists, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hget, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hgetall, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hincrby, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hincrbyfloat, 0, 3, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster_hgetall #define arginfo_class_RedisCluster_hlen arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hmget, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hmset, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hrandfield, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hset, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hsetnx, 0, 3, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hstrlen, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster_hgetall #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr #define arginfo_class_RedisCluster_incrby arginfo_class_RedisCluster_decrby ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_incrbyfloat, 0, 2, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_info, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, sections, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_keys, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lastsave, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lget, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_lindex, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_linsert, 0, 4, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, pos, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, pivot, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_llen, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpop, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpushx, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lrange, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lrem, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lset, 0, 3, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_ltrim, 0, 3, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_mget, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_mset, 0, 1, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_msetnx, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_multi, 0, 0, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_object, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_persist, 0, 1, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_pexpire arginfo_class_RedisCluster_expire #define arginfo_class_RedisCluster_pexpireat arginfo_class_RedisCluster_expireat ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_pfadd, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, elements, IS_ARRAY, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_pfcount arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_pfmerge, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_ping, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "NULL") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_psetex, 0, 3, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_psubscribe, 0, 2, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_pttl arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_publish, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_pubsub, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, values, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_punsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_patterns, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_randomkey, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_STRING) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_rawcommand, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_rename, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key_src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key_dst, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_renamenx, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, newkey, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_restore, 0, 3, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_role, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster_lpop ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_rpoplpush, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_rpush, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, elements, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_rpushx, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sadd, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_saddarray, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_save arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_scard arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_script, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sdiff, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sdiffstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_set, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setbit, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, onoff, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setex, 0, 3, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, expire, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setnx, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setoption, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setrange, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sinter, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sintercard, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sinterstore arginfo_class_RedisCluster_del #define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster_setnx #define arginfo_class_RedisCluster_smismember arginfo_class_RedisCluster_geohash #define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script #define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster_hgetall ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_smove, 0, 3, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sort, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_sort ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_spop, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_srandmember arginfo_class_RedisCluster_spop #define arginfo_class_RedisCluster_srem arginfo_class_RedisCluster_sadd ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sscan, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_strlen arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_subscribe, 0, 2, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sunion, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sunionstore arginfo_class_RedisCluster_sdiffstore ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_time, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_ttl arginfo_class_RedisCluster_expiretime #define arginfo_class_RedisCluster_type arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_unsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_unlink arginfo_class_RedisCluster_del #define arginfo_class_RedisCluster_unwatch arginfo_class_RedisCluster_clearlasterror ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_watch, 0, 1, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xack, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xadd, 0, 3, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, maxlen, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xclaim, 0, 6, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min_iddle, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xdel, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xautoclaim, 0, 5, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min_idle, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, justid, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xpending, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xrange, 0, 3, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xread, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xreadgroup, 0, 3, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "1") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_xrevrange arginfo_class_RedisCluster_xrange ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xtrim, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, maxlen, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, minid, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zadd, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, score_or_options, MAY_BE_ARRAY|MAY_BE_DOUBLE, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, more_scores_and_mems, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zcard arginfo_class_RedisCluster_expiretime ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zcount, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zincrby, 0, 3, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zinterstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zintercard arginfo_class_RedisCluster_sintercard ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zlexcount, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zpopmax, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zpopmin arginfo_class_RedisCluster_zpopmax ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrange, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, end, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangestore, 0, 4, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dstkey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zrandmember arginfo_class_RedisCluster_hrandfield ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangebylex, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangebyscore, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrank, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrem, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zremrangebylex arginfo_class_RedisCluster_zlexcount #define arginfo_class_RedisCluster_zremrangebyrank arginfo_class_RedisCluster_zlexcount #define arginfo_class_RedisCluster_zremrangebyscore arginfo_class_RedisCluster_zlexcount ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrevrange, 0, 3, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zrevrangebylex arginfo_class_RedisCluster_zrevrange #define arginfo_class_RedisCluster_zrevrangebyscore arginfo_class_RedisCluster_zrevrange #define arginfo_class_RedisCluster_zrevrank arginfo_class_RedisCluster_zrank ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zscan, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zscore, 0, 2, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmscore, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zunionstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "NULL") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zinter, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zdiffstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zunion arginfo_class_RedisCluster_zinter ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zdiff, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_METHOD(RedisCluster, __construct); ZEND_METHOD(RedisCluster, _compress); ZEND_METHOD(RedisCluster, _uncompress); ZEND_METHOD(RedisCluster, _serialize); ZEND_METHOD(RedisCluster, _unserialize); ZEND_METHOD(RedisCluster, _pack); ZEND_METHOD(RedisCluster, _unpack); ZEND_METHOD(RedisCluster, _prefix); ZEND_METHOD(RedisCluster, _masters); ZEND_METHOD(RedisCluster, _redir); ZEND_METHOD(RedisCluster, acl); ZEND_METHOD(RedisCluster, append); ZEND_METHOD(RedisCluster, bgrewriteaof); ZEND_METHOD(RedisCluster, bgsave); ZEND_METHOD(RedisCluster, bitcount); ZEND_METHOD(RedisCluster, bitop); ZEND_METHOD(RedisCluster, bitpos); ZEND_METHOD(RedisCluster, blpop); ZEND_METHOD(RedisCluster, brpop); ZEND_METHOD(RedisCluster, brpoplpush); ZEND_METHOD(RedisCluster, lmove); ZEND_METHOD(RedisCluster, blmove); ZEND_METHOD(RedisCluster, bzpopmax); ZEND_METHOD(RedisCluster, bzpopmin); ZEND_METHOD(RedisCluster, bzmpop); ZEND_METHOD(RedisCluster, zmpop); ZEND_METHOD(RedisCluster, blmpop); ZEND_METHOD(RedisCluster, lmpop); ZEND_METHOD(RedisCluster, clearlasterror); ZEND_METHOD(RedisCluster, client); ZEND_METHOD(RedisCluster, close); ZEND_METHOD(RedisCluster, cluster); ZEND_METHOD(RedisCluster, command); ZEND_METHOD(RedisCluster, config); ZEND_METHOD(RedisCluster, dbsize); ZEND_METHOD(RedisCluster, copy); ZEND_METHOD(RedisCluster, decr); ZEND_METHOD(RedisCluster, decrby); ZEND_METHOD(RedisCluster, decrbyfloat); ZEND_METHOD(RedisCluster, del); ZEND_METHOD(RedisCluster, discard); ZEND_METHOD(RedisCluster, dump); ZEND_METHOD(RedisCluster, echo); ZEND_METHOD(RedisCluster, eval); ZEND_METHOD(RedisCluster, eval_ro); ZEND_METHOD(RedisCluster, evalsha); ZEND_METHOD(RedisCluster, evalsha_ro); ZEND_METHOD(RedisCluster, exec); ZEND_METHOD(RedisCluster, exists); ZEND_METHOD(RedisCluster, touch); ZEND_METHOD(RedisCluster, expire); ZEND_METHOD(RedisCluster, expireat); ZEND_METHOD(RedisCluster, expiretime); ZEND_METHOD(RedisCluster, pexpiretime); ZEND_METHOD(RedisCluster, flushall); ZEND_METHOD(RedisCluster, flushdb); ZEND_METHOD(RedisCluster, geoadd); ZEND_METHOD(RedisCluster, geodist); ZEND_METHOD(RedisCluster, geohash); ZEND_METHOD(RedisCluster, geopos); ZEND_METHOD(RedisCluster, georadius); ZEND_METHOD(RedisCluster, georadius_ro); ZEND_METHOD(RedisCluster, georadiusbymember); ZEND_METHOD(RedisCluster, georadiusbymember_ro); ZEND_METHOD(RedisCluster, geosearch); ZEND_METHOD(RedisCluster, geosearchstore); ZEND_METHOD(RedisCluster, get); ZEND_METHOD(RedisCluster, getbit); ZEND_METHOD(RedisCluster, getlasterror); ZEND_METHOD(RedisCluster, getmode); ZEND_METHOD(RedisCluster, getoption); ZEND_METHOD(RedisCluster, getrange); ZEND_METHOD(RedisCluster, lcs); ZEND_METHOD(RedisCluster, getset); ZEND_METHOD(RedisCluster, gettransferredbytes); ZEND_METHOD(RedisCluster, cleartransferredbytes); ZEND_METHOD(RedisCluster, hdel); ZEND_METHOD(RedisCluster, hexists); ZEND_METHOD(RedisCluster, hget); ZEND_METHOD(RedisCluster, hgetall); ZEND_METHOD(RedisCluster, hincrby); ZEND_METHOD(RedisCluster, hincrbyfloat); ZEND_METHOD(RedisCluster, hkeys); ZEND_METHOD(RedisCluster, hlen); ZEND_METHOD(RedisCluster, hmget); ZEND_METHOD(RedisCluster, hmset); ZEND_METHOD(RedisCluster, hscan); ZEND_METHOD(RedisCluster, hrandfield); ZEND_METHOD(RedisCluster, hset); ZEND_METHOD(RedisCluster, hsetnx); ZEND_METHOD(RedisCluster, hstrlen); ZEND_METHOD(RedisCluster, hvals); ZEND_METHOD(RedisCluster, incr); ZEND_METHOD(RedisCluster, incrby); ZEND_METHOD(RedisCluster, incrbyfloat); ZEND_METHOD(RedisCluster, info); ZEND_METHOD(RedisCluster, keys); ZEND_METHOD(RedisCluster, lastsave); ZEND_METHOD(RedisCluster, lget); ZEND_METHOD(RedisCluster, lindex); ZEND_METHOD(RedisCluster, linsert); ZEND_METHOD(RedisCluster, llen); ZEND_METHOD(RedisCluster, lpop); ZEND_METHOD(RedisCluster, lpos); ZEND_METHOD(RedisCluster, lpush); ZEND_METHOD(RedisCluster, lpushx); ZEND_METHOD(RedisCluster, lrange); ZEND_METHOD(RedisCluster, lrem); ZEND_METHOD(RedisCluster, lset); ZEND_METHOD(RedisCluster, ltrim); ZEND_METHOD(RedisCluster, mget); ZEND_METHOD(RedisCluster, mset); ZEND_METHOD(RedisCluster, msetnx); ZEND_METHOD(RedisCluster, multi); ZEND_METHOD(RedisCluster, object); ZEND_METHOD(RedisCluster, persist); ZEND_METHOD(RedisCluster, pexpire); ZEND_METHOD(RedisCluster, pexpireat); ZEND_METHOD(RedisCluster, pfadd); ZEND_METHOD(RedisCluster, pfcount); ZEND_METHOD(RedisCluster, pfmerge); ZEND_METHOD(RedisCluster, ping); ZEND_METHOD(RedisCluster, psetex); ZEND_METHOD(RedisCluster, psubscribe); ZEND_METHOD(RedisCluster, pttl); ZEND_METHOD(RedisCluster, publish); ZEND_METHOD(RedisCluster, pubsub); ZEND_METHOD(RedisCluster, punsubscribe); ZEND_METHOD(RedisCluster, randomkey); ZEND_METHOD(RedisCluster, rawcommand); ZEND_METHOD(RedisCluster, rename); ZEND_METHOD(RedisCluster, renamenx); ZEND_METHOD(RedisCluster, restore); ZEND_METHOD(RedisCluster, role); ZEND_METHOD(RedisCluster, rpop); ZEND_METHOD(RedisCluster, rpoplpush); ZEND_METHOD(RedisCluster, rpush); ZEND_METHOD(RedisCluster, rpushx); ZEND_METHOD(RedisCluster, sadd); ZEND_METHOD(RedisCluster, saddarray); ZEND_METHOD(RedisCluster, save); ZEND_METHOD(RedisCluster, scan); ZEND_METHOD(RedisCluster, scard); ZEND_METHOD(RedisCluster, script); ZEND_METHOD(RedisCluster, sdiff); ZEND_METHOD(RedisCluster, sdiffstore); ZEND_METHOD(RedisCluster, set); ZEND_METHOD(RedisCluster, setbit); ZEND_METHOD(RedisCluster, setex); ZEND_METHOD(RedisCluster, setnx); ZEND_METHOD(RedisCluster, setoption); ZEND_METHOD(RedisCluster, setrange); ZEND_METHOD(RedisCluster, sinter); ZEND_METHOD(RedisCluster, sintercard); ZEND_METHOD(RedisCluster, sinterstore); ZEND_METHOD(RedisCluster, sismember); ZEND_METHOD(RedisCluster, smismember); ZEND_METHOD(RedisCluster, slowlog); ZEND_METHOD(RedisCluster, smembers); ZEND_METHOD(RedisCluster, smove); ZEND_METHOD(RedisCluster, sort); ZEND_METHOD(RedisCluster, sort_ro); ZEND_METHOD(RedisCluster, spop); ZEND_METHOD(RedisCluster, srandmember); ZEND_METHOD(RedisCluster, srem); ZEND_METHOD(RedisCluster, sscan); ZEND_METHOD(RedisCluster, strlen); ZEND_METHOD(RedisCluster, subscribe); ZEND_METHOD(RedisCluster, sunion); ZEND_METHOD(RedisCluster, sunionstore); ZEND_METHOD(RedisCluster, time); ZEND_METHOD(RedisCluster, ttl); ZEND_METHOD(RedisCluster, type); ZEND_METHOD(RedisCluster, unsubscribe); ZEND_METHOD(RedisCluster, unlink); ZEND_METHOD(RedisCluster, unwatch); ZEND_METHOD(RedisCluster, watch); ZEND_METHOD(RedisCluster, xack); ZEND_METHOD(RedisCluster, xadd); ZEND_METHOD(RedisCluster, xclaim); ZEND_METHOD(RedisCluster, xdel); ZEND_METHOD(RedisCluster, xgroup); ZEND_METHOD(RedisCluster, xautoclaim); ZEND_METHOD(RedisCluster, xinfo); ZEND_METHOD(RedisCluster, xlen); ZEND_METHOD(RedisCluster, xpending); ZEND_METHOD(RedisCluster, xrange); ZEND_METHOD(RedisCluster, xread); ZEND_METHOD(RedisCluster, xreadgroup); ZEND_METHOD(RedisCluster, xrevrange); ZEND_METHOD(RedisCluster, xtrim); ZEND_METHOD(RedisCluster, zadd); ZEND_METHOD(RedisCluster, zcard); ZEND_METHOD(RedisCluster, zcount); ZEND_METHOD(RedisCluster, zincrby); ZEND_METHOD(RedisCluster, zinterstore); ZEND_METHOD(RedisCluster, zintercard); ZEND_METHOD(RedisCluster, zlexcount); ZEND_METHOD(RedisCluster, zpopmax); ZEND_METHOD(RedisCluster, zpopmin); ZEND_METHOD(RedisCluster, zrange); ZEND_METHOD(RedisCluster, zrangestore); ZEND_METHOD(RedisCluster, zrandmember); ZEND_METHOD(RedisCluster, zrangebylex); ZEND_METHOD(RedisCluster, zrangebyscore); ZEND_METHOD(RedisCluster, zrank); ZEND_METHOD(RedisCluster, zrem); ZEND_METHOD(RedisCluster, zremrangebylex); ZEND_METHOD(RedisCluster, zremrangebyrank); ZEND_METHOD(RedisCluster, zremrangebyscore); ZEND_METHOD(RedisCluster, zrevrange); ZEND_METHOD(RedisCluster, zrevrangebylex); ZEND_METHOD(RedisCluster, zrevrangebyscore); ZEND_METHOD(RedisCluster, zrevrank); ZEND_METHOD(RedisCluster, zscan); ZEND_METHOD(RedisCluster, zscore); ZEND_METHOD(RedisCluster, zmscore); ZEND_METHOD(RedisCluster, zunionstore); ZEND_METHOD(RedisCluster, zinter); ZEND_METHOD(RedisCluster, zdiffstore); ZEND_METHOD(RedisCluster, zunion); ZEND_METHOD(RedisCluster, zdiff); static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _compress, arginfo_class_RedisCluster__compress, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _pack, arginfo_class_RedisCluster__pack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgrewriteaof, arginfo_class_RedisCluster_bgrewriteaof, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgsave, arginfo_class_RedisCluster_bgsave, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitcount, arginfo_class_RedisCluster_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitop, arginfo_class_RedisCluster_bitop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitpos, arginfo_class_RedisCluster_bitpos, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, blpop, arginfo_class_RedisCluster_blpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, brpop, arginfo_class_RedisCluster_brpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, brpoplpush, arginfo_class_RedisCluster_brpoplpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lmove, arginfo_class_RedisCluster_lmove, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, blmove, arginfo_class_RedisCluster_blmove, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzpopmax, arginfo_class_RedisCluster_bzpopmax, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzpopmin, arginfo_class_RedisCluster_bzpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzmpop, arginfo_class_RedisCluster_bzmpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zmpop, arginfo_class_RedisCluster_zmpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, blmpop, arginfo_class_RedisCluster_blmpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lmpop, arginfo_class_RedisCluster_lmpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, clearlasterror, arginfo_class_RedisCluster_clearlasterror, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, client, arginfo_class_RedisCluster_client, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, close, arginfo_class_RedisCluster_close, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, cluster, arginfo_class_RedisCluster_cluster, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, command, arginfo_class_RedisCluster_command, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, config, arginfo_class_RedisCluster_config, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, copy, arginfo_class_RedisCluster_copy, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, echo, arginfo_class_RedisCluster_echo, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, eval, arginfo_class_RedisCluster_eval, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, eval_ro, arginfo_class_RedisCluster_eval_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, evalsha, arginfo_class_RedisCluster_evalsha, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, evalsha_ro, arginfo_class_RedisCluster_evalsha_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, exec, arginfo_class_RedisCluster_exec, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, touch, arginfo_class_RedisCluster_touch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, expireat, arginfo_class_RedisCluster_expireat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, expiretime, arginfo_class_RedisCluster_expiretime, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pexpiretime, arginfo_class_RedisCluster_pexpiretime, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, flushall, arginfo_class_RedisCluster_flushall, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, flushdb, arginfo_class_RedisCluster_flushdb, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geoadd, arginfo_class_RedisCluster_geoadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geodist, arginfo_class_RedisCluster_geodist, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geohash, arginfo_class_RedisCluster_geohash, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geopos, arginfo_class_RedisCluster_geopos, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadius, arginfo_class_RedisCluster_georadius, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadius_ro, arginfo_class_RedisCluster_georadius_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadiusbymember, arginfo_class_RedisCluster_georadiusbymember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadiusbymember_ro, arginfo_class_RedisCluster_georadiusbymember_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, gettransferredbytes, arginfo_class_RedisCluster_gettransferredbytes, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, cleartransferredbytes, arginfo_class_RedisCluster_cleartransferredbytes, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hgetall, arginfo_class_RedisCluster_hgetall, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hincrby, arginfo_class_RedisCluster_hincrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hincrbyfloat, arginfo_class_RedisCluster_hincrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hkeys, arginfo_class_RedisCluster_hkeys, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hlen, arginfo_class_RedisCluster_hlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hrandfield, arginfo_class_RedisCluster_hrandfield, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incrbyfloat, arginfo_class_RedisCluster_incrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, info, arginfo_class_RedisCluster_info, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, keys, arginfo_class_RedisCluster_keys, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lastsave, arginfo_class_RedisCluster_lastsave, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lget, arginfo_class_RedisCluster_lget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lindex, arginfo_class_RedisCluster_lindex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, linsert, arginfo_class_RedisCluster_linsert, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, llen, arginfo_class_RedisCluster_llen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpop, arginfo_class_RedisCluster_lpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpos, arginfo_class_RedisCluster_lpos, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpush, arginfo_class_RedisCluster_lpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpushx, arginfo_class_RedisCluster_lpushx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lrange, arginfo_class_RedisCluster_lrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lrem, arginfo_class_RedisCluster_lrem, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lset, arginfo_class_RedisCluster_lset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, ltrim, arginfo_class_RedisCluster_ltrim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, mget, arginfo_class_RedisCluster_mget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, mset, arginfo_class_RedisCluster_mset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, msetnx, arginfo_class_RedisCluster_msetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, multi, arginfo_class_RedisCluster_multi, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, object, arginfo_class_RedisCluster_object, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, persist, arginfo_class_RedisCluster_persist, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pexpire, arginfo_class_RedisCluster_pexpire, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pexpireat, arginfo_class_RedisCluster_pexpireat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pfadd, arginfo_class_RedisCluster_pfadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pfcount, arginfo_class_RedisCluster_pfcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pfmerge, arginfo_class_RedisCluster_pfmerge, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, ping, arginfo_class_RedisCluster_ping, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, psetex, arginfo_class_RedisCluster_psetex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, psubscribe, arginfo_class_RedisCluster_psubscribe, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pttl, arginfo_class_RedisCluster_pttl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, publish, arginfo_class_RedisCluster_publish, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pubsub, arginfo_class_RedisCluster_pubsub, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, punsubscribe, arginfo_class_RedisCluster_punsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, randomkey, arginfo_class_RedisCluster_randomkey, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rawcommand, arginfo_class_RedisCluster_rawcommand, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rename, arginfo_class_RedisCluster_rename, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, renamenx, arginfo_class_RedisCluster_renamenx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, restore, arginfo_class_RedisCluster_restore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, role, arginfo_class_RedisCluster_role, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rpop, arginfo_class_RedisCluster_rpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rpoplpush, arginfo_class_RedisCluster_rpoplpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rpush, arginfo_class_RedisCluster_rpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rpushx, arginfo_class_RedisCluster_rpushx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sadd, arginfo_class_RedisCluster_sadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, saddarray, arginfo_class_RedisCluster_saddarray, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, save, arginfo_class_RedisCluster_save, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, scan, arginfo_class_RedisCluster_scan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, scard, arginfo_class_RedisCluster_scard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, script, arginfo_class_RedisCluster_script, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sdiff, arginfo_class_RedisCluster_sdiff, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sdiffstore, arginfo_class_RedisCluster_sdiffstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, set, arginfo_class_RedisCluster_set, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setbit, arginfo_class_RedisCluster_setbit, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setex, arginfo_class_RedisCluster_setex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setnx, arginfo_class_RedisCluster_setnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setoption, arginfo_class_RedisCluster_setoption, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setrange, arginfo_class_RedisCluster_setrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sinter, arginfo_class_RedisCluster_sinter, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sintercard, arginfo_class_RedisCluster_sintercard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sinterstore, arginfo_class_RedisCluster_sinterstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sismember, arginfo_class_RedisCluster_sismember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smismember, arginfo_class_RedisCluster_smismember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, slowlog, arginfo_class_RedisCluster_slowlog, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smembers, arginfo_class_RedisCluster_smembers, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smove, arginfo_class_RedisCluster_smove, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sort, arginfo_class_RedisCluster_sort, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sort_ro, arginfo_class_RedisCluster_sort_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, spop, arginfo_class_RedisCluster_spop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, srandmember, arginfo_class_RedisCluster_srandmember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, srem, arginfo_class_RedisCluster_srem, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sscan, arginfo_class_RedisCluster_sscan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, strlen, arginfo_class_RedisCluster_strlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, subscribe, arginfo_class_RedisCluster_subscribe, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sunion, arginfo_class_RedisCluster_sunion, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sunionstore, arginfo_class_RedisCluster_sunionstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, time, arginfo_class_RedisCluster_time, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, ttl, arginfo_class_RedisCluster_ttl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, type, arginfo_class_RedisCluster_type, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, unsubscribe, arginfo_class_RedisCluster_unsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, unlink, arginfo_class_RedisCluster_unlink, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, unwatch, arginfo_class_RedisCluster_unwatch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, watch, arginfo_class_RedisCluster_watch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xack, arginfo_class_RedisCluster_xack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xadd, arginfo_class_RedisCluster_xadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xautoclaim, arginfo_class_RedisCluster_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xlen, arginfo_class_RedisCluster_xlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xpending, arginfo_class_RedisCluster_xpending, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xrange, arginfo_class_RedisCluster_xrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xread, arginfo_class_RedisCluster_xread, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xreadgroup, arginfo_class_RedisCluster_xreadgroup, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xrevrange, arginfo_class_RedisCluster_xrevrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xtrim, arginfo_class_RedisCluster_xtrim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zadd, arginfo_class_RedisCluster_zadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zcard, arginfo_class_RedisCluster_zcard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zcount, arginfo_class_RedisCluster_zcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zincrby, arginfo_class_RedisCluster_zincrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zinterstore, arginfo_class_RedisCluster_zinterstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zintercard, arginfo_class_RedisCluster_zintercard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zlexcount, arginfo_class_RedisCluster_zlexcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangestore, arginfo_class_RedisCluster_zrangestore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrandmember, arginfo_class_RedisCluster_zrandmember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrem, arginfo_class_RedisCluster_zrem, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zremrangebylex, arginfo_class_RedisCluster_zremrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zremrangebyrank, arginfo_class_RedisCluster_zremrangebyrank, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zremrangebyscore, arginfo_class_RedisCluster_zremrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrevrange, arginfo_class_RedisCluster_zrevrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrevrangebylex, arginfo_class_RedisCluster_zrevrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrevrangebyscore, arginfo_class_RedisCluster_zrevrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrevrank, arginfo_class_RedisCluster_zrevrank, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zscan, arginfo_class_RedisCluster_zscan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zscore, arginfo_class_RedisCluster_zscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zmscore, arginfo_class_RedisCluster_zmscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zunionstore, arginfo_class_RedisCluster_zunionstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zinter, arginfo_class_RedisCluster_zinter, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zdiffstore, arginfo_class_RedisCluster_zdiffstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zunion, arginfo_class_RedisCluster_zunion, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zdiff, arginfo_class_RedisCluster_zdiff, ZEND_ACC_PUBLIC) ZEND_FE_END }; static const zend_function_entry class_RedisClusterException_methods[] = { ZEND_FE_END }; static zend_class_entry *register_class_RedisCluster(void) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); zval const_OPT_SLAVE_FAILOVER_value; ZVAL_LONG(&const_OPT_SLAVE_FAILOVER_value, REDIS_OPT_FAILOVER); zend_string *const_OPT_SLAVE_FAILOVER_name = zend_string_init_interned("OPT_SLAVE_FAILOVER", sizeof("OPT_SLAVE_FAILOVER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_SLAVE_FAILOVER_name, &const_OPT_SLAVE_FAILOVER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_SLAVE_FAILOVER_name); zval const_FAILOVER_NONE_value; ZVAL_LONG(&const_FAILOVER_NONE_value, REDIS_FAILOVER_NONE); zend_string *const_FAILOVER_NONE_name = zend_string_init_interned("FAILOVER_NONE", sizeof("FAILOVER_NONE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_NONE_name, &const_FAILOVER_NONE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_NONE_name); zval const_FAILOVER_ERROR_value; ZVAL_LONG(&const_FAILOVER_ERROR_value, REDIS_FAILOVER_ERROR); zend_string *const_FAILOVER_ERROR_name = zend_string_init_interned("FAILOVER_ERROR", sizeof("FAILOVER_ERROR") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_ERROR_name, &const_FAILOVER_ERROR_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_ERROR_name); zval const_FAILOVER_DISTRIBUTE_value; ZVAL_LONG(&const_FAILOVER_DISTRIBUTE_value, REDIS_FAILOVER_DISTRIBUTE); zend_string *const_FAILOVER_DISTRIBUTE_name = zend_string_init_interned("FAILOVER_DISTRIBUTE", sizeof("FAILOVER_DISTRIBUTE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_name, &const_FAILOVER_DISTRIBUTE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_DISTRIBUTE_name); zval const_FAILOVER_DISTRIBUTE_SLAVES_value; ZVAL_LONG(&const_FAILOVER_DISTRIBUTE_SLAVES_value, REDIS_FAILOVER_DISTRIBUTE_SLAVES); zend_string *const_FAILOVER_DISTRIBUTE_SLAVES_name = zend_string_init_interned("FAILOVER_DISTRIBUTE_SLAVES", sizeof("FAILOVER_DISTRIBUTE_SLAVES") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_SLAVES_name, &const_FAILOVER_DISTRIBUTE_SLAVES_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_DISTRIBUTE_SLAVES_name); #if (PHP_VERSION_ID >= 80200) zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 5, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); #endif return class_entry; } static zend_class_entry *register_class_RedisClusterException(zend_class_entry *class_entry_RuntimeException) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisClusterException", class_RedisClusterException_methods); class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException); return class_entry; } redis-6.0.2/redis_cluster_legacy_arginfo.h0000644000175000000120000015752414515245367021507 0ustar pyatsukhnenkowheel/* This is a generated file, edit the .stub.php file instead. * Stub hash: 32b24ce215ff4f2299dd838fab7cbc36b81b65eb */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, seeds) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, read_timeout) ZEND_ARG_INFO(0, persistent) ZEND_ARG_INFO(0, auth) ZEND_ARG_INFO(0, context) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__compress, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress #define arginfo_class_RedisCluster__serialize arginfo_class_RedisCluster__compress #define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__compress #define arginfo_class_RedisCluster__pack arginfo_class_RedisCluster__compress #define arginfo_class_RedisCluster__unpack arginfo_class_RedisCluster__compress ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__prefix, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, 0) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster__redir arginfo_class_RedisCluster__masters ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_acl, 0, 0, 2) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, subcmd) ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_append, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bgrewriteaof, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_bgsave arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitcount, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, bybit) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitop, 0, 0, 3) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, deskey) ZEND_ARG_INFO(0, srckey) ZEND_ARG_VARIADIC_INFO(0, otherkeys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitpos, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, bit) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, bybit) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_blpop, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timeout_or_key) ZEND_ARG_VARIADIC_INFO(0, extra_args) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_brpop arginfo_class_RedisCluster_blpop ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_brpoplpush, 0, 0, 3) ZEND_ARG_INFO(0, srckey) ZEND_ARG_INFO(0, deskey) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lmove, 0, 0, 4) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, wherefrom) ZEND_ARG_INFO(0, whereto) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_blmove, 0, 0, 5) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, wherefrom) ZEND_ARG_INFO(0, whereto) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_bzpopmax arginfo_class_RedisCluster_blpop #define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_blpop ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bzmpop, 0, 0, 3) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, from) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zmpop, 0, 0, 2) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, from) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_blmpop arginfo_class_RedisCluster_bzmpop #define arginfo_class_RedisCluster_lmpop arginfo_class_RedisCluster_zmpop #define arginfo_class_RedisCluster_clearlasterror arginfo_class_RedisCluster__masters ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_client, 0, 0, 2) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, subcommand) ZEND_ARG_INFO(0, arg) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_close arginfo_class_RedisCluster__masters ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_cluster, 0, 0, 2) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, command) ZEND_ARG_VARIADIC_INFO(0, extra_args) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_command, 0, 0, 0) ZEND_ARG_VARIADIC_INFO(0, extra_args) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_config, 0, 0, 2) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, subcommand) ZEND_ARG_VARIADIC_INFO(0, extra_args) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_dbsize arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_copy, 0, 0, 2) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_decr, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, by) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_decrby arginfo_class_RedisCluster_append #define arginfo_class_RedisCluster_decrbyfloat arginfo_class_RedisCluster_append ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_del, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_VARIADIC_INFO(0, other_keys) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_discard arginfo_class_RedisCluster__masters #define arginfo_class_RedisCluster_dump arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_echo, 0, 0, 2) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, msg) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_eval, 0, 0, 1) ZEND_ARG_INFO(0, script) ZEND_ARG_INFO(0, args) ZEND_ARG_INFO(0, num_keys) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_eval_ro arginfo_class_RedisCluster_eval ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_evalsha, 0, 0, 1) ZEND_ARG_INFO(0, script_sha) ZEND_ARG_INFO(0, args) ZEND_ARG_INFO(0, num_keys) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_evalsha_ro arginfo_class_RedisCluster_evalsha #define arginfo_class_RedisCluster_exec arginfo_class_RedisCluster__masters #define arginfo_class_RedisCluster_exists arginfo_class_RedisCluster_del #define arginfo_class_RedisCluster_touch arginfo_class_RedisCluster_del ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expire, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expireat, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timestamp) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_expiretime arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_pexpiretime arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_flushall, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, async) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, lng) ZEND_ARG_INFO(0, lat) ZEND_ARG_INFO(0, member) ZEND_ARG_VARIADIC_INFO(0, other_triples_and_options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geodist, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dest) ZEND_ARG_INFO(0, unit) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geohash, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) ZEND_ARG_VARIADIC_INFO(0, other_members) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_geopos arginfo_class_RedisCluster_geohash ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_georadius, 0, 0, 5) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, lng) ZEND_ARG_INFO(0, lat) ZEND_ARG_INFO(0, radius) ZEND_ARG_INFO(0, unit) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_georadius_ro arginfo_class_RedisCluster_georadius ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_georadiusbymember, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) ZEND_ARG_INFO(0, radius) ZEND_ARG_INFO(0, unit) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_georadiusbymember_ro arginfo_class_RedisCluster_georadiusbymember ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geosearch, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, position) ZEND_ARG_INFO(0, shape) ZEND_ARG_INFO(0, unit) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geosearchstore, 0, 0, 5) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, position) ZEND_ARG_INFO(0, shape) ZEND_ARG_INFO(0, unit) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_get arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_append #define arginfo_class_RedisCluster_getlasterror arginfo_class_RedisCluster__masters #define arginfo_class_RedisCluster_getmode arginfo_class_RedisCluster__masters ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getoption, 0, 0, 1) ZEND_ARG_INFO(0, option) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lcs, 0, 0, 2) ZEND_ARG_INFO(0, key1) ZEND_ARG_INFO(0, key2) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_getset arginfo_class_RedisCluster_append #define arginfo_class_RedisCluster_gettransferredbytes arginfo_class_RedisCluster__masters #define arginfo_class_RedisCluster_cleartransferredbytes arginfo_class_RedisCluster__masters #define arginfo_class_RedisCluster_hdel arginfo_class_RedisCluster_geohash ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexists, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_hget arginfo_class_RedisCluster_hexists #define arginfo_class_RedisCluster_hgetall arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hincrby, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_hincrbyfloat arginfo_class_RedisCluster_hincrby #define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_hlen arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hmget, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hmset, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, key_values) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hscan, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(1, iterator) ZEND_ARG_INFO(0, pattern) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hrandfield, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_hset arginfo_class_RedisCluster_hincrby #define arginfo_class_RedisCluster_hsetnx arginfo_class_RedisCluster_hincrby ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hstrlen, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, field) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr #define arginfo_class_RedisCluster_incrby arginfo_class_RedisCluster_append #define arginfo_class_RedisCluster_incrbyfloat arginfo_class_RedisCluster_append ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_info, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_VARIADIC_INFO(0, sections) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_keys, 0, 0, 1) ZEND_ARG_INFO(0, pattern) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_lastsave arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lget, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, index) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_lindex arginfo_class_RedisCluster_lget ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_linsert, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, pos) ZEND_ARG_INFO(0, pivot) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_llen arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpop, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpos, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpush, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_VARIADIC_INFO(0, other_values) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_lpushx arginfo_class_RedisCluster_append #define arginfo_class_RedisCluster_lrange arginfo_class_RedisCluster_getrange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lrem, 0, 0, 2) 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_class_RedisCluster_lset, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, index) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_ltrim arginfo_class_RedisCluster_getrange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_mget, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_mset, 0, 0, 1) ZEND_ARG_INFO(0, key_values) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_msetnx arginfo_class_RedisCluster_mset ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_multi, 0, 0, 0) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_object, 0, 0, 2) ZEND_ARG_INFO(0, subcommand) ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_persist arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_pexpire arginfo_class_RedisCluster_expire #define arginfo_class_RedisCluster_pexpireat arginfo_class_RedisCluster_expireat ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_pfadd, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, elements) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_pfcount arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_pfmerge arginfo_class_RedisCluster_hmget ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_ping, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_psetex, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_psubscribe, 0, 0, 2) ZEND_ARG_INFO(0, patterns) ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_pttl arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_publish, 0, 0, 2) ZEND_ARG_INFO(0, channel) ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_pubsub, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_VARIADIC_INFO(0, values) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_punsubscribe, 0, 0, 1) ZEND_ARG_INFO(0, pattern) ZEND_ARG_VARIADIC_INFO(0, other_patterns) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_randomkey arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rawcommand, 0, 0, 2) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, command) ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rename, 0, 0, 2) ZEND_ARG_INFO(0, key_src) ZEND_ARG_INFO(0, key_dst) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_renamenx, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, newkey) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_restore, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_role arginfo_class_RedisCluster_bgrewriteaof #define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster_lpop ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rpoplpush, 0, 0, 2) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rpush, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_VARIADIC_INFO(0, elements) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_rpushx arginfo_class_RedisCluster_append #define arginfo_class_RedisCluster_sadd arginfo_class_RedisCluster_lpush ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_saddarray, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, values) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_save arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_scan, 0, 0, 2) ZEND_ARG_INFO(1, iterator) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, pattern) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_scard arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_script, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sdiff arginfo_class_RedisCluster_del ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sdiffstore, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, key) ZEND_ARG_VARIADIC_INFO(0, other_keys) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_set arginfo_class_RedisCluster_lpos ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setbit, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, onoff) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setex, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expire) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_setnx arginfo_class_RedisCluster_append ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setoption, 0, 0, 2) ZEND_ARG_INFO(0, option) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sinter arginfo_class_RedisCluster_del ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sintercard, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, limit) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sinterstore arginfo_class_RedisCluster_del #define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster_append #define arginfo_class_RedisCluster_smismember arginfo_class_RedisCluster_geohash #define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script #define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_smove, 0, 0, 3) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sort arginfo_class_RedisCluster_hrandfield #define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_hrandfield #define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster_lpop #define arginfo_class_RedisCluster_srandmember arginfo_class_RedisCluster_lpop #define arginfo_class_RedisCluster_srem arginfo_class_RedisCluster_lpush #define arginfo_class_RedisCluster_sscan arginfo_class_RedisCluster_hscan #define arginfo_class_RedisCluster_strlen arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_subscribe, 0, 0, 2) ZEND_ARG_INFO(0, channels) ZEND_ARG_INFO(0, cb) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sunion arginfo_class_RedisCluster_del #define arginfo_class_RedisCluster_sunionstore arginfo_class_RedisCluster_sdiffstore #define arginfo_class_RedisCluster_time arginfo_class_RedisCluster_bgrewriteaof #define arginfo_class_RedisCluster_ttl arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_type arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_unsubscribe, 0, 0, 1) ZEND_ARG_INFO(0, channels) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_unlink arginfo_class_RedisCluster_del #define arginfo_class_RedisCluster_unwatch arginfo_class_RedisCluster__masters #define arginfo_class_RedisCluster_watch arginfo_class_RedisCluster_del ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xack, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, ids) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xadd, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, id) ZEND_ARG_INFO(0, values) ZEND_ARG_INFO(0, maxlen) ZEND_ARG_INFO(0, approx) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xclaim, 0, 0, 6) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, consumer) ZEND_ARG_INFO(0, min_iddle) ZEND_ARG_INFO(0, ids) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xdel, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, ids) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, id_or_consumer) ZEND_ARG_INFO(0, mkstream) ZEND_ARG_INFO(0, entries_read) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xautoclaim, 0, 0, 5) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, consumer) ZEND_ARG_INFO(0, min_idle) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, justid) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, arg1) ZEND_ARG_INFO(0, arg2) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster__prefix ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xpending, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, consumer) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xread, 0, 0, 1) ZEND_ARG_INFO(0, streams) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, block) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xreadgroup, 0, 0, 3) ZEND_ARG_INFO(0, group) ZEND_ARG_INFO(0, consumer) ZEND_ARG_INFO(0, streams) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, block) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_xrevrange arginfo_class_RedisCluster_xrange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xtrim, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, maxlen) ZEND_ARG_INFO(0, approx) ZEND_ARG_INFO(0, minid) ZEND_ARG_INFO(0, limit) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zadd, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, score_or_options) ZEND_ARG_VARIADIC_INFO(0, more_scores_and_mems) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zcard arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_zcount arginfo_class_RedisCluster_getrange ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zincrby, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zinterstore, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, weights) ZEND_ARG_INFO(0, aggregate) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zintercard arginfo_class_RedisCluster_sintercard ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zlexcount, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, min) ZEND_ARG_INFO(0, max) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zpopmax, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zpopmin arginfo_class_RedisCluster_zpopmax ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangestore, 0, 0, 4) ZEND_ARG_INFO(0, dstkey) ZEND_ARG_INFO(0, srckey) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zrandmember arginfo_class_RedisCluster_hrandfield ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, min) ZEND_ARG_INFO(0, max) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zrangebyscore arginfo_class_RedisCluster_zrange #define arginfo_class_RedisCluster_zrank arginfo_class_RedisCluster_hexists #define arginfo_class_RedisCluster_zrem arginfo_class_RedisCluster_lpush #define arginfo_class_RedisCluster_zremrangebylex arginfo_class_RedisCluster_zlexcount #define arginfo_class_RedisCluster_zremrangebyrank arginfo_class_RedisCluster_zlexcount #define arginfo_class_RedisCluster_zremrangebyscore arginfo_class_RedisCluster_zlexcount ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrevrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, min) ZEND_ARG_INFO(0, max) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zrevrangebylex arginfo_class_RedisCluster_zrevrange #define arginfo_class_RedisCluster_zrevrangebyscore arginfo_class_RedisCluster_zrevrange #define arginfo_class_RedisCluster_zrevrank arginfo_class_RedisCluster_hexists #define arginfo_class_RedisCluster_zscan arginfo_class_RedisCluster_hscan #define arginfo_class_RedisCluster_zscore arginfo_class_RedisCluster_hexists #define arginfo_class_RedisCluster_zmscore arginfo_class_RedisCluster_geohash #define arginfo_class_RedisCluster_zunionstore arginfo_class_RedisCluster_zinterstore ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zinter, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, weights) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zdiffstore, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, keys) ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zunion arginfo_class_RedisCluster_zinter ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zdiff, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_METHOD(RedisCluster, __construct); ZEND_METHOD(RedisCluster, _compress); ZEND_METHOD(RedisCluster, _uncompress); ZEND_METHOD(RedisCluster, _serialize); ZEND_METHOD(RedisCluster, _unserialize); ZEND_METHOD(RedisCluster, _pack); ZEND_METHOD(RedisCluster, _unpack); ZEND_METHOD(RedisCluster, _prefix); ZEND_METHOD(RedisCluster, _masters); ZEND_METHOD(RedisCluster, _redir); ZEND_METHOD(RedisCluster, acl); ZEND_METHOD(RedisCluster, append); ZEND_METHOD(RedisCluster, bgrewriteaof); ZEND_METHOD(RedisCluster, bgsave); ZEND_METHOD(RedisCluster, bitcount); ZEND_METHOD(RedisCluster, bitop); ZEND_METHOD(RedisCluster, bitpos); ZEND_METHOD(RedisCluster, blpop); ZEND_METHOD(RedisCluster, brpop); ZEND_METHOD(RedisCluster, brpoplpush); ZEND_METHOD(RedisCluster, lmove); ZEND_METHOD(RedisCluster, blmove); ZEND_METHOD(RedisCluster, bzpopmax); ZEND_METHOD(RedisCluster, bzpopmin); ZEND_METHOD(RedisCluster, bzmpop); ZEND_METHOD(RedisCluster, zmpop); ZEND_METHOD(RedisCluster, blmpop); ZEND_METHOD(RedisCluster, lmpop); ZEND_METHOD(RedisCluster, clearlasterror); ZEND_METHOD(RedisCluster, client); ZEND_METHOD(RedisCluster, close); ZEND_METHOD(RedisCluster, cluster); ZEND_METHOD(RedisCluster, command); ZEND_METHOD(RedisCluster, config); ZEND_METHOD(RedisCluster, dbsize); ZEND_METHOD(RedisCluster, copy); ZEND_METHOD(RedisCluster, decr); ZEND_METHOD(RedisCluster, decrby); ZEND_METHOD(RedisCluster, decrbyfloat); ZEND_METHOD(RedisCluster, del); ZEND_METHOD(RedisCluster, discard); ZEND_METHOD(RedisCluster, dump); ZEND_METHOD(RedisCluster, echo); ZEND_METHOD(RedisCluster, eval); ZEND_METHOD(RedisCluster, eval_ro); ZEND_METHOD(RedisCluster, evalsha); ZEND_METHOD(RedisCluster, evalsha_ro); ZEND_METHOD(RedisCluster, exec); ZEND_METHOD(RedisCluster, exists); ZEND_METHOD(RedisCluster, touch); ZEND_METHOD(RedisCluster, expire); ZEND_METHOD(RedisCluster, expireat); ZEND_METHOD(RedisCluster, expiretime); ZEND_METHOD(RedisCluster, pexpiretime); ZEND_METHOD(RedisCluster, flushall); ZEND_METHOD(RedisCluster, flushdb); ZEND_METHOD(RedisCluster, geoadd); ZEND_METHOD(RedisCluster, geodist); ZEND_METHOD(RedisCluster, geohash); ZEND_METHOD(RedisCluster, geopos); ZEND_METHOD(RedisCluster, georadius); ZEND_METHOD(RedisCluster, georadius_ro); ZEND_METHOD(RedisCluster, georadiusbymember); ZEND_METHOD(RedisCluster, georadiusbymember_ro); ZEND_METHOD(RedisCluster, geosearch); ZEND_METHOD(RedisCluster, geosearchstore); ZEND_METHOD(RedisCluster, get); ZEND_METHOD(RedisCluster, getbit); ZEND_METHOD(RedisCluster, getlasterror); ZEND_METHOD(RedisCluster, getmode); ZEND_METHOD(RedisCluster, getoption); ZEND_METHOD(RedisCluster, getrange); ZEND_METHOD(RedisCluster, lcs); ZEND_METHOD(RedisCluster, getset); ZEND_METHOD(RedisCluster, gettransferredbytes); ZEND_METHOD(RedisCluster, cleartransferredbytes); ZEND_METHOD(RedisCluster, hdel); ZEND_METHOD(RedisCluster, hexists); ZEND_METHOD(RedisCluster, hget); ZEND_METHOD(RedisCluster, hgetall); ZEND_METHOD(RedisCluster, hincrby); ZEND_METHOD(RedisCluster, hincrbyfloat); ZEND_METHOD(RedisCluster, hkeys); ZEND_METHOD(RedisCluster, hlen); ZEND_METHOD(RedisCluster, hmget); ZEND_METHOD(RedisCluster, hmset); ZEND_METHOD(RedisCluster, hscan); ZEND_METHOD(RedisCluster, hrandfield); ZEND_METHOD(RedisCluster, hset); ZEND_METHOD(RedisCluster, hsetnx); ZEND_METHOD(RedisCluster, hstrlen); ZEND_METHOD(RedisCluster, hvals); ZEND_METHOD(RedisCluster, incr); ZEND_METHOD(RedisCluster, incrby); ZEND_METHOD(RedisCluster, incrbyfloat); ZEND_METHOD(RedisCluster, info); ZEND_METHOD(RedisCluster, keys); ZEND_METHOD(RedisCluster, lastsave); ZEND_METHOD(RedisCluster, lget); ZEND_METHOD(RedisCluster, lindex); ZEND_METHOD(RedisCluster, linsert); ZEND_METHOD(RedisCluster, llen); ZEND_METHOD(RedisCluster, lpop); ZEND_METHOD(RedisCluster, lpos); ZEND_METHOD(RedisCluster, lpush); ZEND_METHOD(RedisCluster, lpushx); ZEND_METHOD(RedisCluster, lrange); ZEND_METHOD(RedisCluster, lrem); ZEND_METHOD(RedisCluster, lset); ZEND_METHOD(RedisCluster, ltrim); ZEND_METHOD(RedisCluster, mget); ZEND_METHOD(RedisCluster, mset); ZEND_METHOD(RedisCluster, msetnx); ZEND_METHOD(RedisCluster, multi); ZEND_METHOD(RedisCluster, object); ZEND_METHOD(RedisCluster, persist); ZEND_METHOD(RedisCluster, pexpire); ZEND_METHOD(RedisCluster, pexpireat); ZEND_METHOD(RedisCluster, pfadd); ZEND_METHOD(RedisCluster, pfcount); ZEND_METHOD(RedisCluster, pfmerge); ZEND_METHOD(RedisCluster, ping); ZEND_METHOD(RedisCluster, psetex); ZEND_METHOD(RedisCluster, psubscribe); ZEND_METHOD(RedisCluster, pttl); ZEND_METHOD(RedisCluster, publish); ZEND_METHOD(RedisCluster, pubsub); ZEND_METHOD(RedisCluster, punsubscribe); ZEND_METHOD(RedisCluster, randomkey); ZEND_METHOD(RedisCluster, rawcommand); ZEND_METHOD(RedisCluster, rename); ZEND_METHOD(RedisCluster, renamenx); ZEND_METHOD(RedisCluster, restore); ZEND_METHOD(RedisCluster, role); ZEND_METHOD(RedisCluster, rpop); ZEND_METHOD(RedisCluster, rpoplpush); ZEND_METHOD(RedisCluster, rpush); ZEND_METHOD(RedisCluster, rpushx); ZEND_METHOD(RedisCluster, sadd); ZEND_METHOD(RedisCluster, saddarray); ZEND_METHOD(RedisCluster, save); ZEND_METHOD(RedisCluster, scan); ZEND_METHOD(RedisCluster, scard); ZEND_METHOD(RedisCluster, script); ZEND_METHOD(RedisCluster, sdiff); ZEND_METHOD(RedisCluster, sdiffstore); ZEND_METHOD(RedisCluster, set); ZEND_METHOD(RedisCluster, setbit); ZEND_METHOD(RedisCluster, setex); ZEND_METHOD(RedisCluster, setnx); ZEND_METHOD(RedisCluster, setoption); ZEND_METHOD(RedisCluster, setrange); ZEND_METHOD(RedisCluster, sinter); ZEND_METHOD(RedisCluster, sintercard); ZEND_METHOD(RedisCluster, sinterstore); ZEND_METHOD(RedisCluster, sismember); ZEND_METHOD(RedisCluster, smismember); ZEND_METHOD(RedisCluster, slowlog); ZEND_METHOD(RedisCluster, smembers); ZEND_METHOD(RedisCluster, smove); ZEND_METHOD(RedisCluster, sort); ZEND_METHOD(RedisCluster, sort_ro); ZEND_METHOD(RedisCluster, spop); ZEND_METHOD(RedisCluster, srandmember); ZEND_METHOD(RedisCluster, srem); ZEND_METHOD(RedisCluster, sscan); ZEND_METHOD(RedisCluster, strlen); ZEND_METHOD(RedisCluster, subscribe); ZEND_METHOD(RedisCluster, sunion); ZEND_METHOD(RedisCluster, sunionstore); ZEND_METHOD(RedisCluster, time); ZEND_METHOD(RedisCluster, ttl); ZEND_METHOD(RedisCluster, type); ZEND_METHOD(RedisCluster, unsubscribe); ZEND_METHOD(RedisCluster, unlink); ZEND_METHOD(RedisCluster, unwatch); ZEND_METHOD(RedisCluster, watch); ZEND_METHOD(RedisCluster, xack); ZEND_METHOD(RedisCluster, xadd); ZEND_METHOD(RedisCluster, xclaim); ZEND_METHOD(RedisCluster, xdel); ZEND_METHOD(RedisCluster, xgroup); ZEND_METHOD(RedisCluster, xautoclaim); ZEND_METHOD(RedisCluster, xinfo); ZEND_METHOD(RedisCluster, xlen); ZEND_METHOD(RedisCluster, xpending); ZEND_METHOD(RedisCluster, xrange); ZEND_METHOD(RedisCluster, xread); ZEND_METHOD(RedisCluster, xreadgroup); ZEND_METHOD(RedisCluster, xrevrange); ZEND_METHOD(RedisCluster, xtrim); ZEND_METHOD(RedisCluster, zadd); ZEND_METHOD(RedisCluster, zcard); ZEND_METHOD(RedisCluster, zcount); ZEND_METHOD(RedisCluster, zincrby); ZEND_METHOD(RedisCluster, zinterstore); ZEND_METHOD(RedisCluster, zintercard); ZEND_METHOD(RedisCluster, zlexcount); ZEND_METHOD(RedisCluster, zpopmax); ZEND_METHOD(RedisCluster, zpopmin); ZEND_METHOD(RedisCluster, zrange); ZEND_METHOD(RedisCluster, zrangestore); ZEND_METHOD(RedisCluster, zrandmember); ZEND_METHOD(RedisCluster, zrangebylex); ZEND_METHOD(RedisCluster, zrangebyscore); ZEND_METHOD(RedisCluster, zrank); ZEND_METHOD(RedisCluster, zrem); ZEND_METHOD(RedisCluster, zremrangebylex); ZEND_METHOD(RedisCluster, zremrangebyrank); ZEND_METHOD(RedisCluster, zremrangebyscore); ZEND_METHOD(RedisCluster, zrevrange); ZEND_METHOD(RedisCluster, zrevrangebylex); ZEND_METHOD(RedisCluster, zrevrangebyscore); ZEND_METHOD(RedisCluster, zrevrank); ZEND_METHOD(RedisCluster, zscan); ZEND_METHOD(RedisCluster, zscore); ZEND_METHOD(RedisCluster, zmscore); ZEND_METHOD(RedisCluster, zunionstore); ZEND_METHOD(RedisCluster, zinter); ZEND_METHOD(RedisCluster, zdiffstore); ZEND_METHOD(RedisCluster, zunion); ZEND_METHOD(RedisCluster, zdiff); static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _compress, arginfo_class_RedisCluster__compress, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _pack, arginfo_class_RedisCluster__pack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgrewriteaof, arginfo_class_RedisCluster_bgrewriteaof, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgsave, arginfo_class_RedisCluster_bgsave, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitcount, arginfo_class_RedisCluster_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitop, arginfo_class_RedisCluster_bitop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitpos, arginfo_class_RedisCluster_bitpos, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, blpop, arginfo_class_RedisCluster_blpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, brpop, arginfo_class_RedisCluster_brpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, brpoplpush, arginfo_class_RedisCluster_brpoplpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lmove, arginfo_class_RedisCluster_lmove, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, blmove, arginfo_class_RedisCluster_blmove, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzpopmax, arginfo_class_RedisCluster_bzpopmax, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzpopmin, arginfo_class_RedisCluster_bzpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzmpop, arginfo_class_RedisCluster_bzmpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zmpop, arginfo_class_RedisCluster_zmpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, blmpop, arginfo_class_RedisCluster_blmpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lmpop, arginfo_class_RedisCluster_lmpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, clearlasterror, arginfo_class_RedisCluster_clearlasterror, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, client, arginfo_class_RedisCluster_client, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, close, arginfo_class_RedisCluster_close, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, cluster, arginfo_class_RedisCluster_cluster, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, command, arginfo_class_RedisCluster_command, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, config, arginfo_class_RedisCluster_config, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, copy, arginfo_class_RedisCluster_copy, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, echo, arginfo_class_RedisCluster_echo, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, eval, arginfo_class_RedisCluster_eval, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, eval_ro, arginfo_class_RedisCluster_eval_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, evalsha, arginfo_class_RedisCluster_evalsha, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, evalsha_ro, arginfo_class_RedisCluster_evalsha_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, exec, arginfo_class_RedisCluster_exec, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, touch, arginfo_class_RedisCluster_touch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, expireat, arginfo_class_RedisCluster_expireat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, expiretime, arginfo_class_RedisCluster_expiretime, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pexpiretime, arginfo_class_RedisCluster_pexpiretime, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, flushall, arginfo_class_RedisCluster_flushall, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, flushdb, arginfo_class_RedisCluster_flushdb, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geoadd, arginfo_class_RedisCluster_geoadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geodist, arginfo_class_RedisCluster_geodist, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geohash, arginfo_class_RedisCluster_geohash, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geopos, arginfo_class_RedisCluster_geopos, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadius, arginfo_class_RedisCluster_georadius, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadius_ro, arginfo_class_RedisCluster_georadius_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadiusbymember, arginfo_class_RedisCluster_georadiusbymember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadiusbymember_ro, arginfo_class_RedisCluster_georadiusbymember_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, gettransferredbytes, arginfo_class_RedisCluster_gettransferredbytes, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, cleartransferredbytes, arginfo_class_RedisCluster_cleartransferredbytes, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hgetall, arginfo_class_RedisCluster_hgetall, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hincrby, arginfo_class_RedisCluster_hincrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hincrbyfloat, arginfo_class_RedisCluster_hincrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hkeys, arginfo_class_RedisCluster_hkeys, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hlen, arginfo_class_RedisCluster_hlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hrandfield, arginfo_class_RedisCluster_hrandfield, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incrbyfloat, arginfo_class_RedisCluster_incrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, info, arginfo_class_RedisCluster_info, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, keys, arginfo_class_RedisCluster_keys, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lastsave, arginfo_class_RedisCluster_lastsave, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lget, arginfo_class_RedisCluster_lget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lindex, arginfo_class_RedisCluster_lindex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, linsert, arginfo_class_RedisCluster_linsert, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, llen, arginfo_class_RedisCluster_llen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpop, arginfo_class_RedisCluster_lpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpos, arginfo_class_RedisCluster_lpos, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpush, arginfo_class_RedisCluster_lpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpushx, arginfo_class_RedisCluster_lpushx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lrange, arginfo_class_RedisCluster_lrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lrem, arginfo_class_RedisCluster_lrem, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lset, arginfo_class_RedisCluster_lset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, ltrim, arginfo_class_RedisCluster_ltrim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, mget, arginfo_class_RedisCluster_mget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, mset, arginfo_class_RedisCluster_mset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, msetnx, arginfo_class_RedisCluster_msetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, multi, arginfo_class_RedisCluster_multi, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, object, arginfo_class_RedisCluster_object, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, persist, arginfo_class_RedisCluster_persist, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pexpire, arginfo_class_RedisCluster_pexpire, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pexpireat, arginfo_class_RedisCluster_pexpireat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pfadd, arginfo_class_RedisCluster_pfadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pfcount, arginfo_class_RedisCluster_pfcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pfmerge, arginfo_class_RedisCluster_pfmerge, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, ping, arginfo_class_RedisCluster_ping, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, psetex, arginfo_class_RedisCluster_psetex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, psubscribe, arginfo_class_RedisCluster_psubscribe, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pttl, arginfo_class_RedisCluster_pttl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, publish, arginfo_class_RedisCluster_publish, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, pubsub, arginfo_class_RedisCluster_pubsub, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, punsubscribe, arginfo_class_RedisCluster_punsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, randomkey, arginfo_class_RedisCluster_randomkey, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rawcommand, arginfo_class_RedisCluster_rawcommand, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rename, arginfo_class_RedisCluster_rename, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, renamenx, arginfo_class_RedisCluster_renamenx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, restore, arginfo_class_RedisCluster_restore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, role, arginfo_class_RedisCluster_role, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rpop, arginfo_class_RedisCluster_rpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rpoplpush, arginfo_class_RedisCluster_rpoplpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rpush, arginfo_class_RedisCluster_rpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, rpushx, arginfo_class_RedisCluster_rpushx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sadd, arginfo_class_RedisCluster_sadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, saddarray, arginfo_class_RedisCluster_saddarray, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, save, arginfo_class_RedisCluster_save, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, scan, arginfo_class_RedisCluster_scan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, scard, arginfo_class_RedisCluster_scard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, script, arginfo_class_RedisCluster_script, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sdiff, arginfo_class_RedisCluster_sdiff, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sdiffstore, arginfo_class_RedisCluster_sdiffstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, set, arginfo_class_RedisCluster_set, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setbit, arginfo_class_RedisCluster_setbit, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setex, arginfo_class_RedisCluster_setex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setnx, arginfo_class_RedisCluster_setnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setoption, arginfo_class_RedisCluster_setoption, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, setrange, arginfo_class_RedisCluster_setrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sinter, arginfo_class_RedisCluster_sinter, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sintercard, arginfo_class_RedisCluster_sintercard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sinterstore, arginfo_class_RedisCluster_sinterstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sismember, arginfo_class_RedisCluster_sismember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smismember, arginfo_class_RedisCluster_smismember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, slowlog, arginfo_class_RedisCluster_slowlog, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smembers, arginfo_class_RedisCluster_smembers, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smove, arginfo_class_RedisCluster_smove, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sort, arginfo_class_RedisCluster_sort, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sort_ro, arginfo_class_RedisCluster_sort_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, spop, arginfo_class_RedisCluster_spop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, srandmember, arginfo_class_RedisCluster_srandmember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, srem, arginfo_class_RedisCluster_srem, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sscan, arginfo_class_RedisCluster_sscan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, strlen, arginfo_class_RedisCluster_strlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, subscribe, arginfo_class_RedisCluster_subscribe, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sunion, arginfo_class_RedisCluster_sunion, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sunionstore, arginfo_class_RedisCluster_sunionstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, time, arginfo_class_RedisCluster_time, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, ttl, arginfo_class_RedisCluster_ttl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, type, arginfo_class_RedisCluster_type, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, unsubscribe, arginfo_class_RedisCluster_unsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, unlink, arginfo_class_RedisCluster_unlink, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, unwatch, arginfo_class_RedisCluster_unwatch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, watch, arginfo_class_RedisCluster_watch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xack, arginfo_class_RedisCluster_xack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xadd, arginfo_class_RedisCluster_xadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xautoclaim, arginfo_class_RedisCluster_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xlen, arginfo_class_RedisCluster_xlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xpending, arginfo_class_RedisCluster_xpending, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xrange, arginfo_class_RedisCluster_xrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xread, arginfo_class_RedisCluster_xread, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xreadgroup, arginfo_class_RedisCluster_xreadgroup, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xrevrange, arginfo_class_RedisCluster_xrevrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xtrim, arginfo_class_RedisCluster_xtrim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zadd, arginfo_class_RedisCluster_zadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zcard, arginfo_class_RedisCluster_zcard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zcount, arginfo_class_RedisCluster_zcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zincrby, arginfo_class_RedisCluster_zincrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zinterstore, arginfo_class_RedisCluster_zinterstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zintercard, arginfo_class_RedisCluster_zintercard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zlexcount, arginfo_class_RedisCluster_zlexcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangestore, arginfo_class_RedisCluster_zrangestore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrandmember, arginfo_class_RedisCluster_zrandmember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrem, arginfo_class_RedisCluster_zrem, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zremrangebylex, arginfo_class_RedisCluster_zremrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zremrangebyrank, arginfo_class_RedisCluster_zremrangebyrank, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zremrangebyscore, arginfo_class_RedisCluster_zremrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrevrange, arginfo_class_RedisCluster_zrevrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrevrangebylex, arginfo_class_RedisCluster_zrevrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrevrangebyscore, arginfo_class_RedisCluster_zrevrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrevrank, arginfo_class_RedisCluster_zrevrank, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zscan, arginfo_class_RedisCluster_zscan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zscore, arginfo_class_RedisCluster_zscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zmscore, arginfo_class_RedisCluster_zmscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zunionstore, arginfo_class_RedisCluster_zunionstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zinter, arginfo_class_RedisCluster_zinter, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zdiffstore, arginfo_class_RedisCluster_zdiffstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zunion, arginfo_class_RedisCluster_zunion, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zdiff, arginfo_class_RedisCluster_zdiff, ZEND_ACC_PUBLIC) ZEND_FE_END }; static const zend_function_entry class_RedisClusterException_methods[] = { ZEND_FE_END }; static zend_class_entry *register_class_RedisCluster(void) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); zval const_OPT_SLAVE_FAILOVER_value; ZVAL_LONG(&const_OPT_SLAVE_FAILOVER_value, REDIS_OPT_FAILOVER); zend_string *const_OPT_SLAVE_FAILOVER_name = zend_string_init_interned("OPT_SLAVE_FAILOVER", sizeof("OPT_SLAVE_FAILOVER") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_SLAVE_FAILOVER_name, &const_OPT_SLAVE_FAILOVER_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_SLAVE_FAILOVER_name); zval const_FAILOVER_NONE_value; ZVAL_LONG(&const_FAILOVER_NONE_value, REDIS_FAILOVER_NONE); zend_string *const_FAILOVER_NONE_name = zend_string_init_interned("FAILOVER_NONE", sizeof("FAILOVER_NONE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_NONE_name, &const_FAILOVER_NONE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_NONE_name); zval const_FAILOVER_ERROR_value; ZVAL_LONG(&const_FAILOVER_ERROR_value, REDIS_FAILOVER_ERROR); zend_string *const_FAILOVER_ERROR_name = zend_string_init_interned("FAILOVER_ERROR", sizeof("FAILOVER_ERROR") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_ERROR_name, &const_FAILOVER_ERROR_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_ERROR_name); zval const_FAILOVER_DISTRIBUTE_value; ZVAL_LONG(&const_FAILOVER_DISTRIBUTE_value, REDIS_FAILOVER_DISTRIBUTE); zend_string *const_FAILOVER_DISTRIBUTE_name = zend_string_init_interned("FAILOVER_DISTRIBUTE", sizeof("FAILOVER_DISTRIBUTE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_name, &const_FAILOVER_DISTRIBUTE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_DISTRIBUTE_name); zval const_FAILOVER_DISTRIBUTE_SLAVES_value; ZVAL_LONG(&const_FAILOVER_DISTRIBUTE_SLAVES_value, REDIS_FAILOVER_DISTRIBUTE_SLAVES); zend_string *const_FAILOVER_DISTRIBUTE_SLAVES_name = zend_string_init_interned("FAILOVER_DISTRIBUTE_SLAVES", sizeof("FAILOVER_DISTRIBUTE_SLAVES") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_SLAVES_name, &const_FAILOVER_DISTRIBUTE_SLAVES_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_DISTRIBUTE_SLAVES_name); return class_entry; } static zend_class_entry *register_class_RedisClusterException(zend_class_entry *class_entry_RuntimeException) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisClusterException", class_RedisClusterException_methods); class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException); return class_entry; } redis-6.0.2/redis_commands.c0000644000175000000120000063262714515245367016573 0ustar pyatsukhnenkowheel/* -*- 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: Michael Grunder | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "redis_commands.h" #include "php_network.h" #ifndef PHP_WIN32 #include /* TCP_KEEPALIVE */ #else #include #endif #include /* Georadius sort type */ typedef enum geoSortType { SORT_NONE, SORT_ASC, SORT_DESC } geoSortType; /* Georadius store type */ typedef enum geoStoreType { STORE_NONE, STORE_COORD, STORE_DIST } geoStoreType; /* Georadius options structure */ typedef struct geoOptions { int withcoord; int withdist; int withhash; long count; zend_bool any; geoSortType sort; geoStoreType store; zend_string *key; } geoOptions; typedef struct redisLcsOptions { zend_bool len; zend_bool idx; zend_long minmatchlen; zend_bool withmatchlen; } redisLcsOptions; typedef struct redisRestoreOptions { zend_bool replace; zend_bool absttl; zend_long idletime; zend_long freq; } redisRestoreOptions; #define REDIS_ZCMD_HAS_DST_KEY (1 << 0) #define REDIS_ZCMD_HAS_WITHSCORES (1 << 1) #define REDIS_ZCMD_HAS_BY_LEX_SCORE (1 << 2) #define REDIS_ZCMD_HAS_REV (1 << 3) #define REDIS_ZCMD_HAS_LIMIT (1 << 4) #define REDIS_ZCMD_INT_RANGE (1 << 5) #define REDIS_ZCMD_HAS_AGGREGATE (1 << 6) /* ZRANGE, ZRANGEBYSCORE, ZRANGESTORE options */ typedef struct redisZcmdOptions { zend_bool withscores; zend_bool byscore; zend_bool bylex; zend_bool rev; zend_string *aggregate; struct { zend_bool enabled; zend_long offset; zend_long count; } limit; } redisZcmdOptions; /* Local passthrough macro for command construction. Given that these methods * are generic (so they work whether the caller is Redis or RedisCluster) we * will always have redis_sock, slot*, and */ #define REDIS_CMD_SPPRINTF(ret, kw, fmt, ...) \ redis_spprintf(redis_sock, slot, ret, kw, fmt, ##__VA_ARGS__) /* Generic commands based on method signature and what kind of things we're * processing. Lots of Redis commands take something like key, value, or * key, value long. Each unique signature like this is written only once */ /* A command that takes no arguments */ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, ""); return SUCCESS; } /* Helper to construct a raw command. Given that the cluster and non cluster * versions are different (RedisCluster needs an additional argument to direct * the command) we take the start of our array and count */ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len) { smart_string cmdstr = {0}; int i; /* Make sure our first argument is a string */ if (Z_TYPE(z_args[0]) != IS_STRING) { php_error_docref(NULL, E_WARNING, "When sending a 'raw' command, the first argument must be a string!"); return FAILURE; } /* Initialize our command string */ redis_cmd_init_sstr(&cmdstr, argc-1, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); for (i = 1; i < argc; i++) { switch (Z_TYPE(z_args[i])) { case IS_STRING: redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[i]), Z_STRLEN(z_args[i])); break; case IS_LONG: redis_cmd_append_sstr_long(&cmdstr,Z_LVAL(z_args[i])); break; case IS_DOUBLE: redis_cmd_append_sstr_dbl(&cmdstr,Z_DVAL(z_args[i])); break; default: php_error_docref(NULL, E_WARNING, "Raw command arguments must be scalar values!"); efree(cmdstr.c); return FAILURE; } } /* Push command and length to caller */ *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } smart_string * redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args) { int i; zend_string *zstr; if (Z_TYPE(z_args[0]) != IS_STRING) { return NULL; } // Branch based on the directive if (!strcasecmp(Z_STRVAL(z_args[0]), "kill")) { // Simple SCRIPT_KILL command REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); redis_cmd_append_sstr(cmd, ZEND_STRL("KILL")); } else if (!strcasecmp(Z_STRVAL(z_args[0]), "flush")) { // Simple SCRIPT FLUSH [ASYNC | SYNC] if (argc > 1 && ( Z_TYPE(z_args[1]) != IS_STRING || strcasecmp(Z_STRVAL(z_args[1]), "sync") || strcasecmp(Z_STRVAL(z_args[1]), "async") )) { return NULL; } REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); redis_cmd_append_sstr(cmd, ZEND_STRL("FLUSH")); if (argc > 1) { redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } } else if (!strcasecmp(Z_STRVAL(z_args[0]), "load")) { // Make sure we have a second argument, and it's not empty. If it is // empty, we can just return an empty array (which is what Redis does) if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING || Z_STRLEN(z_args[1]) < 1) { return NULL; } // Format our SCRIPT LOAD command REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); redis_cmd_append_sstr(cmd, ZEND_STRL("LOAD")); redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } else if (!strcasecmp(Z_STRVAL(z_args[0]), "exists")) { // Make sure we have a second argument if (argc < 2) { return NULL; } /* Construct our SCRIPT EXISTS command */ REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); redis_cmd_append_sstr(cmd, ZEND_STRL("EXISTS")); for (i = 1; i < argc; ++i) { zstr = zval_get_string(&z_args[i]); redis_cmd_append_sstr(cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } } else { /* Unknown directive */ return NULL; } return cmd; } /* Command that takes one optional string */ int redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *arg = NULL; size_t arglen; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &arg, &arglen) == FAILURE) { return FAILURE; } if (arg != NULL) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", arg, arglen); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, ""); } return SUCCESS; } /* Generic command where we just take a string and do nothing to it*/ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *arg; size_t arg_len; // Parse args if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) ==FAILURE) { return FAILURE; } // Build the command without molesting the string *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", arg, arg_len); return SUCCESS; } /* Key, long, zval (serialized) */ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key = NULL; size_t key_len; zend_long expire; zval *z_val; if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &key, &key_len, &expire, &z_val) == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "klv", key, key_len, expire, z_val); return SUCCESS; } /* Generic key, long, string (unserialized) */ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *val; size_t key_len, val_len; zend_long lval; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &key, &key_len, &lval, &val, &val_len) == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kds", key, key_len, (int)lval, val, val_len); return SUCCESS; } /* Generic command construction when we just take a key and value */ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; zval *z_val; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &key, &key_len, &z_val) == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kv", key, key_len, z_val); return SUCCESS; } /* Generic command that takes a key and an unserialized value */ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *val; size_t key_len, val_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &val, &val_len) == FAILURE) { return FAILURE; } // Construct command *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ks", key, key_len, val, val_len); return SUCCESS; } /* Key, string, string without serialization (ZCOUNT, ZREMRANGEBYSCORE) */ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *k, *v1, *v2; size_t klen, v1len, v2len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &k, &klen, &v1, &v1len, &v2, &v2len) == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", k, klen, v1, v1len, v2, v2len); // Success! return SUCCESS; } /* Generic command that takes two keys */ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *key1 = NULL, *key2 = NULL; smart_string cmdstr = {0}; short slot2; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key1) Z_PARAM_STR(key2) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); redis_cmd_init_sstr(&cmdstr, 2, kw, strlen(kw)); redis_cmd_append_sstr_key_zstr(&cmdstr, key1, redis_sock, slot); redis_cmd_append_sstr_key_zstr(&cmdstr, key2, redis_sock, slot ? &slot2 : NULL); if (slot && *slot != slot2) { php_error_docref(0, E_WARNING, "Keys don't hash to the same slot"); smart_string_free(&cmdstr); return FAILURE; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* Generic command construction where we take a key and a long */ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *key = NULL; zend_long lval = 0; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_LONG(lval) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kl", ZSTR_VAL(key), ZSTR_LEN(key), lval); return SUCCESS; } /* long, long */ int redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_long l1 = 0, l2 = 0; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(l1) Z_PARAM_LONG(l2) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ll", l1, l2); return SUCCESS; } /* key, long, long */ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; zend_long val1, val2; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &key, &key_len, &val1, &val2) == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kll", key, key_len, val1, val2); return SUCCESS; } /* Generic command where we take a single key */ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) ==FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "k", key, key_len); return SUCCESS; } int redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { int argc; smart_string cmdstr = {0}; zend_bool abort = 0, force = 0; zend_long timeout = 0, port = 0; zend_string *zkey, *host = NULL; zval *z_to = NULL, *z_ele; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!bl", &z_to, &abort, &timeout) == FAILURE) { return FAILURE; } if (z_to != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_to), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "host")) { host = zval_get_string(z_ele); } else if (zend_string_equals_literal_ci(zkey, "port")) { port = zval_get_long(z_ele); } else if (zend_string_equals_literal_ci(zkey, "force")) { force = zval_is_true(z_ele); } } } ZEND_HASH_FOREACH_END(); if (!host || !port) { php_error_docref(NULL, E_WARNING, "host and port must be provided!"); if (host) zend_string_release(host); return FAILURE; } } argc = (host && port ? 3 + force : 0) + abort + (timeout > 0 ? 2 : 0); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "FAILOVER"); if (host && port) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TO"); redis_cmd_append_sstr_zstr(&cmdstr, host); redis_cmd_append_sstr_int(&cmdstr, port); if (force) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FORCE"); } zend_string_release(host); } if (abort) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ABORT"); } if (timeout > 0) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TIMEOUT"); redis_cmd_append_sstr_long(&cmdstr, timeout); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_bool sync = 0; zend_bool is_null = 1; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_BOOL_OR_NULL(sync, is_null) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); redis_cmd_init_sstr(&cmdstr, !is_null, kw, strlen(kw)); if (!is_null) { ZEND_ASSERT(sync == 0 || sync == 1); if (sync == 0) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASYNC"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SYNC"); } } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* Generic command where we take a key and a double */ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; double val; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &key, &key_len, &val) == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kf", key, key_len, val); return SUCCESS; } /* Generic to construct SCAN and variant commands */ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count) { static char *kw[] = {"SCAN","SSCAN","HSCAN","ZSCAN"}; int argc; smart_string cmdstr = {0}; // Figure out our argument count argc = 1 + (type!=TYPE_SCAN) + (pat_len>0?2:0) + (count>0?2:0); redis_cmd_init_sstr(&cmdstr, argc, kw[type], strlen(kw[type])); // Append our key if it's not a regular SCAN command if (type != TYPE_SCAN) { redis_cmd_append_sstr(&cmdstr, key, key_len); } // Append cursor redis_cmd_append_sstr_long(&cmdstr, it); // Append count if we've got one if (count) { redis_cmd_append_sstr(&cmdstr, ZEND_STRL("COUNT")); redis_cmd_append_sstr_long(&cmdstr, count); } // Append pattern if we've got one if (pat_len) { redis_cmd_append_sstr(&cmdstr, ZEND_STRL("MATCH")); redis_cmd_append_sstr(&cmdstr,pat,pat_len); } // Push command to the caller, return length *cmd = cmdstr.c; return cmdstr.len; } void redis_get_zcmd_options(redisZcmdOptions *dst, zval *src, int flags) { zval *zv, *zoff, *zcnt; zend_string *key; ZEND_ASSERT(dst != NULL); memset(dst, 0, sizeof(*dst)); if (src == NULL) return; if (Z_TYPE_P(src) != IS_ARRAY) { if (Z_TYPE_P(src) == IS_TRUE && (flags & REDIS_ZCMD_HAS_WITHSCORES)) dst->withscores = 1; return; } ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(src), key, zv) { ZVAL_DEREF(zv); if (key) { if ((flags & REDIS_ZCMD_HAS_WITHSCORES) && zend_string_equals_literal_ci(key, "WITHSCORES")) dst->withscores = zval_is_true(zv); else if ((flags & REDIS_ZCMD_HAS_LIMIT) && zend_string_equals_literal_ci(key, "LIMIT") && Z_TYPE_P(zv) == IS_ARRAY) { if ((zoff = zend_hash_index_find(Z_ARRVAL_P(zv), 0)) != NULL && (zcnt = zend_hash_index_find(Z_ARRVAL_P(zv), 1)) != NULL) { dst->limit.enabled = 1; dst->limit.offset = zval_get_long(zoff); dst->limit.count = zval_get_long(zcnt); } else { php_error_docref(NULL, E_WARNING, "LIMIT offset and count must be an array with twe elements"); } } else if ((flags & REDIS_ZCMD_HAS_AGGREGATE && zend_string_equals_literal_ci(key, "AGGREGATE")) && Z_TYPE_P(zv) == IS_STRING) { if (Z_TYPE_P(zv) != IS_STRING || (!zend_string_equals_literal_ci(Z_STR_P(zv), "SUM") && !zend_string_equals_literal_ci(Z_STR_P(zv), "MIN") && !zend_string_equals_literal_ci(Z_STR_P(zv), "MAX"))) { php_error_docref(NULL, E_WARNING, "Valid AGGREGATE options are 'SUM', 'MIN', or 'MAX'"); } else { dst->aggregate = Z_STR_P(zv); } } } else if (Z_TYPE_P(zv) == IS_STRING) { key = Z_STR_P(zv); if ((flags & REDIS_ZCMD_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYSCORE")) dst->byscore = 1, dst->bylex = 0; else if ((flags & REDIS_ZCMD_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYLEX")) dst->bylex = 1, dst->byscore = 0; else if ((flags & REDIS_ZCMD_HAS_REV) && zend_string_equals_literal_ci(key, "REV")) dst->rev = 1; else if ((flags & REDIS_ZCMD_HAS_WITHSCORES && zend_string_equals_literal_ci(key, "WITHSCORES"))) dst->withscores = 1; } } ZEND_HASH_FOREACH_END(); } // + ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count] [WITHSCORES] // + ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset count] // + ZREVRANGE key start stop [WITHSCORES] // + ZRANGEBYSCORE key min max [LIMIT offset count] [WITHSCORES] // + ZREVRANGEBYSCORE key max min [LIMIT offset count] [WITHSCORES] // - ZRANGEBYLEX key min max [LIMIT offset count] // - ZREVRANGEBYLEX key max min [LIMIT offset count] // - ZDIFF [WITHSCORES] // - ZUNION [WITHSCORES] [AGGREGATE X] // - ZINTER [WITHSCORES] [AGGREGATE X] static int redis_get_zcmd_flags(const char *kw) { size_t len = strlen(kw); if (REDIS_STRICMP_STATIC(kw, len, "ZRANGESTORE")) { return REDIS_ZCMD_HAS_DST_KEY | REDIS_ZCMD_HAS_WITHSCORES | REDIS_ZCMD_HAS_BY_LEX_SCORE | REDIS_ZCMD_HAS_REV | REDIS_ZCMD_HAS_LIMIT; } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGE")) { return REDIS_ZCMD_HAS_WITHSCORES | REDIS_ZCMD_HAS_BY_LEX_SCORE | REDIS_ZCMD_HAS_REV | REDIS_ZCMD_HAS_LIMIT; } else if (REDIS_STRICMP_STATIC(kw, len, "ZREVRANGE")) { return REDIS_ZCMD_HAS_WITHSCORES | REDIS_ZCMD_INT_RANGE; } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGEBYSCORE") || REDIS_STRICMP_STATIC(kw, len, "ZREVRANGEBYSCORE")) { return REDIS_ZCMD_HAS_LIMIT | REDIS_ZCMD_HAS_WITHSCORES; } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGEBYLEX") || REDIS_STRICMP_STATIC(kw, len, "ZREVRANGEBYLEX")) { return REDIS_ZCMD_HAS_LIMIT; } else if (REDIS_STRICMP_STATIC(kw, len, "ZDIFF")) { return REDIS_ZCMD_HAS_WITHSCORES; } else if (REDIS_STRICMP_STATIC(kw, len, "ZINTER") || REDIS_STRICMP_STATIC(kw, len, "ZUNION")) { return REDIS_ZCMD_HAS_WITHSCORES | REDIS_ZCMD_HAS_AGGREGATE; } /* Reaching this line means a compile-time error */ ZEND_ASSERT(0); } /* Validate ZLEX* min/max argument strings */ static int validate_zlex_arg(const char *str, size_t len) { return (len > 1 && (*str == '[' || *str == '(')) || (len == 1 && (*str == '+' || *str == '-')); } static int validate_zlex_arg_zval(zval *z) { return Z_TYPE_P(z) == IS_STRING && validate_zlex_arg(Z_STRVAL_P(z), Z_STRLEN_P(z)); } int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *zoptions = NULL, *zstart = NULL, *zend = NULL; zend_string *dst = NULL, *src = NULL; zend_long start = 0, end = 0; smart_string cmdstr = {0}; redisZcmdOptions opt; int min_argc, flags; short slot2; flags = redis_get_zcmd_flags(kw); min_argc = 3 + (flags & REDIS_ZCMD_HAS_DST_KEY); ZEND_PARSE_PARAMETERS_START(min_argc, min_argc + 1) if (flags & REDIS_ZCMD_HAS_DST_KEY) { Z_PARAM_STR(dst) } Z_PARAM_STR(src) if (flags & REDIS_ZCMD_INT_RANGE) { Z_PARAM_LONG(start) Z_PARAM_LONG(end) } else { Z_PARAM_ZVAL(zstart) Z_PARAM_ZVAL(zend) } Z_PARAM_OPTIONAL Z_PARAM_ZVAL_OR_NULL(zoptions) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); redis_get_zcmd_options(&opt, zoptions, flags); if (opt.bylex) { ZEND_ASSERT(!(flags & REDIS_ZCMD_INT_RANGE)); if (!validate_zlex_arg_zval(zstart) || !validate_zlex_arg_zval(zend)) { php_error_docref(NULL, E_WARNING, "Legographical args must start with '[' or '(' or be '+' or '-'"); return FAILURE; } } redis_cmd_init_sstr(&cmdstr, min_argc + !!opt.bylex + !!opt.byscore + !!opt.rev + !!opt.withscores + (opt.limit.enabled ? 3 : 0), kw, strlen(kw)); if (flags & REDIS_ZCMD_HAS_DST_KEY) redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, &slot2); /* Protect the user from crossslot errors */ if ((flags & REDIS_ZCMD_HAS_DST_KEY) && slot && *slot != slot2) { php_error_docref(NULL, E_WARNING, "destination and source keys must map to the same slot"); efree(cmdstr.c); return FAILURE; } if (flags & REDIS_ZCMD_INT_RANGE) { redis_cmd_append_sstr_long(&cmdstr, start); redis_cmd_append_sstr_long(&cmdstr, end); } else { redis_cmd_append_sstr_zval(&cmdstr, zstart, NULL); redis_cmd_append_sstr_zval(&cmdstr, zend, NULL); } REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.byscore, "BYSCORE"); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.bylex, "BYLEX"); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.rev, "REV"); if (opt.limit.enabled) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT"); redis_cmd_append_sstr_long(&cmdstr, opt.limit.offset); redis_cmd_append_sstr_long(&cmdstr, opt.limit.count); } REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withscores, "WITHSCORES"); if (slot) *slot = slot2; *ctx = opt.withscores ? PHPREDIS_CTX_PTR : NULL; *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } static int redis_build_config_get_cmd(smart_string *dst, zval *val) { zend_string *zstr; int ncfg; zval *zv; if (val == NULL || (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY)) { php_error_docref(NULL, E_WARNING, "Must pass a string or array of values to CONFIG GET"); return FAILURE; } else if (Z_TYPE_P(val) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(val)) == 0) { php_error_docref(NULL, E_WARNING, "Cannot pass an empty array to CONFIG GET"); return FAILURE; } ncfg = Z_TYPE_P(val) == IS_STRING ? 1 : zend_hash_num_elements(Z_ARRVAL_P(val)); REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + ncfg, "CONFIG"); REDIS_CMD_APPEND_SSTR_STATIC(dst, "GET"); if (Z_TYPE_P(val) == IS_STRING) { redis_cmd_append_sstr_zstr(dst, Z_STR_P(val)); } else { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), zv) { ZVAL_DEREF(zv); zstr = zval_get_string(zv); redis_cmd_append_sstr_zstr(dst, zstr); zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); } return SUCCESS; } static int redis_build_config_set_cmd(smart_string *dst, zval *key, zend_string *val) { zend_string *zkey, *zstr; zval *zv; /* Legacy case: CONFIG SET */ if (key != NULL && val != NULL) { REDIS_CMD_INIT_SSTR_STATIC(dst, 3, "CONFIG"); REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET"); zstr = zval_get_string(key); redis_cmd_append_sstr_zstr(dst, zstr); zend_string_release(zstr); redis_cmd_append_sstr_zstr(dst, val); return SUCCESS; } /* Now we must have an array with at least one element */ if (key == NULL || Z_TYPE_P(key) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(key)) == 0) { php_error_docref(NULL, E_WARNING, "Must either pass two strings to CONFIG SET or a non-empty array of values"); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + (2 * zend_hash_num_elements(Z_ARRVAL_P(key))), "CONFIG"); REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET"); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(key), zkey, zv) { if (zkey == NULL) goto fail; ZVAL_DEREF(zv); redis_cmd_append_sstr_zstr(dst, zkey); zstr = zval_get_string(zv); redis_cmd_append_sstr_zstr(dst, zstr); zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); return SUCCESS; fail: php_error_docref(NULL, E_WARNING, "Must pass an associate array of config keys and values"); efree(dst->c); memset(dst, 0, sizeof(*dst)); return FAILURE; } int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *op = NULL, *arg = NULL; smart_string cmdstr = {0}; int res = FAILURE; zval *key = NULL; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_ZVAL_OR_NULL(key) Z_PARAM_STR_OR_NULL(arg) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_string_equals_literal_ci(op, "RESETSTAT") || zend_string_equals_literal_ci(op, "REWRITE")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CONFIG"); redis_cmd_append_sstr_zstr(&cmdstr, op); *ctx = redis_boolean_response; res = SUCCESS; } else if (zend_string_equals_literal_ci(op, "GET")) { res = redis_build_config_get_cmd(&cmdstr, key); *ctx = redis_mbulk_reply_zipped_raw; } else if (zend_string_equals_literal_ci(op, "SET")) { res = redis_build_config_set_cmd(&cmdstr, key, arg); *ctx = redis_boolean_response; } else { php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op)); return FAILURE; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return res; } int redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_string *op = NULL, *arg; zval *argv = NULL; int i, argc = 0; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); for (i = 0; i < argc; ++i) { if (Z_TYPE(argv[i]) != IS_STRING) { php_error_docref(NULL, E_WARNING, "invalid argument"); return FAILURE; } } if (zend_string_equals_literal_ci(op, "DELETE")) { if (argc < 1) { php_error_docref(NULL, E_WARNING, "argument required"); return FAILURE; } } else if (zend_string_equals_literal_ci(op, "DUMP")) { *ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "FLUSH")) { if (argc > 0 && !zend_string_equals_literal_ci(Z_STR(argv[0]), "SYNC") && !zend_string_equals_literal_ci(Z_STR(argv[0]), "ASYNC") ) { php_error_docref(NULL, E_WARNING, "invalid argument"); return FAILURE; } } else if (zend_string_equals_literal_ci(op, "KILL")) { // noop } else if (zend_string_equals_literal_ci(op, "LIST")) { if (argc > 0) { if (zend_string_equals_literal_ci(Z_STR(argv[0]), "LIBRARYNAME")) { if (argc < 2) { php_error_docref(NULL, E_WARNING, "argument required"); return FAILURE; } } else if (!zend_string_equals_literal_ci(Z_STR(argv[0]), "WITHCODE")) { php_error_docref(NULL, E_WARNING, "invalid argument"); return FAILURE; } } *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "LOAD")) { if (argc < 1 || ( zend_string_equals_literal_ci(Z_STR(argv[0]), "REPLACE") && argc < 2 )) { php_error_docref(NULL, E_WARNING, "argument required"); return FAILURE; } *ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "RESTORE")) { if (argc < 1 || ( argc > 1 && !zend_string_equals_literal_ci(Z_STR(argv[1]), "FLUSH") && !zend_string_equals_literal_ci(Z_STR(argv[1]), "APPEND") && !zend_string_equals_literal_ci(Z_STR(argv[1]), "REPLACE") )) { php_error_docref(NULL, E_WARNING, "invalid argument"); return FAILURE; } } else if (zend_string_equals_literal_ci(op, "STATS")) { *ctx = PHPREDIS_CTX_PTR + 1; } else { php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op)); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + argc, "FUNCTION"); redis_cmd_append_sstr_zstr(&cmdstr, op); for (i = 0; i < argc; i++) { arg = zval_get_string(&argv[i]); redis_cmd_append_sstr_zstr(&cmdstr, arg); zend_string_release(arg); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { HashTable *keys = NULL, *args = NULL; smart_string cmdstr = {0}; zend_string *fn = NULL; zval *zv; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_STR(fn) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT(keys) Z_PARAM_ARRAY_HT(args) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); redis_cmd_init_sstr(&cmdstr, 2 + (keys ? zend_hash_num_elements(keys) : 0) + (args ? zend_hash_num_elements(args) : 0), kw, strlen(kw)); redis_cmd_append_sstr_zstr(&cmdstr, fn); redis_cmd_append_sstr_long(&cmdstr, keys ? zend_hash_num_elements(keys) : 0); if (keys != NULL) { ZEND_HASH_FOREACH_VAL(keys, zv) { redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); } ZEND_HASH_FOREACH_END(); } if (args != NULL) { ZEND_HASH_FOREACH_VAL(args, zv) { redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); } ZEND_HASH_FOREACH_END(); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; int count = 0; size_t key_len; smart_string cmdstr = {0}; zend_bool withscores = 0; zval *z_opts = NULL, *z_ele; zend_string *zkey; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, &z_opts) == FAILURE) { return FAILURE; } if (z_opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "count")) { count = zval_get_long(z_ele); } else if (zend_string_equals_literal_ci(zkey, "withscores")) { withscores = zval_is_true(z_ele); } } } ZEND_HASH_FOREACH_END(); } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (count != 0) + withscores, "ZRANDMEMBER"); redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); if (count != 0) { redis_cmd_append_sstr_long(&cmdstr, count); *ctx = PHPREDIS_CTX_PTR; } if (withscores) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); *ctx = PHPREDIS_CTX_PTR + 1; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_keys, *z_opts = NULL, *z_key; redisZcmdOptions opts = {0}; smart_string cmdstr = {0}; int numkeys, flags; short s2 = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a", &z_keys, &z_opts) == FAILURE) { return FAILURE; } if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) { return FAILURE; } flags = redis_get_zcmd_flags("ZDIFF"); redis_get_zcmd_options(&opts, z_opts, flags); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + numkeys + opts.withscores, "ZDIFF"); redis_cmd_append_sstr_long(&cmdstr, numkeys); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_key) { ZVAL_DEREF(z_key); redis_cmd_append_sstr_key_zval(&cmdstr, z_key, redis_sock, slot); if (slot && s2 && s2 != *slot) { php_error_docref(NULL, E_WARNING, "Not all keys map to the same slot!"); efree(cmdstr.c); return FAILURE; } if (slot) s2 = *slot; } ZEND_HASH_FOREACH_END(); if (opts.withscores) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); *ctx = PHPREDIS_CTX_PTR; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } static int redis_cmd_append_sstr_score(smart_string *dst, zval *score) { zend_uchar type; zend_long lval; size_t cmdlen; double dval; /* Get current command length */ cmdlen = dst->len; if (Z_TYPE_P(score) == IS_LONG) { redis_cmd_append_sstr_long(dst, Z_LVAL_P(score)); } else if (Z_TYPE_P(score) == IS_DOUBLE) { redis_cmd_append_sstr_dbl(dst, Z_DVAL_P(score)); } else if (Z_TYPE_P(score) == IS_STRING) { type = is_numeric_string(Z_STRVAL_P(score), Z_STRLEN_P(score), &lval, &dval, 0); if (type == IS_LONG) { redis_cmd_append_sstr_long(dst, lval); } else if (type == IS_DOUBLE) { redis_cmd_append_sstr_dbl(dst, dval); } else if (zend_string_equals_literal_ci(Z_STR_P(score), "-inf") || zend_string_equals_literal_ci(Z_STR_P(score), "+inf") || zend_string_equals_literal_ci(Z_STR_P(score), "inf")) { redis_cmd_append_sstr_zstr(dst, Z_STR_P(score)); } } /* Success if we appended something */ if (dst->len > cmdlen) return SUCCESS; /* Nothing appended, failure */ php_error_docref(NULL, E_WARNING, "scores must be numeric or '-inf', 'inf', '+inf'"); return FAILURE; } int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_long limit = -1; HashTable *keys; zend_string *key; zval *zv; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ARRAY_HT(keys) Z_PARAM_OPTIONAL Z_PARAM_LONG(limit) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_hash_num_elements(keys) == 0) { php_error_docref(NULL, E_WARNING, "Must pass at least one key"); return FAILURE; } else if (ZEND_NUM_ARGS() == 2 && limit < 0) { php_error_docref(NULL, E_WARNING, "LIMIT cannot be negative"); return FAILURE; } redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(keys) + (limit > 0 ? 2 : 0), kw, strlen(kw)); redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(keys)); if (slot) *slot = -1; ZEND_HASH_FOREACH_VAL(keys, zv) { key = redis_key_prefix_zval(redis_sock, zv); if (slot) { if (*slot == -1) { *slot = cluster_hash_key_zstr(key); } else if (*slot != cluster_hash_key_zstr(key)) { php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot"); efree(cmdstr.c); zend_string_release(key); return FAILURE; } } redis_cmd_append_sstr_zstr(&cmdstr, key); zend_string_release(key); } ZEND_HASH_FOREACH_END(); if (limit > 0) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT"); redis_cmd_append_sstr_long(&cmdstr, limit); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *host = NULL; zend_long port = 6379; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL Z_PARAM_STR(host) Z_PARAM_LONG(port) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (port < 0 || port > UINT16_MAX) { php_error_docref(NULL, E_WARNING, "Invalid port %ld", (long)port); return FAILURE; } if (ZEND_NUM_ARGS() == 2) { *cmd_len = REDIS_SPPRINTF(cmd, kw, "Sd", host, (int)port); } else { *cmd_len = REDIS_SPPRINTF(cmd, kw, "ss", ZEND_STRL("NO"), ZEND_STRL("ONE")); } return SUCCESS; } int redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_keys, *z_weights = NULL, *z_opts = NULL, *z_ele; redisZcmdOptions opts = {0}; smart_string cmdstr = {0}; int numkeys, flags; short s2 = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a!a", &z_keys, &z_weights, &z_opts) == FAILURE) { return FAILURE; } if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) { return FAILURE; } if (z_weights && zend_hash_num_elements(Z_ARRVAL_P(z_weights)) != numkeys) { php_error_docref(NULL, E_WARNING, "WEIGHTS and keys array should be the same size!"); return FAILURE; } flags = redis_get_zcmd_flags(kw); redis_get_zcmd_options(&opts, z_opts, flags); redis_cmd_init_sstr(&cmdstr, 1 + numkeys + (z_weights ? 1 + numkeys : 0) + (opts.aggregate ? 2 : 0) + opts.withscores, kw, strlen(kw)); redis_cmd_append_sstr_long(&cmdstr, numkeys); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) { ZVAL_DEREF(z_ele); redis_cmd_append_sstr_key_zval(&cmdstr, z_ele, redis_sock, slot); if (slot) { if (s2 && s2 != *slot) { php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot"); efree(cmdstr.c); return FAILURE; } s2 = *slot; } } ZEND_HASH_FOREACH_END(); if (z_weights) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WEIGHTS"); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_weights), z_ele) { ZVAL_DEREF(z_ele); if (redis_cmd_append_sstr_score(&cmdstr, z_ele) == FAILURE) { efree(cmdstr.c); return FAILURE; } } ZEND_HASH_FOREACH_END(); } if (opts.aggregate) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AGGREGATE"); redis_cmd_append_sstr_zstr(&cmdstr, opts.aggregate); } if (opts.withscores) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); *ctx = PHPREDIS_CTX_PTR; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_string *dst = NULL; HashTable *keys = NULL; zend_ulong nkeys; short s2 = 0; zval *zkey; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(dst) Z_PARAM_ARRAY_HT(keys) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); nkeys = zend_hash_num_elements(keys); if (nkeys == 0) return FAILURE; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + nkeys, "ZDIFFSTORE"); redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); redis_cmd_append_sstr_long(&cmdstr, nkeys); ZEND_HASH_FOREACH_VAL(keys, zkey) { ZVAL_DEREF(zkey); redis_cmd_append_sstr_key_zval(&cmdstr, zkey, redis_sock, slot ? &s2 : NULL); if (slot && *slot != s2) { php_error_docref(NULL, E_WARNING, "All keys must hash to the same slot"); efree(cmdstr.c); return FAILURE; } } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* ZUNIONSTORE, ZINTERSTORE */ int redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { HashTable *keys = NULL, *weights = NULL; smart_string cmdstr = {0}; zend_string *dst = NULL; zend_string *agg = NULL; zend_ulong nkeys; zval *zv = NULL; short s2 = 0; ZEND_PARSE_PARAMETERS_START(2, 4) Z_PARAM_STR(dst) Z_PARAM_ARRAY_HT(keys) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(weights) Z_PARAM_STR_OR_NULL(agg) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); nkeys = zend_hash_num_elements(keys); if (nkeys == 0) return FAILURE; if (weights != NULL && zend_hash_num_elements(weights) != nkeys) { php_error_docref(NULL, E_WARNING, "WEIGHTS and keys array must be the same size!"); return FAILURE; } // AGGREGATE option if (agg != NULL && (!zend_string_equals_literal_ci(agg, "SUM") && !zend_string_equals_literal_ci(agg, "MIN") && !zend_string_equals_literal_ci(agg, "MAX"))) { php_error_docref(NULL, E_WARNING, "AGGREGATE option must be 'SUM', 'MIN', or 'MAX'"); return FAILURE; } redis_cmd_init_sstr(&cmdstr, 2 + nkeys + (weights ? 1 + nkeys : 0) + (agg ? 2 : 0), kw, strlen(kw)); redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); redis_cmd_append_sstr_int(&cmdstr, nkeys); ZEND_HASH_FOREACH_VAL(keys, zv) { ZVAL_DEREF(zv); redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot ? &s2 : NULL); if (slot && s2 != *slot) { php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot!"); efree(cmdstr.c); return FAILURE; } } ZEND_HASH_FOREACH_END(); if (weights) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WEIGHTS"); ZEND_HASH_FOREACH_VAL(weights, zv) { ZVAL_DEREF(zv); if (redis_cmd_append_sstr_score(&cmdstr, zv) == FAILURE) { efree(cmdstr.c); return FAILURE; } } ZEND_HASH_FOREACH_END(); } if (agg) { redis_cmd_append_sstr(&cmdstr, ZEND_STRL("AGGREGATE")); redis_cmd_append_sstr_zstr(&cmdstr, agg); } // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { HashTable *channels = NULL; smart_string cmdstr = {0}; zend_string *op, *pattern = NULL; zval *arg = NULL, *z_chan; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(arg) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_string_equals_literal_ci(op, "NUMPAT")) { *ctx = NULL; } else if (zend_string_equals_literal_ci(op, "CHANNELS") || zend_string_equals_literal_ci(op, "SHARDCHANNELS") ) { if (arg != NULL) { if (Z_TYPE_P(arg) != IS_STRING) { php_error_docref(NULL, E_WARNING, "Invalid patern value"); return FAILURE; } pattern = zval_get_string(arg); } *ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "NUMSUB") || zend_string_equals_literal_ci(op, "SHARDNUMSUB") ) { if (arg != NULL) { if (Z_TYPE_P(arg) != IS_ARRAY) { php_error_docref(NULL, E_WARNING, "Invalid channels value"); return FAILURE; } channels = Z_ARRVAL_P(arg); } *ctx = PHPREDIS_CTX_PTR + 1; } else { php_error_docref(NULL, E_WARNING, "Unknown PUBSUB operation '%s'", ZSTR_VAL(op)); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + !!pattern + (channels ? zend_hash_num_elements(channels) : 0), "PUBSUB"); redis_cmd_append_sstr_zstr(&cmdstr, op); if (pattern != NULL) { redis_cmd_append_sstr_zstr(&cmdstr, pattern); zend_string_release(pattern); } else if (channels != NULL) { ZEND_HASH_FOREACH_VAL(channels, z_chan) { redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot); } ZEND_HASH_FOREACH_END(); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE */ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_arr, *z_chan; HashTable *ht_chan; smart_string cmdstr = {0}; subscribeContext *sctx = ecalloc(1, sizeof(*sctx)); unsigned short shardslot = REDIS_CLUSTER_SLOTS; short s2; if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &z_arr, &sctx->cb.fci, &sctx->cb.fci_cache) == FAILURE) { efree(sctx); return FAILURE; } ht_chan = Z_ARRVAL_P(z_arr); sctx->kw = kw; sctx->argc = zend_hash_num_elements(ht_chan); if (sctx->argc == 0) { efree(sctx); return FAILURE; } if (strcasecmp(kw, "ssubscribe") == 0) { zend_hash_internal_pointer_reset(ht_chan); if ((z_chan = zend_hash_get_current_data(ht_chan)) == NULL) { php_error_docref(NULL, E_WARNING, "Internal Zend HashTable error"); efree(sctx); return FAILURE; } shardslot = cluster_hash_key_zval(z_chan); } // Start command construction redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); // Iterate over channels ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot ? &s2 : NULL); if (slot && (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot)) { php_error_docref(NULL, E_WARNING, "All shard channels needs to belong to a single slot"); smart_string_free(&cmdstr); efree(sctx); return FAILURE; } } ZEND_HASH_FOREACH_END(); // Push values out *cmd_len = cmdstr.len; *cmd = cmdstr.c; *ctx = (void*)sctx; if (shardslot != REDIS_CLUSTER_SLOTS) { if (slot) *slot = shardslot; } else { // Pick a slot at random CMD_RAND_SLOT(slot); } return SUCCESS; } /* UNSUBSCRIBE/PUNSUBSCRIBE/SUNSUBSCRIBE */ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; subscribeContext *sctx; HashTable *channels; zval *channel; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(channels) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_hash_num_elements(channels) == 0) return FAILURE; sctx = ecalloc(1, sizeof(*sctx)); sctx->kw = kw; sctx->argc = zend_hash_num_elements(channels); redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); ZEND_HASH_FOREACH_VAL(channels, channel) { redis_cmd_append_sstr_key_zval(&cmdstr, channel, redis_sock, slot); } ZEND_HASH_FOREACH_END(); *cmd_len = cmdstr.len; *cmd = cmdstr.c; *ctx = sctx; return SUCCESS; } /* ZRANGEBYLEX/ZREVRANGEBYLEX */ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *min, *max; size_t key_len, min_len, max_len; int argc = ZEND_NUM_ARGS(); zend_long offset, count; /* We need either 3 or 5 arguments for this to be valid */ if (argc != 3 && argc != 5) { php_error_docref(0, E_WARNING, "Must pass either 3 or 5 arguments"); return FAILURE; } if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, &max, &max_len, &offset, &count) == FAILURE) { return FAILURE; } /* min and max must start with '(' or '[', or be either '-' or '+' */ if (!validate_zlex_arg(min, min_len) || !validate_zlex_arg(max, max_len)) { php_error_docref(NULL, E_WARNING, "Min/Max args can be '-' or '+', or start with '[' or '('"); return FAILURE; } /* Construct command */ if (argc == 3) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, max, max_len); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssll", key, key_len, min, min_len, max, max_len, "LIMIT", 5, offset, count); } return SUCCESS; } /* ZLEXCOUNT/ZREMRANGEBYLEX */ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *min, *max; size_t key_len, min_len, max_len; /* Parse args */ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &min, &min_len, &max, &max_len) == FAILURE) { return FAILURE; } /* Quick sanity check on min/max */ if (!validate_zlex_arg(min, min_len) || !validate_zlex_arg(max, max_len)) { php_error_docref(NULL, E_WARNING, "Min/Max args can be '-' or '+', or start with '[' or '('"); return FAILURE; } /* Construct command */ *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, max, max_len); return SUCCESS; } /* EVAL and EVALSHA */ int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *lua; int argc = 0; zval *z_arr = NULL, *z_ele; HashTable *ht_arr; zend_long num_keys = 0; smart_string cmdstr = {0}; size_t lua_len; zend_string *zstr; short prevslot = -1; /* Parse args */ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &lua, &lua_len, &z_arr, &num_keys) == FAILURE) { return FAILURE; } /* Grab arg count */ if (z_arr != NULL) { ht_arr = Z_ARRVAL_P(z_arr); argc = zend_hash_num_elements(ht_arr); } /* EVAL[SHA] {script || sha1} {num keys} */ redis_cmd_init_sstr(&cmdstr, 2 + argc, kw, strlen(kw)); redis_cmd_append_sstr(&cmdstr, lua, lua_len); redis_cmd_append_sstr_long(&cmdstr, num_keys); // Iterate over our args if we have any if (argc > 0) { ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { zstr = zval_get_string(z_ele); /* If we're still on a key, prefix it check slot */ if (num_keys-- > 0) { redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot); /* If we have been passed a slot, all keys must match */ if (slot) { if (prevslot != -1 && prevslot != *slot) { zend_string_release(zstr); php_error_docref(0, E_WARNING, "All keys do not map to the same slot"); return FAILURE; } prevslot = *slot; } } else { redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); } zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); } else { /* Any slot will do */ CMD_RAND_SLOT(slot); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* Commands that take a key followed by a variable list of serializable * values (RPUSH, LPUSH, SADD, SREM, etc...) */ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_args; smart_string cmdstr = {0}; size_t i; int argc = ZEND_NUM_ARGS(); // We at least need a key and one value if (argc < 2) { zend_wrong_param_count(); return FAILURE; } // Make sure we at least have a key, and we can get other args z_args = emalloc(argc * sizeof(zval)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } /* Initialize our command */ redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); /* Append key */ zend_string *zstr = zval_get_string(&z_args[0]); redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot); zend_string_release(zstr); /* Add members */ for (i = 1; i < argc; i++ ){ redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock); } // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; // Cleanup arg array efree(z_args); // Success! return SUCCESS; } /* Commands that take a key and then an array of values */ static int gen_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, zend_bool pack_values, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; HashTable *values = NULL; zend_string *key = NULL; zval *zv; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(values) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_hash_num_elements(values) == 0) return FAILURE; redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(values), kw, strlen(kw)); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); ZEND_HASH_FOREACH_VAL(values, zv) { redis_cmd_append_sstr_zval(&cmdstr, zv, pack_values ? redis_sock : NULL); } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, 1, cmd, cmd_len, slot, ctx); } int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, 0, cmd, cmd_len, slot, ctx); } /* Generic function that takes one or more non-serialized arguments */ static int gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, uint32_t min_argc, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zval *argv = NULL; int argc = 0; uint32_t i; ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); for (i = 0; i < argc; i++) { redis_cmd_append_sstr_zval(&cmdstr, &argv[i], NULL); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; HashTable *kvals = NULL; zend_string *key; zend_ulong idx; zval *zv; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(kvals) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_hash_num_elements(kvals) == 0) return FAILURE; redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(kvals) * 2, kw, strlen(kw)); ZEND_HASH_FOREACH_KEY_VAL(kvals, idx, key, zv) { ZVAL_DEREF(zv); if (key) { redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, NULL); } else { redis_cmd_append_sstr_key_long(&cmdstr, idx, redis_sock, NULL); } redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* Generic function that takes a variable number of keys, with an optional * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, int kw_len, zend_bool has_timeout, char **cmd, int *cmd_len, short *slot) { zval *argv = NULL, ztimeout = {0}, *zv; smart_string cmdstr = {0}; uint32_t min_argc; short kslot = -1; int single_array; int argc = 0; min_argc = has_timeout ? 2 : 1; ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); single_array = argc == min_argc && Z_TYPE(argv[0]) == IS_ARRAY; if (has_timeout) { if (single_array) ZVAL_COPY_VALUE(&ztimeout, &argv[1]); else ZVAL_COPY_VALUE(&ztimeout, &argv[argc - 1]); if (Z_TYPE(ztimeout) != IS_LONG && Z_TYPE(ztimeout) != IS_DOUBLE) { php_error_docref(NULL, E_WARNING, "Timeout must be a long or double"); return FAILURE; } } // If we're running a single array, rework args if (single_array) { /* Need at least one argument */ argc = zend_hash_num_elements(Z_ARRVAL(argv[0])); if (argc == 0) return FAILURE; if (has_timeout) argc++; } // Begin construction of our command redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); if (single_array) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL(argv[0]), zv) { redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); if (slot) { if (kslot != -1 && *slot != kslot) goto cross_slot; kslot = *slot; } } ZEND_HASH_FOREACH_END(); } else { uint32_t i; for(i = 0; i < argc - !!has_timeout; i++) { redis_cmd_append_sstr_key_zval(&cmdstr, &argv[i], redis_sock, slot); if (slot) { if (kslot != -1 && *slot != kslot) goto cross_slot; kslot = *slot; } } } if (Z_TYPE(ztimeout) == IS_DOUBLE) { redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL(ztimeout)); } else if (Z_TYPE(ztimeout) == IS_LONG) { redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(ztimeout)); } // Push out parameters *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; cross_slot: efree(cmdstr.c); php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); return FAILURE; } int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { int argc, blocking, is_zmpop; smart_string cmdstr = {0}; zend_string *from = NULL; HashTable *keys = NULL; double timeout = 0.0; zend_long count = 1; short slot2 = -1; zval *zv; /* Sanity check on our keyword */ ZEND_ASSERT(kw != NULL && *kw != '\0' && *(kw+1) != '\0'); blocking = tolower(*kw) == 'b'; is_zmpop = tolower(kw[blocking]) == 'z'; ZEND_PARSE_PARAMETERS_START(2 + blocking, 3 + blocking) { if (blocking) { Z_PARAM_DOUBLE(timeout) } Z_PARAM_ARRAY_HT(keys) Z_PARAM_STR(from); Z_PARAM_OPTIONAL Z_PARAM_LONG(count); } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_hash_num_elements(keys) == 0) { php_error_docref(NULL, E_WARNING, "Must pass at least one key"); return FAILURE; } else if (count < 1) { php_error_docref(NULL, E_WARNING, "Count must be > 0"); return FAILURE; } else if (!is_zmpop && !(zend_string_equals_literal_ci(from, "LEFT") || zend_string_equals_literal_ci(from, "RIGHT"))) { php_error_docref(NULL, E_WARNING, "from must be either 'LEFT' or 'RIGHT'"); return FAILURE; } else if (is_zmpop && !(zend_string_equals_literal_ci(from, "MIN") || zend_string_equals_literal_ci(from, "MAX"))) { php_error_docref(NULL, E_WARNING, "from must be either 'MIN' or 'MAX'"); return FAILURE; } argc = 2 + !!blocking + zend_hash_num_elements(keys) + (count != 1 ? 2 : 0); redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); if (blocking) redis_cmd_append_sstr_dbl(&cmdstr, timeout); redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(keys)); if (slot) *slot = -1; ZEND_HASH_FOREACH_VAL(keys, zv) { redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); if (slot) { if (slot2 != -1 && *slot != slot2) { php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot"); efree(cmdstr.c); return FAILURE; } slot2 = *slot; } } ZEND_HASH_FOREACH_END(); redis_cmd_append_sstr_zstr(&cmdstr, from); if (count != 1) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, count); } *ctx = is_zmpop ? PHPREDIS_CTX_PTR : NULL; *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_vararg_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 0, "INFO", cmd, cmd_len, slot, ctx); } int redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_vararg_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, "SCRIPT", cmd, cmd_len, slot, ctx); } /* Generic handling of every blocking pop command (BLPOP, BZPOP[MIN/MAX], etc */ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, strlen(kw), 1, cmd, cmd_len, slot); } /* * Commands with specific signatures or that need unique functions because they * have specific processing (argument validation, etc) that make them unique */ int redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; smart_string cmdstr = {0}; zend_long count = 0; // Make sure the function is being called correctly if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { return FAILURE; } redis_cmd_init_sstr(&cmdstr, 1 + (count > 0), kw, strlen(kw)); redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); if (count > 0) { redis_cmd_append_sstr_long(&cmdstr, (long)count); *ctx = PHPREDIS_CTX_PTR; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_string *op, *zstr; zval *z_args = NULL; int argc = 0, i; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_VARIADIC('*', z_args, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_string_equals_literal_ci(op, "CAT") || zend_string_equals_literal_ci(op, "LIST") || zend_string_equals_literal_ci(op, "USERS") ) { *ctx = NULL; } else if (zend_string_equals_literal_ci(op, "LOAD") || zend_string_equals_literal_ci(op, "SAVE") ) { *ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "GENPASS") || zend_string_equals_literal_ci(op, "WHOAMI") ) { *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "SETUSER")) { if (argc < 1) { php_error_docref(NULL, E_WARNING, "ACL SETUSER requires at least one argument"); return FAILURE; } *ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "DELUSER")) { if (argc < 1) { php_error_docref(NULL, E_WARNING, "ACL DELUSER requires at least one argument"); return FAILURE; } *ctx = PHPREDIS_CTX_PTR + 2; } else if (zend_string_equals_literal_ci(op, "GETUSER")) { if (argc < 1) { php_error_docref(NULL, E_WARNING, "ACL GETUSER requires at least one argument"); return FAILURE; } *ctx = PHPREDIS_CTX_PTR + 3; } else if (zend_string_equals_literal_ci(op, "DRYRUN")) { if (argc < 2) { php_error_docref(NULL, E_WARNING, "ACL DRYRUN requires at least two arguments"); return FAILURE; } *ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "LOG")) { if (argc > 0 && Z_TYPE(z_args[0]) == IS_STRING && ZVAL_STRICMP_STATIC(&z_args[0], "RESET")) { *ctx = PHPREDIS_CTX_PTR; } else { *ctx = PHPREDIS_CTX_PTR + 4; } } else { php_error_docref(NULL, E_WARNING, "Unknown ACL operation '%s'", ZSTR_VAL(op)); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + argc, "ACL"); redis_cmd_append_sstr_zstr(&cmdstr, op); for (i = 0; i < argc; ++i) { zstr = zval_get_string(&z_args[i]); redis_cmd_append_sstr_zstr(&cmdstr, zstr); zend_string_release(zstr); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* Attempt to pull a long expiry from a zval. We're more restrictave than zval_get_long * because that function will return integers from things like open file descriptors * which should simply fail as a TTL */ static int redis_try_get_expiry(zval *zv, zend_long *lval) { double dval; /* Success on an actual long or double */ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) { *lval = zval_get_long(zv); return SUCCESS; } /* Automatically fail if we're not a string */ if (Z_TYPE_P(zv) != IS_STRING) return FAILURE; /* Attempt to get a long from the string */ switch (is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), lval, &dval, 0)) { case IS_DOUBLE: *lval = dval; REDIS_FALLTHROUGH; case IS_LONG: return SUCCESS; default: return FAILURE; } } /* SET */ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zval *z_value, *z_opts=NULL; char *key = NULL, *exp_type = NULL, *set_type = NULL; long exp_set = 0, keep_ttl = 0; zend_long expire = -1; zend_bool get = 0; size_t key_len; // Make sure the function is being called correctly if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &key, &key_len, &z_value, &z_opts) == FAILURE) { return FAILURE; } // Check for an options array if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { HashTable *kt = Z_ARRVAL_P(z_opts); zend_string *zkey; zval *v; /* Iterate our option array */ ZEND_HASH_FOREACH_STR_KEY_VAL(kt, zkey, v) { ZVAL_DEREF(v); /* Detect PX or EX argument and validate timeout */ if (zkey && (zend_string_equals_literal_ci(zkey, "EX") || zend_string_equals_literal_ci(zkey, "PX") || zend_string_equals_literal_ci(zkey, "EXAT") || zend_string_equals_literal_ci(zkey, "PXAT")) ) { exp_set = 1; /* Set expire type */ exp_type = ZSTR_VAL(zkey); /* Try to extract timeout */ if (Z_TYPE_P(v) == IS_LONG) { expire = Z_LVAL_P(v); } else if (Z_TYPE_P(v) == IS_STRING) { expire = atol(Z_STRVAL_P(v)); } } else if (Z_TYPE_P(v) == IS_STRING) { if (zend_string_equals_literal_ci(Z_STR_P(v), "KEEPTTL")) { keep_ttl = 1; } else if (zend_string_equals_literal_ci(Z_STR_P(v), "GET")) { get = 1; } else if (zend_string_equals_literal_ci(Z_STR_P(v), "NX") || zend_string_equals_literal_ci(Z_STR_P(v), "XX")) { set_type = Z_STRVAL_P(v); } } } ZEND_HASH_FOREACH_END(); } else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) { if (redis_try_get_expiry(z_opts, &expire) == FAILURE) { php_error_docref(NULL, E_WARNING, "Expire must be a long, double, or a numeric string"); return FAILURE; } exp_set = 1; } /* Protect the user from syntax errors but give them some info about what's wrong */ if (exp_set && expire < 1) { php_error_docref(NULL, E_WARNING, "EXPIRE can't be < 1"); return FAILURE; } else if (exp_type && keep_ttl) { php_error_docref(NULL, E_WARNING, "KEEPTTL can't be combined with EX or PX option"); return FAILURE; } /* Backward compatibility: If we are passed no options except an EXPIRE ttl, we * actually execute a SETEX command */ if (expire > 0 && !exp_type && !set_type && !keep_ttl) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, z_value); return SUCCESS; } /* Calculate argc based on options set */ int argc = 2 + (exp_type ? 2 : 0) + (set_type != NULL) + (keep_ttl != 0) + get; /* Initial SET */ redis_cmd_init_sstr(&cmdstr, argc, "SET", 3); redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); redis_cmd_append_sstr_zval(&cmdstr, z_value, redis_sock); if (exp_type) { redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type)); redis_cmd_append_sstr_long(&cmdstr, (long)expire); } if (set_type) redis_cmd_append_sstr(&cmdstr, set_type, strlen(set_type)); if (keep_ttl) redis_cmd_append_sstr(&cmdstr, "KEEPTTL", 7); if (get) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GET"); *ctx = PHPREDIS_CTX_PTR; } /* Push command and length to the caller */ *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* MGET */ int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; HashTable *keys = NULL; zval *zkey; /* RedisCluster has a custom MGET implementation */ ZEND_ASSERT(slot == NULL); ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(keys) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_hash_num_elements(keys) == 0) return FAILURE; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, zend_hash_num_elements(keys), "MGET"); ZEND_HASH_FOREACH_VAL(keys, zkey) { ZVAL_DEREF(zkey); redis_cmd_append_sstr_key_zval(&cmdstr, zkey, redis_sock, slot); } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; char *key, *exp_type = NULL; zval *z_opts = NULL, *z_ele; zend_long expire = -1; zend_bool persist = 0; zend_string *zkey; size_t key_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, &z_opts) == FAILURE) { return FAILURE; } if (z_opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (ZSTR_STRICMP_STATIC(zkey, "EX") || ZSTR_STRICMP_STATIC(zkey, "PX") || ZSTR_STRICMP_STATIC(zkey, "EXAT") || ZSTR_STRICMP_STATIC(zkey, "PXAT") ) { exp_type = ZSTR_VAL(zkey); expire = zval_get_long(z_ele); persist = 0; } else if (ZSTR_STRICMP_STATIC(zkey, "PERSIST")) { persist = zval_is_true(z_ele); exp_type = NULL; } } } ZEND_HASH_FOREACH_END(); } if (exp_type != NULL && expire < 1) { php_error_docref(NULL, E_WARNING, "EXPIRE can't be < 1"); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (exp_type ? 2 : persist), "GETEX"); redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); if (exp_type != NULL) { redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type)); redis_cmd_append_sstr_long(&cmdstr, expire); } else if (persist) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "PERSIST"); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* BRPOPLPUSH */ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *src = NULL, *dst = NULL; double timeout = 0; ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_STR(src) Z_PARAM_STR(dst) Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); src = redis_key_prefix_zstr(redis_sock, src); dst = redis_key_prefix_zstr(redis_sock, dst); if (slot && (*slot = cluster_hash_key_zstr(src)) != cluster_hash_key_zstr(dst)) { php_error_docref(NULL, E_WARNING, "Keys must hash to the same slot"); zend_string_release(src); zend_string_release(dst); return FAILURE; } /* Consistency with Redis. If timeout < 0 use RPOPLPUSH */ if (timeout < 0) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "RPOPLPUSH", "SS", src, dst); } else if (fabs(timeout - (long)timeout) < .0001) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BRPOPLPUSH", "SSd", src, dst, (long)timeout); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BRPOPLPUSH", "SSf", src, dst, timeout); } zend_string_release(src); zend_string_release(dst); return SUCCESS; } /* To maintain backward compatibility with earlier versions of phpredis, we * allow for an optional "increment by" argument for INCR and DECR even though * that's not how Redis proper works */ #define TYPE_INCR 0 #define TYPE_DECR 1 /* Handle INCR(BY) and DECR(BY) depending on optional increment value */ static int redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; zend_long val = 1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &val) == FAILURE) { return FAILURE; } /* If our value is 1 we use INCR/DECR. For other values, treat the call as * an INCRBY or DECRBY call */ if (type == TYPE_INCR) { if (val == 1) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "INCR", "k", key, key_len); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "INCRBY", "kl", key, key_len, val); } } else { if (val == 1) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "DECR", "k", key, key_len); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "DECRBY", "kl", key, key_len, val); } } /* Success */ return SUCCESS; } /* INCR */ int redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_INCR, redis_sock, cmd, cmd_len, slot, ctx); } /* DECR */ int redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_DECR, redis_sock, cmd, cmd_len, slot, ctx); } /* HINCRBY */ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; size_t key_len, mem_len; zend_long byval; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { return FAILURE; } // Construct command *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HINCRBY", "ksl", key, key_len, mem, mem_len, byval); // Success return SUCCESS; } /* HINCRBYFLOAT */ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; size_t key_len, mem_len; double byval; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssd", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { return FAILURE; } // Construct command *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HINCRBYFLOAT", "ksf", key, key_len, mem, mem_len, byval); // Success return SUCCESS; } /* HMGET */ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *field = NULL, *zctx = NULL; smart_string cmdstr = {0}; HashTable *fields = NULL; zend_string *key = NULL; zend_ulong valid = 0, i; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(fields) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_hash_num_elements(fields) == 0) return FAILURE; zctx = ecalloc(1 + zend_hash_num_elements(fields), sizeof(*zctx)); ZEND_HASH_FOREACH_VAL(fields, field) { ZVAL_DEREF(field); if (!((Z_TYPE_P(field) == IS_STRING && Z_STRLEN_P(field) > 0) || Z_TYPE_P(field) == IS_LONG)) continue; ZVAL_COPY(&zctx[valid++], field); } ZEND_HASH_FOREACH_END(); if (valid == 0) { efree(zctx); return FAILURE; } ZVAL_NULL(&zctx[valid]); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + valid, "HMGET"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); for (i = 0; i < valid; i++) { redis_cmd_append_sstr_zval(&cmdstr, &zctx[i], NULL); } // Push out command, length, and key context *cmd = cmdstr.c; *cmd_len = cmdstr.len; *ctx = zctx; return SUCCESS; } /* HMSET */ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_string *key = NULL; HashTable *ht = NULL; uint32_t fields; zend_ulong idx; zval *zv; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(ht) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); fields = zend_hash_num_elements(ht); if (fields == 0) return FAILURE; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (2 * fields), "HMSET"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zv) { if (key) { redis_cmd_append_sstr_zstr(&cmdstr, key); } else { redis_cmd_append_sstr_long(&cmdstr, idx); } redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); } ZEND_HASH_FOREACH_END(); *cmd_len = cmdstr.len; *cmd = cmdstr.c; return SUCCESS; } /* HSTRLEN */ int redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *field; size_t key_len, field_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &field, &field_len) == FAILURE ) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HSTRLEN", "ks", key, key_len, field, field_len); return SUCCESS; } static void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) { zend_string *key; zval *zv; ZEND_ASSERT(dst != NULL); memset(dst, 0, sizeof(*dst)); if (ht == NULL) return; ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) { if (key) { if (zend_string_equals_literal_ci(key, "LEN")) { dst->idx = 0; dst->len = zval_is_true(zv); } else if (zend_string_equals_literal_ci(key, "IDX")) { dst->len = 0; dst->idx = zval_is_true(zv); } else if (zend_string_equals_literal_ci(key, "MINMATCHLEN")) { dst->minmatchlen = zval_get_long(zv); } else if (zend_string_equals_literal_ci(key, "WITHMATCHLEN")) { dst->withmatchlen = zval_is_true(zv); } else { php_error_docref(NULL, E_WARNING, "Unknown LCS option '%s'", ZSTR_VAL(key)); } } else if (Z_TYPE_P(zv) == IS_STRING) { if (zend_string_equals_literal_ci(Z_STR_P(zv), "LEN")) { dst->idx = 0; dst->len = 1; } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "IDX")) { dst->idx = 1; dst->len = 0; } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "WITHMATCHLEN")) { dst->withmatchlen = 1; } } } ZEND_HASH_FOREACH_END(); } /* LCS */ int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *key1 = NULL, *key2 = NULL; smart_string cmdstr = {0}; HashTable *ht = NULL; redisLcsOptions opt; int argc; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(key1) Z_PARAM_STR(key2) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(ht) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); key1 = redis_key_prefix_zstr(redis_sock, key1); key2 = redis_key_prefix_zstr(redis_sock, key2); if (slot) { *slot = cluster_hash_key_zstr(key1); if (*slot != cluster_hash_key_zstr(key2)) { php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!"); zend_string_release(key1); zend_string_release(key2); return FAILURE; } } redis_get_lcs_options(&opt, ht); argc = 2 + !!opt.idx + !!opt.len + !!opt.withmatchlen + (opt.minmatchlen ? 2 : 0); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "LCS"); redis_cmd_append_sstr_zstr(&cmdstr, key1); redis_cmd_append_sstr_zstr(&cmdstr, key2); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.idx, "IDX"); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.len, "LEN"); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withmatchlen, "WITHMATCHLEN"); if (opt.minmatchlen) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MINMATCHLEN"); redis_cmd_append_sstr_long(&cmdstr, opt.minmatchlen); } zend_string_release(key1); zend_string_release(key2); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; smart_string cmdstr = {0}; zend_string *op = NULL; zend_long arg = 0; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_LONG(arg) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_string_equals_literal_ci(op, "GET")) { mode = SLOWLOG_GET; } else if (zend_string_equals_literal_ci(op, "LEN")) { mode = SLOWLOG_LEN; } else if (zend_string_equals_literal_ci(op, "RESET")) { mode = SLOWLOG_RESET; } else { php_error_docref(NULL, E_WARNING, "Unknown SLOWLOG operation '%s'", ZSTR_VAL(op)); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2), "SLOWLOG"); redis_cmd_append_sstr_zstr(&cmdstr, op); if (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) redis_cmd_append_sstr_long(&cmdstr, arg); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } void redis_get_restore_options(redisRestoreOptions *dst, HashTable *ht) { zend_string *key; zend_long lval; zval *zv; ZEND_ASSERT(dst != NULL); memset(dst, 0, sizeof(*dst)); dst->idletime = dst->freq = -1; if (ht == NULL) return; ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) { ZVAL_DEREF(zv); if (key) { if (zend_string_equals_literal_ci(key, "IDLETIME")) { lval = zval_get_long(zv); if (lval < 0) { php_error_docref(NULL, E_WARNING, "IDLETIME must be >= 0"); } else { dst->idletime = lval; dst->freq = -1; } } else if (zend_string_equals_literal_ci(key, "FREQ")) { lval = zval_get_long(zv); if (lval < 0 || lval > 255) { php_error_docref(NULL, E_WARNING, "FREQ must be >= 0 and <= 255"); } else { dst->freq = lval; dst->idletime = -1; } } else { php_error_docref(NULL, E_WARNING, "Unknown RESTORE option '%s'", ZSTR_VAL(key)); } } else if (Z_TYPE_P(zv) == IS_STRING) { if (zend_string_equals_literal_ci(Z_STR_P(zv), "REPLACE")) { dst->replace = 1; } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "ABSTTL")) { dst->absttl = 1; } else { php_error_docref(NULL, E_WARNING, "Unknown RESTORE option '%s'", Z_STRVAL_P(zv)); } } } ZEND_HASH_FOREACH_END(); } /* RESTORE */ int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *key, *value = NULL; smart_string cmdstr = {0}; HashTable *options = NULL; redisRestoreOptions opt; zend_long timeout = 0; int argc; ZEND_PARSE_PARAMETERS_START(3, 4) { Z_PARAM_STR(key) Z_PARAM_LONG(timeout) Z_PARAM_STR(value) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(options) } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); redis_get_restore_options(&opt, options); argc = 3 + (opt.idletime>-1?2:0) + (opt.freq>-1?2:0) + !!opt.absttl + !!opt.replace; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "RESTORE"); redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); redis_cmd_append_sstr_long(&cmdstr, timeout); redis_cmd_append_sstr_zstr(&cmdstr, value); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.replace, "REPLACE"); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.absttl, "ABSTTL"); if (opt.idletime > -1) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IDLETIME"); redis_cmd_append_sstr_long(&cmdstr, opt.idletime); } if (opt.freq > -1) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FREQ"); redis_cmd_append_sstr_long(&cmdstr, opt.freq); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* BITPOS key bit [start [end [BYTE | BIT]]] */ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_long start = 0, end = -1; zend_bool bit = 0, bybit = 0; smart_string cmdstr = {0}; zend_string *key = NULL; ZEND_PARSE_PARAMETERS_START(2, 5) Z_PARAM_STR(key) Z_PARAM_BOOL(bit) Z_PARAM_OPTIONAL Z_PARAM_LONG(start) Z_PARAM_LONG(end) Z_PARAM_BOOL(bybit) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (ZEND_NUM_ARGS() > 2 ? 2 : 0) + !!bybit, "BITPOS"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); redis_cmd_append_sstr_long(&cmdstr, bit); /* Start and length if we were passed either */ if (ZEND_NUM_ARGS() > 2) { redis_cmd_append_sstr_long(&cmdstr, start); redis_cmd_append_sstr_long(&cmdstr, end); } /* Finally, BIT or BYTE if we were passed that argument */ REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, !!bybit, "BIT"); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* BITOP */ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_args; int i, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; short s2; // Allocate space for args, parse them as an array z_args = emalloc(argc * sizeof(zval)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 3 || Z_TYPE(z_args[0]) != IS_STRING) { efree(z_args); return FAILURE; } // If we were passed a slot pointer, init to a sentinel value if (slot) *slot = -1; // Initialize command construction, add our operation argument redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("BITOP")); redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); // Now iterate over our keys argument for (i = 1; i < argc; i++) { // Append the key redis_cmd_append_sstr_key_zval(&cmdstr, &z_args[i], redis_sock, slot ? &s2 : NULL); // Verify slot if this is a Cluster request if (slot) { if (*slot != -1 && s2 != *slot) { php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!"); efree(z_args); efree(cmdstr.c); return FAILURE; } *slot = s2; } } // Free our argument array efree(z_args); // Push out variables *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* BITCOUNT */ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; zend_long start = 0, end = -1; zend_bool isbit = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|llb", &key, &key_len, &start, &end, &isbit) == FAILURE) { return FAILURE; } if (isbit) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITCOUNT", "kdds", key, key_len, (int)start, (int)end, "BIT", 3); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITCOUNT", "kdd", key, key_len, (int)start, (int)end); } return SUCCESS; } /* PFADD and PFMERGE are the same except that in one case we serialize, * and in the other case we key prefix */ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, int kw_len, int is_keys, char **cmd, int *cmd_len, short *slot) { smart_string cmdstr = {0}; zend_string *key = NULL; HashTable *ht = NULL; zval *z_ele; int argc=1; short s2; // Parse arguments ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(ht) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); argc += zend_hash_num_elements(ht); // We need at least two arguments if (argc < 2) { return FAILURE; } redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); // Append our array of keys or serialized values */ ZEND_HASH_FOREACH_VAL(ht, z_ele) { if (is_keys) { redis_cmd_append_sstr_key_zval(&cmdstr, z_ele, redis_sock, slot ? &s2 : NULL); if (slot && *slot != s2) { php_error_docref(0, E_WARNING, "All keys must hash to the same slot!"); return FAILURE; } } else { redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); } } ZEND_HASH_FOREACH_END(); // Push output arguments *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* PFADD */ int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, ZEND_STRL("PFADD"), 0, cmd, cmd_len, slot); } /* PFMERGE */ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, ZEND_STRL("PFMERGE"), 1, cmd, cmd_len, slot); } /* PFCOUNT */ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zval *zarg = NULL, *zv; short slot2 = -1; uint32_t keys; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ZVAL(zarg) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (Z_TYPE_P(zarg) == IS_STRING) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "PFCOUNT"); redis_cmd_append_sstr_key_zstr(&cmdstr, Z_STR_P(zarg), redis_sock, slot); } else if (Z_TYPE_P(zarg) == IS_ARRAY) { keys = zend_hash_num_elements(Z_ARRVAL_P(zarg)); if (keys == 0) return FAILURE; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, keys, "PFCOUNT"); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zarg), zv) { redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); if (slot) { if (slot2 != -1 && slot2 != *slot) goto cross_slot; slot2 = *slot; } } ZEND_HASH_FOREACH_END(); } else { php_error_docref(NULL, E_WARNING, "Argument must be either an array or a string"); return FAILURE; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; cross_slot: php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); efree(cmdstr.c); return FAILURE; } int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *user = NULL, *pass = NULL; zval *ztest; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z!", &ztest) == FAILURE || redis_extract_auth_info(ztest, &user, &pass) == FAILURE) { return FAILURE; } /* Construct either AUTH or AUTH */ if (user && pass) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "SS", user, pass); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "S", pass); } redis_sock_set_auth(redis_sock, user, pass); if (user) zend_string_release(user); if (pass) zend_string_release(pass); return SUCCESS; } /* SETBIT */ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; zend_long offset; zend_bool val; if (zend_parse_parameters(ZEND_NUM_ARGS(), "slb", &key, &key_len, &offset, &val) == FAILURE) { return FAILURE; } // Validate our offset if (offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) { php_error_docref(0, E_WARNING, "Invalid OFFSET for bitop command (must be between 0-2^32-1)"); return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETBIT", "kld", key, key_len, offset, (int)val); return SUCCESS; } int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *src = NULL, *dst = NULL, *from = NULL, *to = NULL; smart_string cmdstr = {0}; double timeout = 0.0; short slot2 = 0; int blocking; blocking = toupper(*kw) == 'B'; ZEND_PARSE_PARAMETERS_START(4 + !!blocking, 4 + !!blocking) Z_PARAM_STR(src) Z_PARAM_STR(dst) Z_PARAM_STR(from) Z_PARAM_STR(to) if (blocking) { Z_PARAM_DOUBLE(timeout) } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (!zend_string_equals_literal_ci(from, "LEFT") && !zend_string_equals_literal_ci(from, "RIGHT")) { php_error_docref(NULL, E_WARNING, "Wherefrom argument must be 'LEFT' or 'RIGHT'"); return FAILURE; } else if (!zend_string_equals_literal_ci(to, "LEFT") && !zend_string_equals_literal_ci(to, "RIGHT")) { php_error_docref(NULL, E_WARNING, "Whereto argument must be 'LEFT' or 'RIGHT'"); return FAILURE; } redis_cmd_init_sstr(&cmdstr, 4 + !!blocking, kw, strlen(kw)); redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); /* Protect the user from CROSSLOT errors */ if (slot && slot2 != *slot) { php_error_docref(NULL, E_WARNING, "Both keys must hash to the same slot!"); efree(cmdstr.c); return FAILURE; } redis_cmd_append_sstr_zstr(&cmdstr, from); redis_cmd_append_sstr_zstr(&cmdstr, to); if (blocking) redis_cmd_append_sstr_dbl(&cmdstr, timeout); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* LINSERT */ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *pos; size_t key_len, pos_len; zval *z_val, *z_pivot; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszz", &key, &key_len, &pos, &pos_len, &z_pivot, &z_val) == FAILURE) { return FAILURE; } // Validate position if (strcasecmp(pos, "after") && strcasecmp(pos, "before")) { php_error_docref(NULL, E_WARNING, "Position must be either 'BEFORE' or 'AFTER'"); return FAILURE; } /* Construct command */ *cmd_len = REDIS_CMD_SPPRINTF(cmd, "LINSERT", "ksvv", key, key_len, pos, pos_len, z_pivot, z_val); // Success return SUCCESS; } /* LREM */ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; zend_long count = 0; zval *z_val; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|l", &key, &key_len, &z_val, &count) == FAILURE) { return FAILURE; } /* Construct command */ *cmd_len = REDIS_CMD_SPPRINTF(cmd, "LREM", "kdv", key, key_len, count, z_val); // Success! return SUCCESS; } int redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; int argc = 2; size_t key_len; smart_string cmdstr = {0}; zend_bool withrank = 0; zend_long rank = 0, count = -1, maxlen = -1; zend_string *zkey; zval *z_val, *z_ele, *z_opts = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|a", &key, &key_len, &z_val, &z_opts) == FAILURE) { return FAILURE; } if (z_opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "count")) { count = zval_get_long(z_ele); } else if (zend_string_equals_literal_ci(zkey, "maxlen")) { maxlen = zval_get_long(z_ele); } else if (zend_string_equals_literal_ci(zkey, "rank")) { rank = zval_get_long(z_ele); withrank = 1; } } } ZEND_HASH_FOREACH_END(); } argc += (withrank ? 2 : 0) + (count >= 0 ? 2 : 0) + (maxlen >= 0 ? 2 : 0); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "LPOS"); redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); redis_cmd_append_sstr_zval(&cmdstr, z_val, redis_sock); if (withrank) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "RANK"); redis_cmd_append_sstr_long(&cmdstr, rank); } if (count >= 0) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, count); *ctx = PHPREDIS_CTX_PTR; } if (maxlen >= 0) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN"); redis_cmd_append_sstr_long(&cmdstr, maxlen); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *src = NULL, *dst = NULL; smart_string cmdstr = {0}; zval *zv = NULL; short slot2; ZEND_PARSE_PARAMETERS_START(3, 3) { Z_PARAM_STR(src) Z_PARAM_STR(dst) Z_PARAM_ZVAL(zv) } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "SMOVE"); redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); if (slot && *slot != slot2) { php_error_docref(0, E_WARNING, "Source and destination keys don't hash to the same slot!"); efree(cmdstr.c); return FAILURE; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* HSET */ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { int i, argc; smart_string cmdstr = {0}; zend_string *zkey; zval *z_args, *z_ele; if ((argc = ZEND_NUM_ARGS()) < 2) { return FAILURE; } z_args = ecalloc(argc, sizeof(*z_args)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } if (argc == 2) { if (Z_TYPE(z_args[1]) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL(z_args[1])) == 0) { efree(z_args); return FAILURE; } /* Initialize our command */ redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL(z_args[1])), ZEND_STRL("HSET")); /* Append key */ zkey = zval_get_string(&z_args[0]); redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, slot); zend_string_release(zkey); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); } } ZEND_HASH_FOREACH_END(); } else { if (argc % 2 == 0) { efree(z_args); return FAILURE; } /* Initialize our command */ redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HSET")); /* Append key */ zkey = zval_get_string(&z_args[0]); redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, slot); zend_string_release(zkey); for (i = 1; i < argc; ++i) { if (i % 2) { zkey = zval_get_string(&z_args[i]); redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); zend_string_release(zkey); } else { redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock); } } } // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; // Cleanup arg array efree(z_args); return SUCCESS; } /* HSETNX */ int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; size_t key_len, mem_len; zval *z_val; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, &mem, &mem_len, &z_val) == FAILURE) { return FAILURE; } /* Construct command */ *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HSETNX", "ksv", key, key_len, mem, mem_len, z_val); // Success return SUCCESS; } int redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; int count = 0; size_t key_len; smart_string cmdstr = {0}; zend_bool withvalues = 0; zval *z_opts = NULL, *z_ele; zend_string *zkey; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, &z_opts) == FAILURE) { return FAILURE; } if (z_opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "count")) { count = zval_get_long(z_ele); } else if (zend_string_equals_literal_ci(zkey, "withvalues")) { withvalues = zval_is_true(z_ele); } } } ZEND_HASH_FOREACH_END(); } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (count != 0) + withvalues, "HRANDFIELD"); redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); if (count != 0) { redis_cmd_append_sstr_long(&cmdstr, count); *ctx = PHPREDIS_CTX_PTR; } if (withvalues) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHVALUES"); *ctx = PHPREDIS_CTX_PTR + 1; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_long db = 0; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(db) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (db < 0 || db > INT_MAX) return FAILURE; *ctx = (void*)(uintptr_t)db; *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SELECT", "d", db); return SUCCESS; } /* SRANDMEMBER */ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { uint32_t argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; zend_string *key = NULL; zend_long count = 0; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(key) Z_PARAM_OPTIONAL Z_PARAM_LONG(count) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, ZEND_NUM_ARGS(), "SRANDMEMBER"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); if (argc == 2) redis_cmd_append_sstr_long(&cmdstr, count); *cmd = cmdstr.c; *cmd_len = cmdstr.len; *ctx = argc == 2 ? PHPREDIS_CTX_PTR : NULL; return SUCCESS; } /* ZINCRBY */ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; size_t key_len; double incrby; zval *z_val; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sdz", &key, &key_len, &incrby, &z_val) == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, "ZINCRBY", "kfv", key, key_len, incrby, z_val); return SUCCESS; } /* SORT */ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_opts=NULL, *z_ele, z_argv; char *key; HashTable *ht_opts; smart_string cmdstr = {0}; size_t key_len; int key_free; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, &z_opts) == FAILURE) { return FAILURE; } // If we don't have an options array, the command is quite simple if (!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) { // Construct command *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "k", key, key_len); return SUCCESS; } // Create our hash table to hold our sort arguments array_init(&z_argv); // SORT key_free = redis_key_prefix(redis_sock, &key, &key_len); add_next_index_stringl(&z_argv, key, key_len); if (key_free) efree(key); // Set slot CMD_SET_SLOT(slot,key,key_len); // Grab the hash table ht_opts = Z_ARRVAL_P(z_opts); // Handle BY pattern if (((z_ele = zend_hash_str_find(ht_opts, "by", sizeof("by") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "BY", sizeof("BY") - 1)) != NULL ) && Z_TYPE_P(z_ele) == IS_STRING ) { // "BY" option is disabled in cluster if (slot) { php_error_docref(NULL, E_WARNING, "SORT BY option is not allowed in Redis Cluster"); zval_dtor(&z_argv); return FAILURE; } // ... BY add_next_index_stringl(&z_argv, "BY", sizeof("BY") - 1); add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } // Handle ASC/DESC option if (((z_ele = zend_hash_str_find(ht_opts, "sort", sizeof("sort") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "SORT", sizeof("SORT") - 1)) != NULL ) && Z_TYPE_P(z_ele) == IS_STRING ) { // 'asc'|'desc' add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } // STORE option if (((z_ele = zend_hash_str_find(ht_opts, "store", sizeof("store") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "STORE", sizeof("STORE") - 1)) != NULL ) && Z_TYPE_P(z_ele) == IS_STRING ) { // Slot verification int cross_slot = slot && *slot != cluster_hash_key( Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); if (cross_slot) { php_error_docref(0, E_WARNING, "Error, SORT key and STORE key have different slots!"); zval_dtor(&z_argv); return FAILURE; } // STORE add_next_index_stringl(&z_argv, "STORE", sizeof("STORE") - 1); add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); // We are using STORE *ctx = PHPREDIS_CTX_PTR; } // GET option if (((z_ele = zend_hash_str_find(ht_opts, "get", sizeof("get") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "GET", sizeof("GET") - 1)) != NULL ) && (Z_TYPE_P(z_ele) == IS_STRING || Z_TYPE_P(z_ele) == IS_ARRAY) ) { // Disabled in cluster if (slot) { php_error_docref(NULL, E_WARNING, "GET option for SORT disabled in Redis Cluster"); zval_dtor(&z_argv); return FAILURE; } // If it's a string just add it if (Z_TYPE_P(z_ele) == IS_STRING) { add_next_index_stringl(&z_argv, "GET", sizeof("GET") - 1); add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { int added = 0; zval *z_key; ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_ele), z_key) { // If we can't get the data, or it's not a string, skip if (z_key == NULL || Z_TYPE_P(z_key) != IS_STRING) { continue; } /* Add get per thing we're getting */ add_next_index_stringl(&z_argv, "GET", sizeof("GET") - 1); // Add this key to our argv array add_next_index_stringl(&z_argv, Z_STRVAL_P(z_key), Z_STRLEN_P(z_key)); added++; } ZEND_HASH_FOREACH_END(); // Make sure we were able to add at least one if (added == 0) { php_error_docref(NULL, E_WARNING, "Array of GET values requested, but none are valid"); zval_dtor(&z_argv); return FAILURE; } } } // ALPHA if (((z_ele = zend_hash_str_find(ht_opts, "alpha", sizeof("alpha") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL) && zval_is_true(z_ele) ) { add_next_index_stringl(&z_argv, "ALPHA", sizeof("ALPHA") - 1); } // LIMIT if (((z_ele = zend_hash_str_find(ht_opts, "limit", sizeof("limit") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "LIMIT", sizeof("LIMIT") - 1)) != NULL ) && Z_TYPE_P(z_ele) == IS_ARRAY ) { HashTable *ht_off = Z_ARRVAL_P(z_ele); zval *z_off, *z_cnt; if ((z_off = zend_hash_index_find(ht_off, 0)) != NULL && (z_cnt = zend_hash_index_find(ht_off, 1)) != NULL ) { if ((Z_TYPE_P(z_off) != IS_STRING && Z_TYPE_P(z_off) != IS_LONG) || (Z_TYPE_P(z_cnt) != IS_STRING && Z_TYPE_P(z_cnt) != IS_LONG) ) { php_error_docref(NULL, E_WARNING, "LIMIT options on SORT command must be longs or strings"); zval_dtor(&z_argv); return FAILURE; } // Add LIMIT argument add_next_index_stringl(&z_argv, "LIMIT", sizeof("LIMIT") - 1); long low, high; if (Z_TYPE_P(z_off) == IS_STRING) { low = atol(Z_STRVAL_P(z_off)); } else { low = Z_LVAL_P(z_off); } if (Z_TYPE_P(z_cnt) == IS_STRING) { high = atol(Z_STRVAL_P(z_cnt)); } else { high = Z_LVAL_P(z_cnt); } // Add our two LIMIT arguments add_next_index_long(&z_argv, low); add_next_index_long(&z_argv, high); } } // Start constructing our command HashTable *ht_argv = Z_ARRVAL_P(&z_argv); redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(ht_argv), kw, strlen(kw)); // Iterate through our arguments ZEND_HASH_FOREACH_VAL(ht_argv, z_ele) { // Args are strings or longs if (Z_TYPE_P(z_ele) == IS_STRING) { redis_cmd_append_sstr(&cmdstr,Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele)); } } ZEND_HASH_FOREACH_END(); /* Clean up our arguments array. Note we don't have to free any prefixed * key as that we didn't duplicate the pointer if we prefixed */ zval_dtor(&z_argv); // Push our length and command *cmd_len = cmdstr.len; *cmd = cmdstr.c; // Success! return SUCCESS; } /* HDEL */ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_args; smart_string cmdstr = {0}; char *arg; int arg_free, i; size_t arg_len; int argc = ZEND_NUM_ARGS(); zend_string *zstr; // We need at least KEY and one member if (argc < 2) { return FAILURE; } // Grab arguments as an array z_args = emalloc(argc * sizeof(zval)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } // Get first argument (the key) as a string zstr = zval_get_string(&z_args[0]); arg = ZSTR_VAL(zstr); arg_len = ZSTR_LEN(zstr); // Prefix arg_free = redis_key_prefix(redis_sock, &arg, &arg_len); // Start command construction redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HDEL")); redis_cmd_append_sstr(&cmdstr, arg, arg_len); // Set our slot, free key if we prefixed it CMD_SET_SLOT(slot,arg,arg_len); zend_string_release(zstr); if (arg_free) efree(arg); // Iterate through the members we're removing for (i = 1; i < argc; i++) { zstr = zval_get_string(&z_args[i]); redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; // Cleanup efree(z_args); // Success! return SUCCESS; } /* ZADD */ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *zstr, *key = NULL, *exp_type = NULL, *range_type = NULL; zend_bool ch = 0, incr = 0; smart_string cmdstr = {0}; zval *argv = NULL, *z_opt; int argc = 0, pos = 0; ZEND_PARSE_PARAMETERS_START(3, -1) Z_PARAM_STR(key) Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); // Need key, [NX|XX] [LT|GT] [CH] [INCR] score, value, [score, value...] */ if (argc % 2 != 0) { if (Z_TYPE(argv[0]) != IS_ARRAY) { return FAILURE; } ZEND_HASH_FOREACH_VAL(Z_ARRVAL(argv[0]), z_opt) { if (Z_TYPE_P(z_opt) == IS_STRING) { zstr = Z_STR_P(z_opt); if (zend_string_equals_literal_ci(zstr, "NX") || zend_string_equals_literal_ci(zstr, "XX")) { exp_type = Z_STR_P(z_opt); } else if (zend_string_equals_literal_ci(zstr, "LT") || zend_string_equals_literal_ci(zstr, "GT")) { range_type = Z_STR_P(z_opt); } else if (zend_string_equals_literal_ci(zstr, "CH")) { ch = 1; } else if (zend_string_equals_literal_ci(zstr, "INCR")) { if (argc != 3) { // Only one score-element pair can be specified in this mode. return FAILURE; } incr = 1; } } } ZEND_HASH_FOREACH_END(); pos++; } // Start command construction REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (argc - pos) + !!exp_type + !!range_type + !!ch + !!incr, "ZADD"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); if (exp_type) redis_cmd_append_sstr_zstr(&cmdstr, exp_type); if (range_type) redis_cmd_append_sstr_zstr(&cmdstr, range_type); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, ch, "CH"); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, incr, "INCR"); // Now the rest of our arguments while (pos < argc) { // Append score and member if (redis_cmd_append_sstr_score(&cmdstr, &argv[pos]) == FAILURE) { smart_string_free(&cmdstr); return FAILURE; } redis_cmd_append_sstr_zval(&cmdstr, &argv[pos+1], redis_sock); pos += 2; } // Push output values *cmd = cmdstr.c; *cmd_len = cmdstr.len; *ctx = incr ? PHPREDIS_CTX_PTR : NULL; return SUCCESS; } /* OBJECT */ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *subcmd = NULL, *key = NULL; smart_string cmdstr = {0}; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(subcmd) Z_PARAM_STR(key) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_string_equals_literal_ci(subcmd, "REFCOUNT") || zend_string_equals_literal_ci(subcmd, "IDLETIME")) { *ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(subcmd, "ENCODING")) { *ctx = PHPREDIS_CTX_PTR + 1; } else { php_error_docref(NULL, E_WARNING, "Invalid subcommand sent to OBJECT"); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "OBJECT"); redis_cmd_append_sstr_zstr(&cmdstr, subcmd); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_args, *z_ele; smart_string cmdstr = {0}; zend_bool ch = 0; zend_string *zstr; char *mode = NULL; int argc, i; // We at least need a key and three values if ((argc = ZEND_NUM_ARGS()) < 4 || (argc % 3 != 1 && argc % 3 != 2)) { zend_wrong_param_count(); return FAILURE; } // Make sure we at least have a key, and we can get other args z_args = ecalloc(argc, sizeof(*z_args)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } if (argc % 3 == 2) { argc--; if (Z_TYPE(z_args[argc]) != IS_ARRAY) { php_error_docref(NULL, E_WARNING, "Invalid options value"); efree(z_args); return FAILURE; } ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[argc]), z_ele) { ZVAL_DEREF(z_ele); if (Z_TYPE_P(z_ele) == IS_STRING) { if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "NX") || zend_string_equals_literal_ci(Z_STR_P(z_ele), "XX")) { mode = Z_STRVAL_P(z_ele); } else if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "CH")) { ch = 1; } } } ZEND_HASH_FOREACH_END(); } /* Initialize our command */ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc + (mode != NULL) + ch, "GEOADD"); /* Append key */ zstr = zval_get_string(&z_args[0]); redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot); zend_string_release(zstr); /* Append options */ if (mode != NULL) { redis_cmd_append_sstr(&cmdstr, mode, strlen(mode)); } REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, ch, "CH"); /* Append members */ for (i = 1; i < argc; ++i) { redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock); } // Cleanup arg array efree(z_args); // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* GEODIST */ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *source, *dest, *unit = NULL; size_t keylen, sourcelen, destlen, unitlen; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|s", &key, &keylen, &source, &sourcelen, &dest, &destlen, &unit, &unitlen) == FAILURE) { return FAILURE; } /* Construct command */ if (unit != NULL) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "GEODIST", "ksss", key, keylen, source, sourcelen, dest, destlen, unit, unitlen); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "GEODIST", "kss", key, keylen, source, sourcelen, dest, destlen); } return SUCCESS; } geoStoreType get_georadius_store_type(zend_string *key) { if (ZSTR_LEN(key) == 5 && !strcasecmp(ZSTR_VAL(key), "store")) { return STORE_COORD; } else if (ZSTR_LEN(key) == 9 && !strcasecmp(ZSTR_VAL(key), "storedist")) { return STORE_DIST; } return STORE_NONE; } /* Helper function to get COUNT and possible ANY flag which is passable to * both GEORADIUS and GEOSEARCH */ static int get_georadius_count_options(zval *optval, geoOptions *opts) { zval *z_tmp; /* Short circuit on bad options */ if (Z_TYPE_P(optval) != IS_ARRAY && Z_TYPE_P(optval) != IS_LONG) goto error; if (Z_TYPE_P(optval) == IS_ARRAY) { z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 0); if (z_tmp) { if (Z_TYPE_P(z_tmp) != IS_LONG || Z_LVAL_P(z_tmp) <= 0) goto error; opts->count = Z_LVAL_P(z_tmp); } z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 1); if (z_tmp) { opts->any = zval_is_true(z_tmp); } } else { if (Z_LVAL_P(optval) <= 0) goto error; opts->count = Z_LVAL_P(optval); } return SUCCESS; error: php_error_docref(NULL, E_WARNING, "Invalid COUNT value"); return FAILURE; } /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */ static int get_georadius_opts(HashTable *ht, geoOptions *opts) { zend_string *zkey; char *optstr; zval *optval; /* Iterate over our argument array, collating which ones we have */ ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, optval) { ZVAL_DEREF(optval); /* If the key is numeric it's a non value option */ if (zkey) { if (zend_string_equals_literal_ci(zkey, "COUNT")) { if (get_georadius_count_options(optval, opts) == FAILURE) { if (opts->key) zend_string_release(opts->key); return FAILURE; } } else if (opts->store == STORE_NONE) { opts->store = get_georadius_store_type(zkey); if (opts->store != STORE_NONE) { opts->key = zval_get_string(optval); } } } else { /* Option needs to be a string */ if (Z_TYPE_P(optval) != IS_STRING) continue; optstr = Z_STRVAL_P(optval); if (!strcasecmp(optstr, "withcoord")) { opts->withcoord = 1; } else if (!strcasecmp(optstr, "withdist")) { opts->withdist = 1; } else if (!strcasecmp(optstr, "withhash")) { opts->withhash = 1; } else if (!strcasecmp(optstr, "asc")) { opts->sort = SORT_ASC; } else if (!strcasecmp(optstr, "desc")) { opts->sort = SORT_DESC; } } } ZEND_HASH_FOREACH_END(); /* STORE and STOREDIST are not compatible with the WITH* options */ if (opts->key != NULL && (opts->withcoord || opts->withdist || opts->withhash)) { php_error_docref(NULL, E_WARNING, "STORE[DIST] is not compatible with WITHCOORD, WITHDIST or WITHHASH"); if (opts->key) zend_string_release(opts->key); return FAILURE; } /* Success */ return SUCCESS; } /* Helper to append options to a GEORADIUS or GEORADIUSBYMEMBER command */ void append_georadius_opts(RedisSock *redis_sock, smart_string *str, short *slot, geoOptions *opt) { if (opt->withcoord) REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHCOORD"); if (opt->withdist) REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHDIST"); if (opt->withhash) REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHHASH"); /* Append sort if it's not GEO_NONE */ if (opt->sort == SORT_ASC) { REDIS_CMD_APPEND_SSTR_STATIC(str, "ASC"); } else if (opt->sort == SORT_DESC) { REDIS_CMD_APPEND_SSTR_STATIC(str, "DESC"); } /* Append our count if we've got one */ if (opt->count) { REDIS_CMD_APPEND_SSTR_STATIC(str, "COUNT"); redis_cmd_append_sstr_long(str, opt->count); if (opt->any) { REDIS_CMD_APPEND_SSTR_STATIC(str, "ANY"); } } /* Append store options if we've got them */ if (opt->store != STORE_NONE && opt->key != NULL) { if (opt->store == STORE_COORD) { REDIS_CMD_APPEND_SSTR_STATIC(str, "STORE"); } else { REDIS_CMD_APPEND_SSTR_STATIC(str, "STOREDIST"); } redis_cmd_append_sstr_key_zstr(str, opt->key, redis_sock, slot); } } /* GEORADIUS / GEORADIUS_RO */ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *key = NULL, *unit = NULL; double lng = 0, lat = 0, radius = 0; smart_string cmdstr = {0}; HashTable *opts = NULL; geoOptions gopts = {0}; short store_slot = -1; uint32_t argc; ZEND_PARSE_PARAMETERS_START(5, 6) Z_PARAM_STR(key) Z_PARAM_DOUBLE(lng) Z_PARAM_DOUBLE(lat) Z_PARAM_DOUBLE(radius) Z_PARAM_STR(unit) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(opts) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); /* Parse any GEORADIUS options we have */ if (opts != NULL && get_georadius_opts(opts, &gopts) != SUCCESS) return FAILURE; /* Increment argc depending on options */ argc = 5 + gopts.withcoord + gopts.withdist + gopts.withhash + (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0) + (gopts.store != STORE_NONE ? 2 : 0); /* Begin construction of our command */ redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); /* Append required arguments */ redis_cmd_append_sstr_dbl(&cmdstr, lng); redis_cmd_append_sstr_dbl(&cmdstr, lat); redis_cmd_append_sstr_dbl(&cmdstr, radius); redis_cmd_append_sstr_zstr(&cmdstr, unit); /* Append optional arguments */ append_georadius_opts(redis_sock, &cmdstr, slot ? &store_slot : NULL, &gopts); /* Free key if it was prefixed */ if (gopts.key) zend_string_release(gopts.key); /* Protect the user from CROSSSLOT if we're in cluster */ if (slot && gopts.store != STORE_NONE && *slot != store_slot) { php_error_docref(NULL, E_WARNING, "Key and STORE[DIST] key must hash to the same slot"); efree(cmdstr.c); return FAILURE; } /* Set slot, command and len, and return */ *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* GEORADIUSBYMEMBER/GEORADIUSBYMEMBER_RO * key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] */ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem, *unit; size_t keylen, memlen, unitlen; short store_slot = 0; int keyfree, argc = 4; double radius; geoOptions gopts = {0}; zval *opts = NULL; smart_string cmdstr = {0}; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssds|a", &key, &keylen, &mem, &memlen, &radius, &unit, &unitlen, &opts) == FAILURE) { return FAILURE; } if (opts != NULL) { /* Attempt to parse our options array */ if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts) == FAILURE) { return FAILURE; } } /* Increment argc based on options */ argc += gopts.withcoord + gopts.withdist + gopts.withhash + (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0) + (gopts.store != STORE_NONE ? 2 : 0); /* Begin command construction*/ redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); /* Prefix our key if we're prefixing and set the slot */ keyfree = redis_key_prefix(redis_sock, &key, &keylen); CMD_SET_SLOT(slot, key, keylen); /* Append required arguments */ redis_cmd_append_sstr(&cmdstr, key, keylen); redis_cmd_append_sstr(&cmdstr, mem, memlen); redis_cmd_append_sstr_long(&cmdstr, radius); redis_cmd_append_sstr(&cmdstr, unit, unitlen); /* Append options */ append_georadius_opts(redis_sock, &cmdstr, slot ? &store_slot : NULL, &gopts); /* Free key if we prefixed */ if (keyfree) efree(key); if (gopts.key) zend_string_release(gopts.key); /* Protect the user from CROSSSLOT if we're in cluster */ if (slot && gopts.store != STORE_NONE && *slot != store_slot) { php_error_docref(NULL, E_WARNING, "Key and STORE[DIST] key must hash to the same slot"); efree(cmdstr.c); return FAILURE; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *unit; int argc = 2; size_t keylen, unitlen; geoOptions gopts = {0}; smart_string cmdstr = {0}; zval *position, *shape, *opts = NULL, *z_ele; zend_string *zkey, *zstr; if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a", &key, &keylen, &position, &shape, &unit, &unitlen, &opts) == FAILURE) { return FAILURE; } if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { argc += 2; } else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) { argc += 3; } else { php_error_docref(NULL, E_WARNING, "Invalid position"); return FAILURE; } if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { argc += 2; } else if (Z_TYPE_P(shape) == IS_ARRAY) { argc += 3; } else { php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); return FAILURE; } /* Attempt to parse our options array */ if (opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { ZVAL_DEREF(z_ele); if (zkey != NULL && zend_string_equals_literal_ci(zkey, "COUNT")) { if (get_georadius_count_options(z_ele, &gopts) == FAILURE) { return FAILURE; } } else if (Z_TYPE_P(z_ele) == IS_STRING) { zstr = Z_STR_P(z_ele); if (zend_string_equals_literal_ci(zstr, "WITHCOORD")) { gopts.withcoord = 1; } else if (zend_string_equals_literal_ci(zstr, "WITHDIST")) { gopts.withdist = 1; } else if (zend_string_equals_literal_ci(zstr, "WITHHASH")) { gopts.withhash = 1; } else if (zend_string_equals_literal_ci(zstr, "ASC")) { gopts.sort = SORT_ASC; } else if (zend_string_equals_literal_ci(zstr, "DESC")) { gopts.sort = SORT_DESC; } } } ZEND_HASH_FOREACH_END(); } /* Increment argc based on options */ argc += gopts.withcoord + gopts.withdist + gopts.withhash + (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCH"); redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); if (Z_TYPE_P(position) == IS_ARRAY) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { ZVAL_DEREF(z_ele); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); } ZEND_HASH_FOREACH_END(); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); } if (Z_TYPE_P(shape) == IS_ARRAY) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) { ZVAL_DEREF(z_ele); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); } ZEND_HASH_FOREACH_END(); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS"); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape)); } redis_cmd_append_sstr(&cmdstr, unit, unitlen); /* Append optional arguments */ if (gopts.withcoord) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHCOORD"); if (gopts.withdist) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHDIST"); if (gopts.withhash) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHHASH"); /* Append sort if it's not GEO_NONE */ if (gopts.sort == SORT_ASC) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASC"); } else if (gopts.sort == SORT_DESC) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DESC"); } /* Append our count if we've got one */ if (gopts.count) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, gopts.count); if (gopts.any) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ANY"); } } if ((argc = gopts.withcoord + gopts.withdist + gopts.withhash) > 0) { *ctx = PHPREDIS_CTX_PTR; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { int argc = 3; char *dest, *src, *unit; size_t destlen, srclen, unitlen; geoOptions gopts = {0}; smart_string cmdstr = {0}; zval *position, *shape, *opts = NULL, *z_ele; zend_string *zkey; short s2 = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszzs|a", &dest, &destlen, &src, &srclen, &position, &shape, &unit, &unitlen, &opts) == FAILURE) { return FAILURE; } if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { argc += 2; } else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) { argc += 3; } else { php_error_docref(NULL, E_WARNING, "Invalid position"); return FAILURE; } if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { argc += 2; } else if (Z_TYPE_P(shape) == IS_ARRAY) { argc += 3; } else { php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); return FAILURE; } /* Attempt to parse our options array */ if (opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { ZVAL_DEREF(z_ele); if (zkey != NULL) { if (zend_string_equals_literal_ci(zkey, "COUNT")) { if (Z_TYPE_P(z_ele) != IS_LONG || Z_LVAL_P(z_ele) <= 0) { php_error_docref(NULL, E_WARNING, "COUNT must be an integer > 0!"); return FAILURE; } gopts.count = Z_LVAL_P(z_ele); } } else if (Z_TYPE_P(z_ele) == IS_STRING) { if (!strcasecmp(Z_STRVAL_P(z_ele), "ASC")) { gopts.sort = SORT_ASC; } else if (!strcasecmp(Z_STRVAL_P(z_ele), "DESC")) { gopts.sort = SORT_DESC; } else if (!strcasecmp(Z_STRVAL_P(z_ele), "STOREDIST")) { gopts.store = STORE_DIST; } } } ZEND_HASH_FOREACH_END(); } /* Increment argc based on options */ argc += gopts.withcoord + gopts.withdist + gopts.withhash + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0) + (gopts.store != STORE_NONE); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCHSTORE"); redis_cmd_append_sstr_key(&cmdstr, dest, destlen, redis_sock, slot); redis_cmd_append_sstr_key(&cmdstr, src, srclen, redis_sock, slot ? &s2 : NULL); if (slot && *slot != s2) { php_error_docref(NULL, E_WARNING, "All keys must hash to the same slot"); efree(cmdstr.c); return FAILURE; } if (Z_TYPE_P(position) == IS_ARRAY) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { ZVAL_DEREF(z_ele); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); } ZEND_HASH_FOREACH_END(); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); } if (Z_TYPE_P(shape) == IS_ARRAY) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) { ZVAL_DEREF(z_ele); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); } ZEND_HASH_FOREACH_END(); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS"); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape)); } redis_cmd_append_sstr(&cmdstr, unit, unitlen); /* Append sort if it's not GEO_NONE */ if (gopts.sort == SORT_ASC) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASC"); } else if (gopts.sort == SORT_DESC) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DESC"); } /* Append our count if we've got one */ if (gopts.count) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, gopts.count); } if (gopts.store == STORE_DIST) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "STOREDIST"); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* MIGRATE host port destination-db timeout [COPY] [REPLACE] [[AUTH password] | [AUTH2 username password]] [KEYS key [key ...]] Starting with Redis version 3.0.0: Added the COPY and REPLACE options. Starting with Redis version 3.0.6: Added the KEYS option. Starting with Redis version 4.0.7: Added the AUTH option. Starting with Redis version 6.0.0: Added the AUTH2 option. */ /* MIGRATE */ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *host = NULL, *key = NULL, *user = NULL, *pass = NULL; zend_long destdb = 0, port = 0, timeout = 0; zval *zkeys = NULL, *zkey, *zauth = NULL; zend_bool copy = 0, replace = 0; smart_string cmdstr = {0}; int argc; ZEND_PARSE_PARAMETERS_START(5, 8) Z_PARAM_STR(host) Z_PARAM_LONG(port) Z_PARAM_ZVAL(zkeys) Z_PARAM_LONG(destdb) Z_PARAM_LONG(timeout) Z_PARAM_OPTIONAL Z_PARAM_BOOL(copy) Z_PARAM_BOOL(replace) Z_PARAM_ZVAL_OR_NULL(zauth) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); /* Sanity check on our optional AUTH argument */ if (zauth && redis_extract_auth_info(zauth, &user, &pass) == FAILURE) { php_error_docref(NULL, E_WARNING, "AUTH must be a string or an array with one or two strings"); user = pass = NULL; } /* Protect against being passed an array with zero elements */ if (Z_TYPE_P(zkeys) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(zkeys)) == 0) { php_error_docref(NULL, E_WARNING, "Keys array cannot be empty"); return FAILURE; } /* host, port, key|"", dest-db, timeout, [copy, replace] [KEYS key1..keyN] */ argc = 5 + copy + replace + (user||pass ? 1 : 0) + (user != NULL) + (pass != NULL); if (Z_TYPE_P(zkeys) == IS_ARRAY) { /* +1 for the "KEYS" argument itself */ argc += 1 + zend_hash_num_elements(Z_ARRVAL_P(zkeys)); } /* Initialize MIGRATE command with host and port */ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "MIGRATE"); redis_cmd_append_sstr_zstr(&cmdstr, host); redis_cmd_append_sstr_long(&cmdstr, port); /* If passed a keys array the keys come later, otherwise pass the key to * migrate here */ if (Z_TYPE_P(zkeys) == IS_ARRAY) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, ""); } else { key = redis_key_prefix_zval(redis_sock, zkeys); redis_cmd_append_sstr_zstr(&cmdstr, key); zend_string_release(key); } redis_cmd_append_sstr_long(&cmdstr, destdb); redis_cmd_append_sstr_long(&cmdstr, timeout); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, copy, "COPY"); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, replace, "REPLACE"); if (user && pass) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AUTH2"); redis_cmd_append_sstr_zstr(&cmdstr, user); redis_cmd_append_sstr_zstr(&cmdstr, pass); } else if (pass) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AUTH"); redis_cmd_append_sstr_zstr(&cmdstr, pass); } /* Append actual keys if we've got a keys array */ if (Z_TYPE_P(zkeys) == IS_ARRAY) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "KEYS"); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zkeys), zkey) { key = redis_key_prefix_zval(redis_sock, zkey); redis_cmd_append_sstr_zstr(&cmdstr, key); zend_string_release(key); } ZEND_HASH_FOREACH_END(); } if (user) zend_string_release(user); if (pass) zend_string_release(pass); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* A generic passthru function for variadic key commands that take one or more * keys. This is essentially all of them except ones that STORE data. */ int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, strlen(kw), 0, cmd, cmd_len, slot); } static int redis_build_client_list_command(smart_string *cmdstr, int argc, zval *z_args) { zend_string *zkey; zval *z_ele, *type = NULL, *id = NULL; if (argc > 0) { if (Z_TYPE(z_args[0]) != IS_ARRAY) { return FAILURE; } ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[0]), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "type")) { if (Z_TYPE_P(z_ele) != IS_STRING || ( !ZVAL_STRICMP_STATIC(z_ele, "normal") && !ZVAL_STRICMP_STATIC(z_ele, "master") && !ZVAL_STRICMP_STATIC(z_ele, "replica") && !ZVAL_STRICMP_STATIC(z_ele, "pubsub") )) { return FAILURE; } type = z_ele; } else if (zend_string_equals_literal_ci(zkey, "id")) { if (Z_TYPE_P(z_ele) != IS_STRING && ( Z_TYPE_P(z_ele) != IS_ARRAY || !zend_hash_num_elements(Z_ARRVAL_P(z_ele)) )) { return FAILURE; } id = z_ele; } } } ZEND_HASH_FOREACH_END(); } REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 1 + (type ? 2 : 0) + ( id ? (Z_TYPE_P(id) == IS_ARRAY ? 1 + zend_hash_num_elements(Z_ARRVAL_P(id)) : 2) : 0 ), "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "LIST"); if (type != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TYPE"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(type), Z_STRLEN_P(type)); } if (id != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ID"); if (Z_TYPE_P(id) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(id), z_ele) { if (Z_TYPE_P(z_ele) == IS_STRING) { redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { zkey = zval_get_string(z_ele); redis_cmd_append_sstr(cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); zend_string_release(zkey); } } ZEND_HASH_FOREACH_END(); } else { redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(id), Z_STRLEN_P(id)); } } return SUCCESS; } static int redis_build_client_kill_command(smart_string *cmdstr, int argc, zval *z_args) { zend_string *zkey; zval *z_ele, *id = NULL, *type = NULL, *address = NULL, *opts = NULL, *user = NULL, *addr = NULL, *laddr = NULL, *skipme = NULL; if (argc > 0) { if (argc > 1) { if (Z_TYPE(z_args[0]) != IS_STRING || Z_TYPE(z_args[1]) != IS_ARRAY) { return FAILURE; } address = &z_args[0]; opts = &z_args[1]; } else if (Z_TYPE(z_args[0]) == IS_STRING) { address = &z_args[0]; } else if (Z_TYPE(z_args[0]) == IS_ARRAY) { opts = &z_args[0]; } else { return FAILURE; } if (opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (Z_TYPE_P(z_ele) != IS_STRING) { return FAILURE; } if (zend_string_equals_literal_ci(zkey, "id")) { id = z_ele; } else if (zend_string_equals_literal_ci(zkey, "type")) { if (!ZVAL_STRICMP_STATIC(z_ele, "normal") && !ZVAL_STRICMP_STATIC(z_ele, "master") && !ZVAL_STRICMP_STATIC(z_ele, "slave") && !ZVAL_STRICMP_STATIC(z_ele, "replica") && !ZVAL_STRICMP_STATIC(z_ele, "pubsub") ) { return FAILURE; } type = z_ele; } else if (zend_string_equals_literal_ci(zkey, "user")) { user = z_ele; } else if (zend_string_equals_literal_ci(zkey, "addr")) { addr = z_ele; } else if (zend_string_equals_literal_ci(zkey, "laddr")) { laddr = z_ele; } else if (zend_string_equals_literal_ci(zkey, "skipme")) { if (!ZVAL_STRICMP_STATIC(z_ele, "yes") && !ZVAL_STRICMP_STATIC(z_ele, "no") ) { return FAILURE; } skipme = z_ele; } } } ZEND_HASH_FOREACH_END(); } } REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 1 + (address != 0) + (id ? 2 : 0) + (type ? 2 : 0) + (user ? 2 : 0) + (addr ? 2 : 0) + (laddr ? 2 : 0) + (skipme ? 2 : 0), "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "KILL"); if (address != NULL) { redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(address), Z_STRLEN_P(address)); } if (id != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ID"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(id), Z_STRLEN_P(id)); } if (type != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TYPE"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(type), Z_STRLEN_P(type)); } if (user != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "USER"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(user), Z_STRLEN_P(user)); } if (addr != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ADDR"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(addr), Z_STRLEN_P(addr)); } if (laddr != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "LADDR"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(laddr), Z_STRLEN_P(laddr)); } if (skipme != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "SKIPME"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(skipme), Z_STRLEN_P(skipme)); } return SUCCESS; } static int redis_build_client_tracking_command(smart_string *cmdstr, int argc, zval *z_args) { zend_string *zkey; zval *z_ele, *redirect = NULL, *prefix = NULL; zend_bool bcast = 0, optin = 0, optout = 0, noloop = 0; if (argc < 1) { return FAILURE; } if (argc > 1) { if (Z_TYPE(z_args[1]) != IS_ARRAY) { return FAILURE; } ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "redirect")) { if (Z_TYPE_P(z_ele) != IS_STRING) { return FAILURE; } redirect = z_ele; } else if (zend_string_equals_literal_ci(zkey, "prefix")) { if (Z_TYPE_P(z_ele) != IS_STRING && Z_TYPE_P(z_ele) != IS_ARRAY) { return FAILURE; } prefix = z_ele; } else if (zend_string_equals_literal_ci(zkey, "bcast")) { bcast = zval_is_true(z_ele); } else if (zend_string_equals_literal_ci(zkey, "optin")) { optin = zval_is_true(z_ele); } else if (zend_string_equals_literal_ci(zkey, "optout")) { optout = zval_is_true(z_ele); } else if (zend_string_equals_literal_ci(zkey, "noloop")) { noloop = zval_is_true(z_ele); } } } ZEND_HASH_FOREACH_END(); } REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 2 + (redirect ? 2 : 0) + (prefix ? 2 * zend_hash_num_elements(Z_ARRVAL_P(prefix)) : 0) + bcast + optin + optout + noloop, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TRACKING"); if (Z_TYPE(z_args[0]) == IS_STRING && ( ZVAL_STRICMP_STATIC(&z_args[0], "on") || ZVAL_STRICMP_STATIC(&z_args[0], "off") )) { redis_cmd_append_sstr(cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } else if (zval_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ON"); } else { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OFF"); } if (redirect != NULL) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "REDIRECT"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(redirect), Z_STRLEN_P(redirect)); } if (prefix != NULL) { if (Z_TYPE_P(prefix) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(prefix), z_ele) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PREFIX"); if (Z_TYPE_P(z_ele) == IS_STRING) { redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { zkey = zval_get_string(z_ele); redis_cmd_append_sstr(cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); zend_string_release(zkey); } } ZEND_HASH_FOREACH_END(); } else { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PREFIX"); redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(prefix), Z_STRLEN_P(prefix)); } } if (bcast) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "BCAST"); } if (optin) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OPTIN"); } if (optout) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OPTOUT"); } if (noloop) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "NOLOOP"); } return SUCCESS; } int redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_string *op = NULL; zval *z_args = NULL; int argc = 0; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_VARIADIC('*', z_args, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_string_equals_literal_ci(op, "INFO")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "INFO"); } else if (zend_string_equals_literal_ci(op, "LIST")) { if (redis_build_client_list_command(&cmdstr, argc, z_args) != 0) { return FAILURE; } *ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "CACHING")) { if (argc < 1) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "CACHING"); if (Z_TYPE(z_args[0]) == IS_STRING && ( ZVAL_STRICMP_STATIC(&z_args[0], "yes") || ZVAL_STRICMP_STATIC(&z_args[0], "no") )) { redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } else if (zval_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "YES"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO"); } *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "GETNAME")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GETNAME"); *ctx = PHPREDIS_CTX_PTR + 3; } else if (zend_string_equals_literal_ci(op, "GETREDIR") || zend_string_equals_literal_ci(op, "ID")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(op), ZSTR_LEN(op)); *ctx = PHPREDIS_CTX_PTR + 2; } else if (zend_string_equals_literal_ci(op, "KILL")) { if (redis_build_client_kill_command(&cmdstr, argc, z_args) != 0) { return FAILURE; } *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "NO-EVICT")) { if (argc < 1) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO-EVICT"); if (Z_TYPE(z_args[0]) == IS_STRING && ( ZVAL_STRICMP_STATIC(&z_args[0], "on") || ZVAL_STRICMP_STATIC(&z_args[0], "off") )) { redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } else if (zval_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ON"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "OFF"); } *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "PAUSE")) { if (argc < 1 || Z_TYPE(z_args[0]) != IS_LONG || ( argc > 1 && ( Z_TYPE(z_args[1]) != IS_STRING || ( !ZVAL_STRICMP_STATIC(&z_args[1], "write") && !ZVAL_STRICMP_STATIC(&z_args[1], "all") ) ) )) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 1 ? 3 : 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "PAUSE"); redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(z_args[0])); if (argc > 1) { redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "REPLY")) { if (argc > 0 && ( Z_TYPE(z_args[0]) != IS_STRING || ( !ZVAL_STRICMP_STATIC(&z_args[0], "on") && !ZVAL_STRICMP_STATIC(&z_args[0], "off") && !ZVAL_STRICMP_STATIC(&z_args[0], "skip") ) )) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 0 ? 2 : 1, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REPLY"); if (argc > 0) { redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "SETNAME")) { if (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SETNAME"); redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "TRACKING")) { if (redis_build_client_tracking_command(&cmdstr, argc, z_args) != 0) { return FAILURE; } *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "TRACKINGINFO")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TRACKINGINFO"); *ctx = PHPREDIS_CTX_PTR + 4; } else if (zend_string_equals_literal_ci(op, "UNBLOCK")) { if (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING || ( argc > 1 && ( Z_TYPE(z_args[1]) != IS_STRING || ( !ZVAL_STRICMP_STATIC(&z_args[1], "timeout") && !ZVAL_STRICMP_STATIC(&z_args[1], "error") ) ) )) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 1 ? 3 : 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "UNBLOCK"); redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); if (argc > 1) { redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } *ctx = PHPREDIS_CTX_PTR + 2; } else if (zend_string_equals_literal_ci(op, "UNPAUSE")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "UNPAUSE"); *ctx = PHPREDIS_CTX_PTR + 1; } else { return FAILURE; } // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* COMMAND */ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_string *op = NULL, *zstr; zval *z_args = NULL; int i, argc = 0; ZEND_PARSE_PARAMETERS_START(0, -1) Z_PARAM_OPTIONAL Z_PARAM_STR(op) Z_PARAM_VARIADIC('*', z_args, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (op == NULL) { *ctx = NULL; argc = 0; } else if (zend_string_equals_literal_ci(op, "COUNT")) { *ctx = PHPREDIS_CTX_PTR; argc = 0; } else if (zend_string_equals_literal_ci(op, "DOCS") || zend_string_equals_literal_ci(op, "INFO") ) { *ctx = NULL; } else if (zend_string_equals_literal_ci(op, "GETKEYS") || zend_string_equals_literal_ci(op, "LIST") ) { *ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "GETKEYSANDFLAGS")) { *ctx = PHPREDIS_CTX_PTR + 2; } else { php_error_docref(NULL, E_WARNING, "Unknown COMMAND operation '%s'", ZSTR_VAL(op)); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, !!op + argc, "COMMAND"); if (op) redis_cmd_append_sstr_zstr(&cmdstr, op); for (i = 0; i < argc; ++i) { zstr = zval_get_string(&z_args[i]); redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; /* Any slot will do */ CMD_RAND_SLOT(slot); return SUCCESS; } int redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *src = NULL, *dst = NULL; smart_string cmdstr = {0}; HashTable *opts = NULL; zend_bool replace = 0; zend_string *zkey; zend_long db = -1; short slot2; zval *zv; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(src) Z_PARAM_STR(dst) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(opts) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zkey, zv) { if (zkey == NULL) continue; ZVAL_DEREF(zv); if (zend_string_equals_literal_ci(zkey, "db")) { db = zval_get_long(zv); } else if (zend_string_equals_literal_ci(zkey, "replace")) { replace = zval_is_true(zv); } } ZEND_HASH_FOREACH_END(); } if (slot && db != -1) { php_error_docref(NULL, E_WARNING, "Cant copy to a specific DB in cluster mode"); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (db > -1 ? 2 : 0) + replace, "COPY"); redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); if (slot && *slot != slot2) { php_error_docref(NULL, E_WARNING, "Keys must hash to the same slot!"); efree(cmdstr.c); return FAILURE; } if (db > -1) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DB"); redis_cmd_append_sstr_long(&cmdstr, db); } REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, replace, "REPLACE"); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* XADD */ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_string *arrkey; zval *z_fields, *value; zend_long maxlen = 0; zend_bool approx = 0, nomkstream = 0; zend_ulong idx; HashTable *ht_fields; int fcount, argc; char *key, *id; size_t keylen, idlen; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|lbb", &key, &keylen, &id, &idlen, &z_fields, &maxlen, &approx, &nomkstream) == FAILURE) { return FAILURE; } /* At least one field and string are required */ ht_fields = Z_ARRVAL_P(z_fields); if ((fcount = zend_hash_num_elements(ht_fields)) == 0) { return FAILURE; } if (maxlen < 0 || (maxlen == 0 && approx != 0)) { php_error_docref(NULL, E_WARNING, "Warning: Invalid MAXLEN argument or approximate flag"); } /* Calculate argc for XADD. It's a bit complex because we've got * an optional MAXLEN argument which can either take the form MAXLEN N * or MAXLEN ~ N */ argc = 2 + nomkstream + (fcount * 2) + (maxlen > 0 ? (approx ? 3 : 2) : 0); /* XADD key ID field string [field string ...] */ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XADD"); redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); if (nomkstream) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NOMKSTREAM"); } /* Now append our MAXLEN bits if we've got them */ if (maxlen > 0) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN"); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, approx, "~"); redis_cmd_append_sstr_long(&cmdstr, maxlen); } /* Now append ID and field(s) */ redis_cmd_append_sstr(&cmdstr, id, idlen); ZEND_HASH_FOREACH_KEY_VAL(ht_fields, idx, arrkey, value) { redis_cmd_append_sstr_arrkey(&cmdstr, arrkey, idx); redis_cmd_append_sstr_zval(&cmdstr, value, redis_sock); } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } // XPENDING key group [start end count [consumer] [idle]] int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *key = NULL, *group = NULL, *start = NULL, *end = NULL, *consumer = NULL; zend_long count = -1, idle = 0; smart_string cmdstr = {0}; int argc; ZEND_PARSE_PARAMETERS_START(2, 7) Z_PARAM_STR(key) Z_PARAM_STR(group) Z_PARAM_OPTIONAL Z_PARAM_STR_OR_NULL(start) Z_PARAM_STR_OR_NULL(end) Z_PARAM_LONG(count) Z_PARAM_STR_OR_NULL(consumer) Z_PARAM_LONG(idle) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); /* If we've been passed a start argument, we also need end and count */ if (start != NULL && (end == NULL || count < 0)) { php_error_docref(NULL, E_WARNING, "'$start' must be accompanied by '$end' and '$count' arguments"); return FAILURE; } /* Calculate argc. It's either 2, 5, 6 or 7 */ argc = 2 + (start != NULL ? 3 + (consumer != NULL) + (idle != 0) : 0); /* Construct command and add required arguments */ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XPENDING"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); redis_cmd_append_sstr_zstr(&cmdstr, group); /* Add optional argumentst */ if (start) { if (idle != 0) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IDLE"); redis_cmd_append_sstr_long(&cmdstr, (long)idle); } redis_cmd_append_sstr_zstr(&cmdstr, start); redis_cmd_append_sstr_zstr(&cmdstr, end); redis_cmd_append_sstr_long(&cmdstr, (long)count); /* Finally add consumer if we have it */ if (consumer) redis_cmd_append_sstr_zstr(&cmdstr, consumer); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* X[REV]RANGE key start end [COUNT count] */ int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; char *key, *start, *end; size_t keylen, startlen, endlen; zend_long count = -1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &keylen, &start, &startlen, &end, &endlen, &count) == FAILURE) { return FAILURE; } redis_cmd_init_sstr(&cmdstr, 3 + (2 * (count > -1)), kw, strlen(kw)); redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); redis_cmd_append_sstr(&cmdstr, start, startlen); redis_cmd_append_sstr(&cmdstr, end, endlen); if (count > -1) { redis_cmd_append_sstr(&cmdstr, ZEND_STRL("COUNT")); redis_cmd_append_sstr_long(&cmdstr, count); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* Helper function to take an associative array and append the Redis * STREAMS stream [stream...] id [id ...] arguments to a command string. */ static int append_stream_args(smart_string *cmdstr, HashTable *ht, RedisSock *redis_sock, short *slot) { char *kptr, kbuf[40]; int klen, i, pos = 0; zend_string *key, *idstr; short oldslot = -1; zval **id; zend_ulong idx; /* Append STREAM qualifier */ REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "STREAMS"); /* Allocate memory to keep IDs */ id = emalloc(sizeof(*id) * zend_hash_num_elements(ht)); /* Iterate over our stream => id array appending streams and retaining each * value for final arguments */ ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, id[pos++]) { if (key) { klen = ZSTR_LEN(key); kptr = ZSTR_VAL(key); } else { klen = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); kptr = (char*)kbuf; } /* Append stream key */ redis_cmd_append_sstr_key(cmdstr, kptr, klen, redis_sock, slot); /* Protect the user against CROSSSLOT to avoid confusion */ if (slot) { if (oldslot != -1 && *slot != oldslot) { php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!"); efree(id); return FAILURE; } oldslot = *slot; } } ZEND_HASH_FOREACH_END(); /* Add our IDs */ for (i = 0; i < pos; i++) { idstr = zval_get_string(id[i]); redis_cmd_append_sstr(cmdstr, ZSTR_VAL(idstr), ZSTR_LEN(idstr)); zend_string_release(idstr); } /* Clean up ID container array */ efree(id); return 0; } /* XREAD [COUNT count] [BLOCK ms] STREAMS key [key ...] id [id ...] */ int redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zend_long count = -1, block = -1; zval *z_streams; int argc, scount; HashTable *kt; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|ll", &z_streams, &count, &block) == FAILURE) { return FAILURE; } /* At least one stream and ID is required */ kt = Z_ARRVAL_P(z_streams); if ((scount = zend_hash_num_elements(kt)) < 1) { return FAILURE; } /* Calculate argc and start constructing command */ argc = 1 + (2 * scount) + (2 * (count > -1)) + (2 * (block > -1)); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XREAD"); /* Append COUNT if we have it */ if (count > -1) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, count); } /* Append BLOCK if we have it */ if (block > -1) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BLOCK"); redis_cmd_append_sstr_long(&cmdstr, block); } /* Append final STREAM key [key ...] id [id ...] arguments */ if (append_stream_args(&cmdstr, kt, redis_sock, slot) < 0) { efree(cmdstr.c); return FAILURE; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* XREADGROUP GROUP group consumer [COUNT count] [BLOCK ms] * STREAMS key [key ...] id [id ...] */ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; zval *z_streams; HashTable *kt; char *group, *consumer; size_t grouplen, consumerlen; int scount, argc; zend_long count, block; zend_bool no_count = 1, no_block = 1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|l!l!", &group, &grouplen, &consumer, &consumerlen, &z_streams, &count, &no_count, &block, &no_block) == FAILURE) { return FAILURE; } /* Negative COUNT or BLOCK is illegal so abort immediately */ if ((!no_count && count < 0) || (!no_block && block < 0)) { php_error_docref(NULL, E_WARNING, "Negative values for COUNT or BLOCK are illegal."); return FAILURE; } /* Redis requires at least one stream */ kt = Z_ARRVAL_P(z_streams); if ((scount = zend_hash_num_elements(kt)) < 1) { return FAILURE; } /* Calculate argc and start constructing commands */ argc = 4 + (2 * scount) + (2 * !no_count) + (2 * !no_block); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XREADGROUP"); /* Group and consumer */ REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GROUP"); redis_cmd_append_sstr(&cmdstr, group, grouplen); redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); /* Append COUNT if we have it */ if (!no_count) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, count); } /* Append BLOCK argument if we have it */ if (!no_block) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BLOCK"); redis_cmd_append_sstr_long(&cmdstr, block); } /* Finally append stream and id args */ if (append_stream_args(&cmdstr, kt, redis_sock, slot) < 0) { efree(cmdstr.c); return FAILURE; } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* XACK key group id [id ...] */ int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; char *key, *group; size_t keylen, grouplen; zend_string *idstr; zval *z_ids, *z_id; HashTable *ht_ids; int idcount; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa", &key, &keylen, &group, &grouplen, &z_ids) == FAILURE) { return FAILURE; } ht_ids = Z_ARRVAL_P(z_ids); if ((idcount = zend_hash_num_elements(ht_ids)) < 1) { return FAILURE; } /* Create command and add initial arguments */ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + idcount, "XACK"); redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); redis_cmd_append_sstr(&cmdstr, group, grouplen); /* Append IDs */ ZEND_HASH_FOREACH_VAL(ht_ids, z_id) { idstr = zval_get_string(z_id); redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(idstr), ZSTR_LEN(idstr)); zend_string_release(idstr); } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* XCLAIM options container */ typedef struct xclaimOptions { struct { char *type; int64_t time; } idle; zend_long retrycount; int force; int justid; } xclaimOptions; /* Attempt to extract an int64_t from the provided zval */ static int zval_get_i64(zval *zv, int64_t *retval) { if (Z_TYPE_P(zv) == IS_LONG) { *retval = (int64_t)Z_LVAL_P(zv); return SUCCESS; } else if (Z_TYPE_P(zv) == IS_DOUBLE) { *retval = (int64_t)Z_DVAL_P(zv); return SUCCESS; } else if (Z_TYPE_P(zv) == IS_STRING) { zend_long lval; double dval; switch (is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &lval, &dval, 1)) { case IS_LONG: *retval = (int64_t)lval; return SUCCESS; case IS_DOUBLE: *retval = (int64_t)dval; return SUCCESS; } } /* If we make it here we have failed */ return FAILURE; } /* Helper function to get an integer XCLAIM argument. This can overflow a * 32-bit PHP long so we have to extract it as an int64_t. If the value is * not a valid number or negative, we'll inform the user of the problem and * that the argument is being ignored. */ static int64_t get_xclaim_i64_arg(const char *key, zval *zv) { int64_t retval = -1; /* Extract an i64, and if we can't let the user know there is an issue. */ if (zval_get_i64(zv, &retval) == FAILURE || retval < 0) { php_error_docref(NULL, E_WARNING, "Invalid XCLAIM option '%s' will be ignored", key); } return retval; } /* Helper to extract XCLAIM options */ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) { zend_string *zkey; HashTable *ht; zval *zv; /* Initialize options array to sane defaults */ memset(opt, 0, sizeof(*opt)); opt->retrycount = -1; opt->idle.time = -1; /* Early return if we don't have any options */ if (z_arr == NULL) return; /* Iterate over our options array */ ht = Z_ARRVAL_P(z_arr); ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, zv) { if (zkey) { if (zend_string_equals_literal_ci(zkey, "TIME")) { opt->idle.type = "TIME"; opt->idle.time = get_xclaim_i64_arg("TIME", zv); } else if (zend_string_equals_literal_ci(zkey, "IDLE")) { opt->idle.type = "IDLE"; opt->idle.time = get_xclaim_i64_arg("IDLE", zv); } else if (zend_string_equals_literal_ci(zkey, "RETRYCOUNT")) { opt->retrycount = zval_get_long(zv); } } else if (Z_TYPE_P(zv) == IS_STRING) { if (zend_string_equals_literal_ci(Z_STR_P(zv), "FORCE")) { opt->force = 1; } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "JUSTID")) { opt->justid = 1; } } } ZEND_HASH_FOREACH_END(); } /* Count argc for any options we may have */ static int xclaim_options_argc(xclaimOptions *opt) { int argc = 0; if (opt->idle.type != NULL && opt->idle.time != -1) argc += 2; if (opt->retrycount != -1) argc += 2; if (opt->force) argc++; if (opt->justid) argc++; return argc; } /* Append XCLAIM options */ static void append_xclaim_options(smart_string *cmd, xclaimOptions *opt) { /* IDLE/TIME long */ if (opt->idle.type != NULL && opt->idle.time != -1) { redis_cmd_append_sstr(cmd, opt->idle.type, strlen(opt->idle.type)); redis_cmd_append_sstr_i64(cmd, opt->idle.time); } /* RETRYCOUNT */ if (opt->retrycount != -1) { REDIS_CMD_APPEND_SSTR_STATIC(cmd, "RETRYCOUNT"); redis_cmd_append_sstr_long(cmd, opt->retrycount); } /* FORCE and JUSTID */ if (opt->force) REDIS_CMD_APPEND_SSTR_STATIC(cmd, "FORCE"); if (opt->justid) REDIS_CMD_APPEND_SSTR_STATIC(cmd, "JUSTID"); } int redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; char *key, *group, *consumer, *start; size_t keylen, grouplen, consumerlen, startlen; zend_long min_idle, count = -1; zend_bool justid = 0; int argc; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssls|lb", &key, &keylen, &group, &grouplen, &consumer, &consumerlen, &min_idle, &start, &startlen, &count, &justid ) == FAILURE) { return FAILURE; } argc = 5 + (count > 0 ? 1 + count : 0) + justid; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XAUTOCLAIM"); redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); redis_cmd_append_sstr(&cmdstr, group, grouplen); redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); redis_cmd_append_sstr_long(&cmdstr, min_idle); redis_cmd_append_sstr(&cmdstr, start, startlen); if (count > 0) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, count); } if (justid) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "JUSTID"); } // Set the context to distinguish XCLAIM from XAUTOCLAIM which // have slightly different reply structures. *ctx = PHPREDIS_CTX_PTR; *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* XCLAIM [IDLE ] [TIME ] [RETRYCOUNT ] [FORCE] [JUSTID] */ int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; char *key, *group, *consumer; size_t keylen, grouplen, consumerlen; zend_long min_idle; int argc, id_count; zval *z_ids, *z_id, *z_opts = NULL; zend_string *zstr; HashTable *ht_ids; xclaimOptions opts; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssla|a", &key, &keylen, &group, &grouplen, &consumer, &consumerlen, &min_idle, &z_ids, &z_opts) == FAILURE) { return FAILURE; } /* At least one id is required */ ht_ids = Z_ARRVAL_P(z_ids); if ((id_count = zend_hash_num_elements(ht_ids)) < 1) { return FAILURE; } /* Extract options array if we've got them */ get_xclaim_options(z_opts, &opts); /* Now we have enough information to calculate argc */ argc = 4 + id_count + xclaim_options_argc(&opts); /* Start constructing our command */ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XCLAIM"); redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); redis_cmd_append_sstr(&cmdstr, group, grouplen); redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); redis_cmd_append_sstr_long(&cmdstr, min_idle); /* Add IDs */ ZEND_HASH_FOREACH_VAL(ht_ids, z_id) { zstr = zval_get_string(z_id); redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); /* Finally add our options */ append_xclaim_options(&cmdstr, &opts); /* Success */ *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* XGROUP HELP * XGROUP CREATE key group id [MKSTREAM] [ENTRIESREAD ] * XGROUP SETID key group id [ENTRIESREAD ] * XGROUP CREATECONSUMER key group consumer * XGROUP DELCONSUMER key group consumer * XGROUP DESTROY key group */ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *op = NULL, *key = NULL, *group = NULL, *id_or_consumer = NULL; int nargs, is_create = 0, is_setid = 0; zend_long entries_read = -2; smart_string cmdstr = {0}; zend_bool mkstream = 0; ZEND_PARSE_PARAMETERS_START(1, 6) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_STR(key) Z_PARAM_STR(group) Z_PARAM_STR(id_or_consumer) Z_PARAM_BOOL(mkstream) Z_PARAM_LONG(entries_read) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (zend_string_equals_literal_ci(op, "HELP")) { nargs = 0; } else if ((is_create = zend_string_equals_literal_ci(op, "CREATE")) || (is_setid = zend_string_equals_literal_ci(op, "SETID")) || zend_string_equals_literal_ci(op, "CREATECONSUMER") || zend_string_equals_literal_ci(op, "DELCONSUMER")) { nargs = 3; } else if (zend_string_equals_literal_ci(op, "DESTROY")) { nargs = 2; } else { php_error_docref(NULL, E_WARNING, "Unknown XGROUP operation '%s'", ZSTR_VAL(op)); return FAILURE; } if (ZEND_NUM_ARGS() < nargs) { php_error_docref(NULL, E_WARNING, "Operation '%s' requires %d arguments", ZSTR_VAL(op), nargs); return FAILURE; } mkstream &= is_create; if (!(is_create || is_setid)) entries_read = -2; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + nargs + !!mkstream + (entries_read != -2 ? 2 : 0), "XGROUP"); redis_cmd_append_sstr_zstr(&cmdstr, op); if (nargs-- > 0) redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); if (nargs-- > 0) redis_cmd_append_sstr_zstr(&cmdstr, group); if (nargs-- > 0) redis_cmd_append_sstr_zstr(&cmdstr, id_or_consumer); REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, !!mkstream, "MKSTREAM"); if (entries_read != -2) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ENTRIESREAD"); redis_cmd_append_sstr_long(&cmdstr, entries_read); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } /* XINFO CONSUMERS key group * XINFO GROUPS key * XINFO STREAM key [FULL [COUNT N]] * XINFO HELP */ int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *op = NULL, *key = NULL, *arg = NULL; smart_string cmdstr = {0}; zend_long count = -1; ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_STR_OR_NULL(key) Z_PARAM_STR_OR_NULL(arg) Z_PARAM_LONG(count) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if ((arg != NULL && key == NULL) || (count != -1 && (key == NULL || arg == NULL))) { php_error_docref(NULL, E_WARNING, "Cannot pass a non-null optional argument after a NULL one."); return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (key != NULL) + (arg != NULL) + (count > -1 ? 2 : 0), "XINFO"); redis_cmd_append_sstr_zstr(&cmdstr, op); if (key != NULL) redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); if (arg != NULL) redis_cmd_append_sstr_zstr(&cmdstr, arg); if (count > -1) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, count); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } // XTRIM key [= | ~] threshold [LIMIT count] int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *key = NULL, *threshold = NULL; zend_bool approx = 0, minid = 0; smart_string cmdstr = {0}; zend_long limit = -1; int argc; ZEND_PARSE_PARAMETERS_START(2, 5) Z_PARAM_STR(key) Z_PARAM_STR(threshold) Z_PARAM_OPTIONAL Z_PARAM_BOOL(approx) Z_PARAM_BOOL(minid) Z_PARAM_LONG(limit) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); argc = 4 + (approx && limit > -1 ? 2 : 0); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XTRIM"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); if (minid) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MINID"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN"); } if (approx) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "~"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "="); } redis_cmd_append_sstr_zstr(&cmdstr, threshold); if (limit > -1 && approx) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT"); redis_cmd_append_sstr_long(&cmdstr, limit); } else if (limit > -1) { php_error_docref(NULL, E_WARNING, "Cannot use LIMIT without an approximate match, ignoring"); } else if (ZEND_NUM_ARGS() == 5) { php_error_docref(NULL, E_WARNING, "Limit must be >= 0"); } *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } // [P]EXPIRE[AT] [NX | XX | GT | LT] int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *key = NULL, *mode = NULL; smart_string cmdstr = {0}; zend_long timeout = 0; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(key) Z_PARAM_LONG(timeout) Z_PARAM_OPTIONAL Z_PARAM_STR_OR_NULL(mode) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (mode != NULL && !(zend_string_equals_literal_ci(mode, "NX") || zend_string_equals_literal_ci(mode, "XX") || zend_string_equals_literal_ci(mode, "LT") || zend_string_equals_literal_ci(mode, "GT"))) { php_error_docref(NULL, E_WARNING, "Unknown expiration modifier '%s'", ZSTR_VAL(mode)); return FAILURE; } redis_cmd_init_sstr(&cmdstr, 2 + (mode != NULL), kw, strlen(kw)); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); redis_cmd_append_sstr_long(&cmdstr, timeout); if (mode != NULL) redis_cmd_append_sstr_zstr(&cmdstr, mode); *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; } int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { if (zend_parse_parameters_none() == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "s", kw, strlen(kw)); return SUCCESS; } int redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { zend_string *name; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) { return FAILURE; } *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "sS", kw, strlen(kw), name); return SUCCESS; } /* * Redis commands that don't deal with the server at all. The RedisSock* * pointer is the only thing retrieved differently, so we just take that * in addition to the standard INTERNAL_FUNCTION_PARAMETERS for arg parsing, * return value handling, and thread safety. */ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redisCluster *c) { zend_long option; if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &option) == FAILURE) { RETURN_FALSE; } // Return the requested option switch(option) { case REDIS_OPT_SERIALIZER: RETURN_LONG(redis_sock->serializer); case REDIS_OPT_COMPRESSION: RETURN_LONG(redis_sock->compression); case REDIS_OPT_COMPRESSION_LEVEL: RETURN_LONG(redis_sock->compression_level); case REDIS_OPT_PREFIX: if (redis_sock->prefix) { RETURN_STRINGL(ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix)); } RETURN_NULL(); case REDIS_OPT_READ_TIMEOUT: RETURN_DOUBLE(redis_sock->read_timeout); case REDIS_OPT_TCP_KEEPALIVE: RETURN_LONG(redis_sock->tcp_keepalive); case REDIS_OPT_SCAN: RETURN_LONG(redis_sock->scan); case REDIS_OPT_REPLY_LITERAL: RETURN_LONG(redis_sock->reply_literal); case REDIS_OPT_NULL_MBULK_AS_NULL: RETURN_LONG(redis_sock->null_mbulk_as_null); case REDIS_OPT_FAILOVER: RETURN_LONG(c->failover); case REDIS_OPT_MAX_RETRIES: RETURN_LONG(redis_sock->max_retries); case REDIS_OPT_BACKOFF_ALGORITHM: RETURN_LONG(redis_sock->backoff.algorithm); case REDIS_OPT_BACKOFF_BASE: RETURN_LONG(redis_sock->backoff.base / 1000); case REDIS_OPT_BACKOFF_CAP: RETURN_LONG(redis_sock->backoff.cap / 1000); default: RETURN_FALSE; } } void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redisCluster *c) { zend_long val_long, option; zval *val; zend_string *val_str; struct timeval read_tv; int tcp_keepalive = 0; php_netstream_data_t *sock; if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &option, &val) == FAILURE) { RETURN_FALSE; } switch(option) { case REDIS_OPT_SERIALIZER: val_long = zval_get_long(val); if (val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP || val_long == REDIS_SERIALIZER_JSON #ifdef HAVE_REDIS_IGBINARY || val_long == REDIS_SERIALIZER_IGBINARY #endif #ifdef HAVE_REDIS_MSGPACK || val_long == REDIS_SERIALIZER_MSGPACK #endif ) { redis_sock->serializer = val_long; RETURN_TRUE; } break; case REDIS_OPT_REPLY_LITERAL: val_long = zval_get_long(val); redis_sock->reply_literal = val_long != 0; RETURN_TRUE; case REDIS_OPT_NULL_MBULK_AS_NULL: val_long = zval_get_long(val); redis_sock->null_mbulk_as_null = val_long != 0; RETURN_TRUE; case REDIS_OPT_COMPRESSION: val_long = zval_get_long(val); if (val_long == REDIS_COMPRESSION_NONE #ifdef HAVE_REDIS_LZF || val_long == REDIS_COMPRESSION_LZF #endif #ifdef HAVE_REDIS_ZSTD || val_long == REDIS_COMPRESSION_ZSTD #endif #ifdef HAVE_REDIS_LZ4 || val_long == REDIS_COMPRESSION_LZ4 #endif ) { redis_sock->compression = val_long; RETURN_TRUE; } break; case REDIS_OPT_COMPRESSION_LEVEL: val_long = zval_get_long(val); redis_sock->compression_level = val_long; RETURN_TRUE; case REDIS_OPT_PREFIX: if (redis_sock->prefix) { zend_string_release(redis_sock->prefix); redis_sock->prefix = NULL; } val_str = zval_get_string(val); if (ZSTR_LEN(val_str) > 0) { redis_sock->prefix = val_str; } else { zend_string_release(val_str); } RETURN_TRUE; case REDIS_OPT_READ_TIMEOUT: redis_sock->read_timeout = zval_get_double(val); if (redis_sock->stream) { read_tv.tv_sec = (time_t)redis_sock->read_timeout; read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &read_tv); } RETURN_TRUE; case REDIS_OPT_TCP_KEEPALIVE: /* Don't set TCP_KEEPALIVE if we're using a unix socket. */ if (ZSTR_VAL(redis_sock->host)[0] == '/' && redis_sock->port < 1) { RETURN_FALSE; } tcp_keepalive = zval_get_long(val) > 0 ? 1 : 0; if (redis_sock->tcp_keepalive == tcp_keepalive) { RETURN_TRUE; } if (redis_sock->stream) { /* set TCP_KEEPALIVE */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; if (setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&tcp_keepalive, sizeof(tcp_keepalive)) == -1) { RETURN_FALSE; } redis_sock->tcp_keepalive = tcp_keepalive; } RETURN_TRUE; case REDIS_OPT_SCAN: val_long = zval_get_long(val); if (val_long == REDIS_SCAN_NORETRY) { redis_sock->scan &= ~REDIS_SCAN_RETRY; } else if (val_long == REDIS_SCAN_NOPREFIX) { redis_sock->scan &= ~REDIS_SCAN_PREFIX; } else if (val_long == REDIS_SCAN_RETRY || val_long == REDIS_SCAN_PREFIX) { redis_sock->scan |= val_long; } else { break; } RETURN_TRUE; case REDIS_OPT_FAILOVER: if (c == NULL) RETURN_FALSE; val_long = zval_get_long(val); if (val_long == REDIS_FAILOVER_NONE || val_long == REDIS_FAILOVER_ERROR || val_long == REDIS_FAILOVER_DISTRIBUTE || val_long == REDIS_FAILOVER_DISTRIBUTE_SLAVES) { c->failover = val_long; RETURN_TRUE; } break; case REDIS_OPT_MAX_RETRIES: val_long = zval_get_long(val); if(val_long >= 0) { redis_sock->max_retries = val_long; RETURN_TRUE; } break; case REDIS_OPT_BACKOFF_ALGORITHM: val_long = zval_get_long(val); if(val_long >= 0 && val_long < REDIS_BACKOFF_ALGORITHMS) { redis_sock->backoff.algorithm = val_long; RETURN_TRUE; } break; case REDIS_OPT_BACKOFF_BASE: val_long = zval_get_long(val); if(val_long >= 0) { redis_sock->backoff.base = val_long * 1000; RETURN_TRUE; } break; case REDIS_OPT_BACKOFF_CAP: val_long = zval_get_long(val); if(val_long >= 0) { redis_sock->backoff.cap = val_long * 1000; RETURN_TRUE; } break; default: php_error_docref(NULL, E_WARNING, "Unknown option '" ZEND_LONG_FMT "'", option); break; } RETURN_FALSE; } void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *key; size_t key_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) ==FAILURE) { RETURN_FALSE; } if (redis_sock->prefix) { int keyfree = redis_key_prefix(redis_sock, &key, &key_len); RETVAL_STRINGL(key, key_len); if (keyfree) efree(key); } else { RETURN_STRINGL(key, key_len); } } void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zval *z_val; char *val; size_t val_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z_val) == FAILURE) { RETURN_FALSE; } int val_free = redis_serialize(redis_sock, z_val, &val, &val_len); RETVAL_STRINGL(val, val_len); if (val_free) efree(val); } void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_class_entry *ex) { char *value; size_t value_len; // Parse our arguments if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) == FAILURE) { RETURN_FALSE; } // We only need to attempt unserialization if we have a serializer running if (redis_sock->serializer == REDIS_SERIALIZER_NONE) { // Just return the value that was passed to us RETURN_STRINGL(value, value_len); } zval z_ret; if (!redis_unserialize(redis_sock, value, value_len, &z_ret)) { // Badly formed input, throw an exception zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0); RETURN_FALSE; } RETURN_ZVAL(&z_ret, 0, 0); } void redis_compress_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *zstr; size_t len; char *buf; int cmp_free; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &zstr) == FAILURE) { RETURN_FALSE; } cmp_free = redis_compress(redis_sock, &buf, &len, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); RETVAL_STRINGL(buf, len); if (cmp_free) efree(buf); } void redis_uncompress_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_class_entry *ex) { zend_string *zstr; size_t len; char *buf; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &zstr) == FAILURE) { RETURN_FALSE; } else if (ZSTR_LEN(zstr) == 0 || redis_sock->compression == REDIS_COMPRESSION_NONE) { RETURN_STR_COPY(zstr); } if (!redis_uncompress(redis_sock, &buf, &len, ZSTR_VAL(zstr), ZSTR_LEN(zstr))) { zend_throw_exception(ex, "Invalid compressed data or uncompression error", 0); RETURN_FALSE; } RETVAL_STRINGL(buf, len); efree(buf); } void redis_pack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { int valfree; size_t len; char *val; zval *zv; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zv) == FAILURE) { RETURN_FALSE; } valfree = redis_pack(redis_sock, zv, &val, &len); RETVAL_STRINGL(val, len); if (valfree) efree(val); } void redis_unpack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *str; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) { RETURN_FALSE; } if (redis_unpack(redis_sock, ZSTR_VAL(str), ZSTR_LEN(str), return_value) == 0) { RETURN_STR_COPY(str); } } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ redis-6.0.2/redis_commands.h0000644000175000000120000004247314515245367016572 0ustar pyatsukhnenkowheel#ifndef REDIS_COMMANDS_H #define REDIS_COMMANDS_H #include "common.h" #include "library.h" #include "cluster_library.h" /* Pick a random slot, any slot (for stuff like publish/subscribe) */ #define CMD_RAND_SLOT(slot) \ if(slot) *slot = rand() % REDIS_CLUSTER_MOD /* Macro for setting the slot if we've been asked to */ #define CMD_SET_SLOT(slot,key,key_len) \ if (slot) *slot = cluster_hash_key(key,key_len); /* Simple container so we can push subscribe context out */ typedef struct { zend_fcall_info fci; zend_fcall_info_cache fci_cache; } subscribeCallback; typedef struct subscribeContext { char *kw; int argc; subscribeCallback cb; } subscribeContext; /* Construct a raw command */ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len); /* Construct a script command */ smart_string *redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args); /* Redis command generics. Many commands share common prototypes meaning that * we can write one function to handle all of them. For example, there are * many COMMAND key value commands, or COMMAND key commands. */ int redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); /* Construct SCAN and similar commands, as well as check iterator */ int redis_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, char **cmd, int *cmd_len); /* ZRANGE, ZREVRANGE, ZRANGEBYSCORE, and ZREVRANGEBYSCORE callback type */ typedef int (*zrange_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *,char**,int*,int*,short*,void**); int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them * unique */ int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); int redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); /* Commands that don't communicate with Redis at all (such as getOption, * setOption, _prefix, _serialize, etc). These can be handled in one place * with the method of grabbing our RedisSock* object in different ways * depending if this is a Redis object or a RedisCluster object. */ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redisCluster *c); void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redisCluster *c); void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_class_entry *ex); void redis_compress_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); void redis_uncompress_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_class_entry *ex); void redis_pack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); void redis_unpack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); #endif /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ redis-6.0.2/redis_session.c0000644000175000000120000011347614515245367016451 0ustar pyatsukhnenkowheel/* -*- 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 | | Maintainer: Nicolas Favre-Felix | | Maintainer: Nasreddine Bouafif | | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ #include "common.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef PHP_SESSION #include "ext/standard/info.h" #include "php_redis.h" #include "redis_session.h" #include #include "library.h" #include "cluster_library.h" #include "php.h" #include "php_ini.h" #include "php_variables.h" #include "SAPI.h" #include "ext/standard/url.h" #define REDIS_SESSION_PREFIX "PHPREDIS_SESSION:" #define CLUSTER_SESSION_PREFIX "PHPREDIS_CLUSTER_SESSION:" /* Session lock LUA as well as its SHA1 hash */ #define LOCK_RELEASE_LUA_STR "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end" #define LOCK_RELEASE_LUA_LEN (sizeof(LOCK_RELEASE_LUA_STR) - 1) #define LOCK_RELEASE_SHA_STR "b70c2384248f88e6b75b9f89241a180f856ad852" #define LOCK_RELEASE_SHA_LEN (sizeof(LOCK_RELEASE_SHA_STR) - 1) /* Check if a response is the Redis +OK status response */ #define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3)) #define NEGATIVE_LOCK_RESPONSE 1 #define CLUSTER_DEFAULT_PREFIX() \ zend_string_init(CLUSTER_SESSION_PREFIX, sizeof(CLUSTER_SESSION_PREFIX) - 1, 0) ps_module ps_mod_redis = { PS_MOD_UPDATE_TIMESTAMP(redis) }; ps_module ps_mod_redis_cluster = { PS_MOD_UPDATE_TIMESTAMP(rediscluster) }; typedef struct { zend_bool is_locked; zend_string *session_key; zend_string *lock_key; zend_string *lock_secret; } redis_session_lock_status; typedef struct redis_pool_member_ { RedisSock *redis_sock; int weight; struct redis_pool_member_ *next; } redis_pool_member; typedef struct { int totalWeight; int count; redis_pool_member *head; redis_session_lock_status lock_status; } redis_pool; // static char *session_conf_string(HashTable *ht, const char *key, size_t keylen) { // } PHP_REDIS_API void redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight) { redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); rpm->redis_sock = redis_sock; rpm->weight = weight; rpm->next = pool->head; pool->head = rpm; pool->totalWeight += weight; } PHP_REDIS_API void redis_pool_free(redis_pool *pool) { redis_pool_member *rpm, *next; rpm = pool->head; while (rpm) { next = rpm->next; redis_sock_disconnect(rpm->redis_sock, 0, 1); redis_free_socket(rpm->redis_sock); efree(rpm); rpm = next; } /* Cleanup after our lock */ if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); if (pool->lock_status.lock_secret) zend_string_release(pool->lock_status.lock_secret); if (pool->lock_status.lock_key) zend_string_release(pool->lock_status.lock_key); /* Cleanup pool itself */ efree(pool); } /* Retreive session.gc_maxlifetime from php.ini protecting against an integer overflow */ static int session_gc_maxlifetime(void) { zend_long value = INI_INT("session.gc_maxlifetime"); if (value > INT_MAX) { php_error_docref(NULL, E_NOTICE, "session.gc_maxlifetime overflows INT_MAX, truncating."); return INT_MAX; } else if (value <= 0) { php_error_docref(NULL, E_NOTICE, "session.gc_maxlifetime is <= 0, defaulting to 1440 seconds"); return 1440; } return value; } /* Send a command to Redis. Returns byte count written to socket (-1 on failure) */ static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, char **reply, int *replylen) { *reply = NULL; int len_written = redis_sock_write(redis_sock, cmd, cmdlen); if (len_written >= 0) { *reply = redis_sock_read(redis_sock, replylen); } return len_written; } PHP_REDIS_API redis_pool_member * redis_pool_get_sock(redis_pool *pool, const char *key) { unsigned int pos, i; memcpy(&pos, key, sizeof(pos)); pos %= pool->totalWeight; redis_pool_member *rpm = pool->head; for(i = 0; i < pool->totalWeight;) { if (pos >= i && pos < i + rpm->weight) { if (redis_sock_server_open(rpm->redis_sock) == 0) { return rpm; } } i += rpm->weight; rpm = rpm->next; } return NULL; } /* Helper to set our session lock key */ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len ) { char *reply; int sent_len, reply_len; sent_len = redis_simple_cmd(redis_sock, cmd, cmd_len, &reply, &reply_len); if (reply) { if (IS_REDIS_OK(reply, reply_len)) { efree(reply); return SUCCESS; } efree(reply); } /* Return FAILURE in case of network problems */ return sent_len >= 0 ? NEGATIVE_LOCK_RESPONSE : FAILURE; } static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status ) { char *cmd, hostname[HOST_NAME_MAX] = {0}, suffix[] = "_LOCK"; int cmd_len, lock_wait_time, retries, i, set_lock_key_result, expiry; /* Short circuit if we are already locked or not using session locks */ if (lock_status->is_locked || !INI_INT("redis.session.locking_enabled")) return SUCCESS; /* How long to wait between attempts to acquire lock */ lock_wait_time = INI_INT("redis.session.lock_wait_time"); if (lock_wait_time == 0) { lock_wait_time = 20000; } /* Maximum number of times to retry (-1 means infinite) */ retries = INI_INT("redis.session.lock_retries"); if (retries == 0) { retries = 100; } /* How long should the lock live (in seconds) */ expiry = INI_INT("redis.session.lock_expire"); if (expiry == 0) { expiry = INI_INT("max_execution_time"); } /* Generate our qualified lock key */ if (lock_status->lock_key) zend_string_release(lock_status->lock_key); lock_status->lock_key = zend_string_alloc(ZSTR_LEN(lock_status->session_key) + sizeof(suffix) - 1, 0); memcpy(ZSTR_VAL(lock_status->lock_key), ZSTR_VAL(lock_status->session_key), ZSTR_LEN(lock_status->session_key)); memcpy(ZSTR_VAL(lock_status->lock_key) + ZSTR_LEN(lock_status->session_key), suffix, sizeof(suffix) - 1); /* Calculate lock secret */ gethostname(hostname, HOST_NAME_MAX); if (lock_status->lock_secret) zend_string_release(lock_status->lock_secret); lock_status->lock_secret = strpprintf(0, "%s|%ld", hostname, (long)getpid()); if (expiry > 0) { cmd_len = REDIS_SPPRINTF(&cmd, "SET", "SSssd", lock_status->lock_key, lock_status->lock_secret, "NX", 2, "PX", 2, expiry * 1000); } else { cmd_len = REDIS_SPPRINTF(&cmd, "SET", "SSs", lock_status->lock_key, lock_status->lock_secret, "NX", 2); } /* Attempt to get our lock */ for (i = 0; retries == -1 || i <= retries; i++) { set_lock_key_result = set_session_lock_key(redis_sock, cmd, cmd_len); if (set_lock_key_result == SUCCESS) { lock_status->is_locked = 1; break; } else if (set_lock_key_result == FAILURE) { /* In case of network problems, break the loop and report to userland */ lock_status->is_locked = 0; break; } /* Sleep unless we're done making attempts */ if (retries == -1 || i < retries) { usleep(lock_wait_time); } } /* Cleanup SET command */ efree(cmd); /* Success if we're locked */ return lock_status->is_locked ? SUCCESS : FAILURE; } #define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !strncmp(reply, ZSTR_VAL(secret), len)) static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status) { if (!INI_INT("redis.session.locking_enabled")) { return 1; } /* If locked and redis.session.lock_expire is not set => TTL=max_execution_time Therefore it is guaranteed that the current process is still holding the lock */ if (lock_status->is_locked && INI_INT("redis.session.lock_expire") != 0) { char *cmd, *reply = NULL; int replylen, cmdlen; /* Command to get our lock key value and compare secrets */ cmdlen = REDIS_SPPRINTF(&cmd, "GET", "S", lock_status->lock_key); /* Attempt to refresh the lock */ redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); /* Cleanup */ efree(cmd); if (reply == NULL) { lock_status->is_locked = 0; } else { lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret); efree(reply); } /* Issue a warning if we're not locked. We don't attempt to refresh the lock * if we aren't flagged as locked, so if we're not flagged here something * failed */ if (!lock_status->is_locked) { php_error_docref(NULL, E_WARNING, "Session lock expired"); } } return lock_status->is_locked; } /* Release any session lock we hold and cleanup allocated lock data. This function * first attempts to use EVALSHA and then falls back to EVAL if EVALSHA fails. This * will cause Redis to cache the script, so subsequent calls should then succeed * using EVALSHA. */ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status) { char *cmd, *reply; int i, cmdlen, replylen; /* Keywords, command, and length fallbacks */ const char *kwd[] = {"EVALSHA", "EVAL"}; const char *lua[] = {LOCK_RELEASE_SHA_STR, LOCK_RELEASE_LUA_STR}; int len[] = {LOCK_RELEASE_SHA_LEN, LOCK_RELEASE_LUA_LEN}; /* We first want to try EVALSHA and then fall back to EVAL */ for (i = 0; lock_status->is_locked && i < sizeof(kwd)/sizeof(*kwd); i++) { /* Construct our command */ cmdlen = REDIS_SPPRINTF(&cmd, (char*)kwd[i], "sdSS", lua[i], len[i], 1, lock_status->lock_key, lock_status->lock_secret); /* Send it off */ redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); /* Release lock and cleanup reply if we got one */ if (reply != NULL) { lock_status->is_locked = 0; efree(reply); } /* Cleanup command */ efree(cmd); } /* Something has failed if we are still locked */ if (lock_status->is_locked) { php_error_docref(NULL, E_WARNING, "Failed to release session lock"); } } #if PHP_VERSION_ID < 70300 #define REDIS_URL_STR(umem) umem #else #define REDIS_URL_STR(umem) ZSTR_VAL(umem) #endif /* {{{ PS_OPEN_FUNC */ PS_OPEN_FUNC(redis) { php_url *url; zval params, context, *zv; int i, j, path_len; redis_pool *pool = ecalloc(1, sizeof(*pool)); for (i = 0, j = 0, path_len = strlen(save_path); i < path_len; i = j + 1) { /* find beginning of url */ while ( i< path_len && (isspace(save_path[i]) || save_path[i] == ',')) i++; /* find end of url */ j = i; while (jquery != NULL) { HashTable *ht; char *query; array_init(¶ms); if (url->fragment) { spprintf(&query, 0, "%s#%s", REDIS_URL_STR(url->query), REDIS_URL_STR(url->fragment)); } else { query = estrdup(REDIS_URL_STR(url->query)); } sapi_module.treat_data(PARSE_STRING, query, ¶ms); ht = Z_ARRVAL(params); REDIS_CONF_INT_STATIC(ht, "weight", &weight); REDIS_CONF_BOOL_STATIC(ht, "persistent", &persistent); REDIS_CONF_INT_STATIC(ht, "database", &db); REDIS_CONF_DOUBLE_STATIC(ht, "timeout", &timeout); REDIS_CONF_DOUBLE_STATIC(ht, "read_timeout", &read_timeout); REDIS_CONF_LONG_STATIC(ht, "retry_interval", &retry_interval); REDIS_CONF_STRING_STATIC(ht, "persistent_id", &persistent_id); REDIS_CONF_STRING_STATIC(ht, "prefix", &prefix); REDIS_CONF_AUTH_STATIC(ht, "auth", &user, &pass); if ((zv = REDIS_HASH_STR_FIND_TYPE_STATIC(ht, "stream", IS_ARRAY)) != NULL) { ZVAL_ZVAL(&context, zv, 1, 0); } zval_dtor(¶ms); } if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { char *path = estrndup(save_path+i, j-i); php_error_docref(NULL, E_WARNING, "Failed to parse session.save_path (error at offset %d, url was '%s')", i, path); efree(path); php_url_free(url); if (persistent_id) zend_string_release(persistent_id); if (prefix) zend_string_release(prefix); if (user) zend_string_release(user); if (pass) zend_string_release(pass); redis_pool_free(pool); PS_SET_MOD_DATA(NULL); return FAILURE; } RedisSock *redis_sock; char *addr, *scheme; size_t addrlen; int port, addr_free = 0; scheme = url->scheme ? REDIS_URL_STR(url->scheme) : "tcp"; if (url->host) { port = url->port; addrlen = spprintf(&addr, 0, "%s://%s", scheme, REDIS_URL_STR(url->host)); addr_free = 1; } else { /* unix */ port = 0; addr = REDIS_URL_STR(url->path); addrlen = strlen(addr); } redis_sock = redis_sock_create(addr, addrlen, port, timeout, read_timeout, persistent, persistent_id ? ZSTR_VAL(persistent_id) : NULL, retry_interval); if (db >= 0) { /* default is -1 which leaves the choice to redis. */ redis_sock->dbNumber = db; } if (Z_TYPE(context) == IS_ARRAY) { redis_sock_set_stream_context(redis_sock, &context); } redis_pool_add(pool, redis_sock, weight); redis_sock->prefix = prefix; redis_sock_set_auth(redis_sock, user, pass); if (addr_free) efree(addr); if (persistent_id) zend_string_release(persistent_id); if (user) zend_string_release(user); if (pass) zend_string_release(pass); php_url_free(url); } } if (pool->head) { PS_SET_MOD_DATA(pool); return SUCCESS; } return FAILURE; } /* }}} */ /* {{{ PS_CLOSE_FUNC */ PS_CLOSE_FUNC(redis) { redis_pool *pool = PS_GET_MOD_DATA(); if (pool) { if (pool->lock_status.session_key) { redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(pool->lock_status.session_key)); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (redis_sock) { lock_release(redis_sock, &pool->lock_status); } } redis_pool_free(pool); PS_SET_MOD_DATA(NULL); } return SUCCESS; } /* }}} */ static zend_string * redis_session_key(RedisSock *redis_sock, const char *key, int key_len) { zend_string *session; char default_prefix[] = REDIS_SESSION_PREFIX; char *prefix = default_prefix; size_t prefix_len = sizeof(default_prefix)-1; if (redis_sock->prefix) { prefix = ZSTR_VAL(redis_sock->prefix); prefix_len = ZSTR_LEN(redis_sock->prefix); } /* build session key */ session = zend_string_alloc(key_len + prefix_len, 0); memcpy(ZSTR_VAL(session), prefix, prefix_len); memcpy(ZSTR_VAL(session) + prefix_len, key, key_len); return session; } /* {{{ PS_CREATE_SID_FUNC */ PS_CREATE_SID_FUNC(redis) { int retries = 3; redis_pool *pool = PS_GET_MOD_DATA(); if (!pool) { return php_session_create_id(NULL); } while (retries-- > 0) { zend_string* sid = php_session_create_id((void **) &pool); redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid)); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_NOTICE, "Redis connection not available"); zend_string_release(sid); return php_session_create_id(NULL); } if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); pool->lock_status.session_key = redis_session_key(redis_sock, ZSTR_VAL(sid), ZSTR_LEN(sid)); if (lock_acquire(redis_sock, &pool->lock_status) == SUCCESS) { return sid; } zend_string_release(pool->lock_status.session_key); zend_string_release(sid); sid = NULL; } php_error_docref(NULL, E_WARNING, "Acquiring session lock failed while creating session_id"); return NULL; } /* }}} */ /* {{{ PS_VALIDATE_SID_FUNC */ PS_VALIDATE_SID_FUNC(redis) { char *cmd, *response; int cmd_len, response_len; const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key); if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); return FAILURE; } /* send EXISTS command */ zend_string *session = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "EXISTS", "S", session); zend_string_release(session); if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); efree(cmd); return FAILURE; } efree(cmd); if (response_len == 2 && response[0] == ':' && response[1] == '1') { efree(response); return SUCCESS; } else { efree(response); return FAILURE; } } /* }}} */ /* {{{ PS_UPDATE_TIMESTAMP_FUNC */ PS_UPDATE_TIMESTAMP_FUNC(redis) { char *cmd, *response; int cmd_len, response_len; const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key); if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); return FAILURE; } /* send EXPIRE command */ zend_string *session = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, session_gc_maxlifetime()); zend_string_release(session); if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); efree(cmd); return FAILURE; } efree(cmd); if (response_len == 2 && response[0] == ':') { efree(response); return SUCCESS; } else { efree(response); return FAILURE; } } /* }}} */ /* {{{ PS_READ_FUNC */ PS_READ_FUNC(redis) { char *resp, *cmd; int resp_len, cmd_len; const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key); if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); return FAILURE; } /* send GET command */ if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); pool->lock_status.session_key = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); if (lock_acquire(redis_sock, &pool->lock_status) != SUCCESS) { php_error_docref(NULL, E_WARNING, "Failed to acquire session lock"); efree(cmd); return FAILURE; } if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); efree(cmd); return FAILURE; } efree(cmd); /* Read response from Redis. If we get a NULL response from redis_sock_read * this can indicate an error, OR a "NULL bulk" reply (empty session data) * in which case we can reply with success. */ if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL && resp_len != -1) { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); return FAILURE; } if (resp_len < 0) { *val = ZSTR_EMPTY_ALLOC(); } else { *val = zend_string_init(resp, resp_len, 0); } efree(resp); return SUCCESS; } /* }}} */ /* {{{ PS_WRITE_FUNC */ PS_WRITE_FUNC(redis) { char *cmd, *response; int cmd_len, response_len; const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); return FAILURE; } /* send SET command */ zend_string *session = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, session_gc_maxlifetime(), sval, svallen); zend_string_release(session); if (!write_allowed(redis_sock, &pool->lock_status)) { php_error_docref(NULL, E_WARNING, "Unable to write session: session lock not held"); efree(cmd); return FAILURE; } if (redis_sock_write(redis_sock, cmd, cmd_len ) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); efree(cmd); return FAILURE; } efree(cmd); if (IS_REDIS_OK(response, response_len)) { efree(response); return SUCCESS; } else { php_error_docref(NULL, E_WARNING, "Error writing session data to Redis: %s", response); efree(response); return FAILURE; } } /* }}} */ /* {{{ PS_DESTROY_FUNC */ PS_DESTROY_FUNC(redis) { char *cmd, *response; int cmd_len, response_len; const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key); redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); return FAILURE; } /* Release lock */ lock_release(redis_sock, &pool->lock_status); /* send DEL command */ zend_string *session = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "S", session); zend_string_release(session); if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); efree(cmd); return FAILURE; } efree(cmd); if (response_len == 2 && response[0] == ':' && (response[1] == '0' || response[1] == '1')) { efree(response); return SUCCESS; } else { efree(response); return FAILURE; } } /* }}} */ /* {{{ PS_GC_FUNC */ PS_GC_FUNC(redis) { return SUCCESS; } /* }}} */ /** * Redis Cluster session handler functions */ /* Prefix a session key */ static char *cluster_session_key(redisCluster *c, const char *key, int keylen, int *skeylen, short *slot) { char *skey; *skeylen = keylen + ZSTR_LEN(c->flags->prefix); skey = emalloc(*skeylen); memcpy(skey, ZSTR_VAL(c->flags->prefix), ZSTR_LEN(c->flags->prefix)); memcpy(skey + ZSTR_LEN(c->flags->prefix), key, keylen); *slot = cluster_hash_key(skey, *skeylen); return skey; } PS_OPEN_FUNC(rediscluster) { redisCluster *c; zval z_conf, *zv, *context; HashTable *ht_conf, *ht_seeds; double timeout = 0, read_timeout = 0; int persistent = 0, failover = REDIS_FAILOVER_NONE; zend_string *prefix = NULL, *user = NULL, *pass = NULL, *failstr = NULL; /* Parse configuration for session handler */ array_init(&z_conf); sapi_module.treat_data(PARSE_STRING, estrdup(save_path), &z_conf); /* We need seeds */ zv = REDIS_HASH_STR_FIND_TYPE_STATIC(Z_ARRVAL(z_conf), "seed", IS_ARRAY); if (zv == NULL) { zval_dtor(&z_conf); return FAILURE; } /* Grab a copy of our config hash table and keep seeds array */ ht_conf = Z_ARRVAL(z_conf); ht_seeds = Z_ARRVAL_P(zv); /* Optional configuration settings */ REDIS_CONF_DOUBLE_STATIC(ht_conf, "timeout", &timeout); REDIS_CONF_DOUBLE_STATIC(ht_conf, "read_timeout", &read_timeout); REDIS_CONF_BOOL_STATIC(ht_conf, "persistent", &persistent); /* Sanity check on our timeouts */ if (timeout < 0 || read_timeout < 0) { php_error_docref(NULL, E_WARNING, "Can't set negative timeout values in session configuration"); zval_dtor(&z_conf); return FAILURE; } REDIS_CONF_STRING_STATIC(ht_conf, "prefix", &prefix); REDIS_CONF_AUTH_STATIC(ht_conf, "auth", &user, &pass); REDIS_CONF_STRING_STATIC(ht_conf, "failover", &failstr); /* Need to massage failover string if we have it */ if (failstr) { if (zend_string_equals_literal_ci(failstr, "error")) { failover = REDIS_FAILOVER_ERROR; } else if (zend_string_equals_literal_ci(failstr, "distribute")) { failover = REDIS_FAILOVER_DISTRIBUTE; } } redisCachedCluster *cc; zend_string **seeds, *hash = NULL; uint32_t nseeds; #define CLUSTER_SESSION_CLEANUP() \ if (hash) zend_string_release(hash); \ if (failstr) zend_string_release(failstr); \ if (prefix) zend_string_release(prefix); \ if (user) zend_string_release(user); \ if (pass) zend_string_release(pass); \ free_seed_array(seeds, nseeds); \ zval_dtor(&z_conf); \ /* Extract at least one valid seed or abort */ seeds = cluster_validate_args(timeout, read_timeout, ht_seeds, &nseeds, NULL); if (seeds == NULL) { php_error_docref(NULL, E_WARNING, "No valid seeds detected"); CLUSTER_SESSION_CLEANUP(); return FAILURE; } c = cluster_create(timeout, read_timeout, failover, persistent); if (prefix) { c->flags->prefix = zend_string_copy(prefix); } else { c->flags->prefix = CLUSTER_DEFAULT_PREFIX(); } redis_sock_set_auth(c->flags, user, pass); if ((context = REDIS_HASH_STR_FIND_TYPE_STATIC(ht_conf, "stream", IS_ARRAY)) != NULL) { redis_sock_set_stream_context(c->flags, context); } /* First attempt to load from cache */ if (CLUSTER_CACHING_ENABLED()) { hash = cluster_hash_seeds(seeds, nseeds); if ((cc = cluster_cache_load(hash))) { cluster_init_cache(c, cc); goto success; } } /* Initialize seed array, and attempt to map keyspace */ cluster_init_seeds(c, seeds, nseeds); if (cluster_map_keyspace(c) != SUCCESS) goto failure; /* Now cache our cluster if caching is enabled */ if (hash) cluster_cache_store(hash, c->nodes); success: CLUSTER_SESSION_CLEANUP(); PS_SET_MOD_DATA(c); return SUCCESS; failure: CLUSTER_SESSION_CLEANUP(); cluster_free(c, 1); return FAILURE; } /* {{{ PS_CREATE_SID_FUNC */ PS_CREATE_SID_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; char *cmd, *skey; zend_string *sid; int cmdlen, skeylen; int retries = 3; short slot; if (!c) { return php_session_create_id(NULL); } if (INI_INT("session.use_strict_mode") == 0) { return php_session_create_id((void **) &c); } while (retries-- > 0) { sid = php_session_create_id((void **) &c); /* Create session key if it doesn't already exist */ skey = cluster_session_key(c, ZSTR_VAL(sid), ZSTR_LEN(sid), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL, &cmd, "SET", "ssssd", skey, skeylen, "", 0, "NX", 2, "EX", 2, session_gc_maxlifetime()); efree(skey); /* Attempt to kick off our command */ c->readonly = 0; if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { php_error_docref(NULL, E_NOTICE, "Redis connection not available"); efree(cmd); zend_string_release(sid); return php_session_create_id(NULL);; } efree(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 1); if (!reply || c->err) { php_error_docref(NULL, E_NOTICE, "Unable to read redis response"); } else if (reply->len > 0) { cluster_free_reply(reply, 1); break; } else { php_error_docref(NULL, E_NOTICE, "Redis sid collision on %s, retrying %d time(s)", sid->val, retries); } if (reply) { cluster_free_reply(reply, 1); } zend_string_release(sid); sid = NULL; } return sid; } /* }}} */ /* {{{ PS_VALIDATE_SID_FUNC */ PS_VALIDATE_SID_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; char *cmd, *skey; int cmdlen, skeylen; int res = FAILURE; short slot; /* Check key is valid and whether it already exists */ if (php_session_valid_key(ZSTR_VAL(key)) == FAILURE) { php_error_docref(NULL, E_NOTICE, "Invalid session key: %s", ZSTR_VAL(key)); return FAILURE; } skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXISTS", "s", skey, skeylen); efree(skey); /* We send to master, to ensure consistency */ c->readonly = 0; if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { php_error_docref(NULL, E_NOTICE, "Redis connection not available"); efree(cmd); return FAILURE; } efree(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); if (!reply || c->err) { php_error_docref(NULL, E_NOTICE, "Unable to read redis response"); res = FAILURE; } else if (reply->integer == 1) { res = SUCCESS; } /* Clean up */ if (reply) { cluster_free_reply(reply, 1); } return res; } /* }}} */ /* {{{ PS_UPDATE_TIMESTAMP_FUNC */ PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; char *cmd, *skey; int cmdlen, skeylen; short slot; /* No need to update the session timestamp if we've already done so */ if (INI_INT("redis.session.early_refresh")) { return SUCCESS; } /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXPIRE", "sd", skey, skeylen, session_gc_maxlifetime()); efree(skey); /* Attempt to send EXPIRE command */ c->readonly = 0; if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry"); efree(cmd); return FAILURE; } /* Clean up our command */ efree(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; } /* Clean up */ cluster_free_reply(reply, 1); return SUCCESS; } /* }}} */ /* {{{ PS_READ_FUNC */ PS_READ_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; char *cmd, *skey; int cmdlen, skeylen, free_flag; short slot; /* Set up our command and slot information */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); /* Update the session ttl if early refresh is enabled */ if (INI_INT("redis.session.early_refresh")) { cmdlen = redis_spprintf(NULL, NULL, &cmd, "GETEX", "ssd", skey, skeylen, "EX", 2, session_gc_maxlifetime()); c->readonly = 0; } else { cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen); c->readonly = 1; } efree(skey); /* Attempt to kick off our command */ if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { efree(cmd); return FAILURE; } /* Clean up command */ efree(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; } /* Push reply value to caller */ if (reply->str == NULL) { *val = ZSTR_EMPTY_ALLOC(); } else { *val = zend_string_init(reply->str, reply->len, 0); } free_flag = 1; /* Clean up */ cluster_free_reply(reply, free_flag); /* Success! */ return SUCCESS; } /* {{{ PS_WRITE_FUNC */ PS_WRITE_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; char *cmd, *skey; int cmdlen, skeylen; short slot; /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL, &cmd, "SETEX", "sds", skey, skeylen, session_gc_maxlifetime(), ZSTR_VAL(val), ZSTR_LEN(val)); efree(skey); /* Attempt to send command */ c->readonly = 0; if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { efree(cmd); return FAILURE; } /* Clean up our command */ efree(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; } /* Clean up*/ cluster_free_reply(reply, 1); return SUCCESS; } /* {{{ PS_DESTROY_FUNC(rediscluster) */ PS_DESTROY_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; char *cmd, *skey; int cmdlen, skeylen; short slot; /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL, &cmd, "DEL", "s", skey, skeylen); efree(skey); /* Attempt to send command */ if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { efree(cmd); return FAILURE; } /* Clean up our command */ efree(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; } /* Clean up our reply */ cluster_free_reply(reply, 1); return SUCCESS; } /* {{{ PS_CLOSE_FUNC */ PS_CLOSE_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); if (c) { cluster_free(c, 1); PS_SET_MOD_DATA(NULL); } return SUCCESS; } /* {{{ PS_GC_FUNC */ PS_GC_FUNC(rediscluster) { return SUCCESS; } #endif /* vim: set tabstop=4 expandtab: */ redis-6.0.2/redis_session.h0000644000175000000120000000115514515245367016444 0ustar pyatsukhnenkowheel#ifndef REDIS_SESSION_H #define REDIS_SESSION_H #ifdef PHP_SESSION #include "ext/session/php_session.h" PS_OPEN_FUNC(redis); PS_CLOSE_FUNC(redis); PS_READ_FUNC(redis); PS_WRITE_FUNC(redis); PS_DESTROY_FUNC(redis); PS_GC_FUNC(redis); PS_CREATE_SID_FUNC(redis); PS_VALIDATE_SID_FUNC(redis); PS_UPDATE_TIMESTAMP_FUNC(redis); PS_OPEN_FUNC(rediscluster); PS_CLOSE_FUNC(rediscluster); PS_READ_FUNC(rediscluster); PS_WRITE_FUNC(rediscluster); PS_DESTROY_FUNC(rediscluster); PS_GC_FUNC(rediscluster); PS_CREATE_SID_FUNC(rediscluster); PS_VALIDATE_SID_FUNC(rediscluster); PS_UPDATE_TIMESTAMP_FUNC(rediscluster); #endif #endif redis-6.0.2/redis_sentinel.c0000644000175000000120000000701314515245367016574 0ustar pyatsukhnenkowheel/* +----------------------------------------------------------------------+ | Copyright (c) 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. | +----------------------------------------------------------------------+ | Author: Pavlo Yatsukhnenko | | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ #include "php_redis.h" #include "redis_commands.h" #include "redis_sentinel.h" #include zend_class_entry *redis_sentinel_ce; extern zend_class_entry *redis_exception_ce; #if PHP_VERSION_ID < 80000 #include "redis_sentinel_legacy_arginfo.h" #else #include "zend_attributes.h" #include "redis_sentinel_arginfo.h" #endif PHP_MINIT_FUNCTION(redis_sentinel) { /* RedisSentinel class */ redis_sentinel_ce = register_class_RedisSentinel(); redis_sentinel_ce->create_object = create_sentinel_object; return SUCCESS; } PHP_METHOD(RedisSentinel, __construct) { HashTable *opts = NULL; redis_sentinel_object *sentinel; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(opts) ZEND_PARSE_PARAMETERS_END_EX(RETURN_THROWS()); sentinel = PHPREDIS_ZVAL_GET_OBJECT(redis_sentinel_object, getThis()); sentinel->sock = redis_sock_create(ZEND_STRL("127.0.0.1"), 26379, 0, 0, 0, NULL, 0); if (opts != NULL && redis_sock_configure(sentinel->sock, opts) != SUCCESS) { RETURN_THROWS(); } sentinel->sock->sentinel = 1; } PHP_METHOD(RedisSentinel, ckquorum) { REDIS_PROCESS_KW_CMD("ckquorum", redis_sentinel_str_cmd, redis_boolean_response); } PHP_METHOD(RedisSentinel, failover) { REDIS_PROCESS_KW_CMD("failover", redis_sentinel_str_cmd, redis_boolean_response); } PHP_METHOD(RedisSentinel, flushconfig) { REDIS_PROCESS_KW_CMD("flushconfig", redis_sentinel_cmd, redis_boolean_response); } PHP_METHOD(RedisSentinel, getMasterAddrByName) { REDIS_PROCESS_KW_CMD("get-master-addr-by-name", redis_sentinel_str_cmd, redis_mbulk_reply_raw); } PHP_METHOD(RedisSentinel, master) { REDIS_PROCESS_KW_CMD("master", redis_sentinel_str_cmd, redis_mbulk_reply_zipped_raw); } PHP_METHOD(RedisSentinel, masters) { REDIS_PROCESS_KW_CMD("masters", redis_sentinel_cmd, sentinel_mbulk_reply_zipped_assoc); } PHP_METHOD(RedisSentinel, myid) { REDIS_PROCESS_KW_CMD("myid", redis_sentinel_cmd, redis_string_response); } PHP_METHOD(RedisSentinel, ping) { REDIS_PROCESS_KW_CMD("ping", redis_empty_cmd, redis_boolean_response); } PHP_METHOD(RedisSentinel, reset) { REDIS_PROCESS_KW_CMD("reset", redis_sentinel_str_cmd, redis_long_response); } PHP_METHOD(RedisSentinel, sentinels) { REDIS_PROCESS_KW_CMD("sentinels", redis_sentinel_str_cmd, sentinel_mbulk_reply_zipped_assoc); } PHP_METHOD(RedisSentinel, slaves) { REDIS_PROCESS_KW_CMD("slaves", redis_sentinel_str_cmd, sentinel_mbulk_reply_zipped_assoc); } redis-6.0.2/redis_sentinel.h0000644000175000000120000000036214515245367016601 0ustar pyatsukhnenkowheel#ifndef REDIS_SENTINEL_H #define REDIS_SENTINEL_H #include "sentinel_library.h" #define PHP_REDIS_SENTINEL_VERSION "1.0" extern zend_class_entry *redis_sentinel_ce; extern PHP_MINIT_FUNCTION(redis_sentinel); #endif /* REDIS_SENTINEL_H */ redis-6.0.2/redis_sentinel.stub.php0000644000175000000120000000200714515245367020113 0ustar pyatsukhnenkowheelsock) { redis_sock_disconnect(obj->sock, 0, 1); redis_free_socket(obj->sock); } zend_object_std_dtor(&obj->std); } zend_object * create_sentinel_object(zend_class_entry *ce) { redis_sentinel_object *obj = ecalloc(1, sizeof(*obj) + zend_object_properties_size(ce)); zend_object_std_init(&obj->std, ce); object_properties_init(&obj->std, ce); memcpy(&redis_sentinel_object_handlers, zend_get_std_object_handlers(), sizeof(redis_sentinel_object_handlers)); redis_sentinel_object_handlers.offset = XtOffsetOf(redis_sentinel_object, std); redis_sentinel_object_handlers.free_obj = free_redis_sentinel_object; obj->std.handlers = &redis_sentinel_object_handlers; return &obj->std; } PHP_REDIS_API int sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[4096]; int i, nelem; size_t len; zval z_ret; /* Throws exception on failure */ if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { RETVAL_FALSE; return FAILURE; } if (*inbuf != TYPE_MULTIBULK) { if (*inbuf == TYPE_ERR) { redis_sock_set_err(redis_sock, inbuf + 1, len - 1); } RETVAL_FALSE; return FAILURE; } array_init(&z_ret); nelem = atoi(inbuf + 1); for (i = 0; i < nelem; ++i) { /* redis_mbulk_reply_zipped_raw calls redis_mbulk_reply_zipped * which puts result into return_value via RETVAL_ZVAL */ redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); add_next_index_zval(&z_ret, return_value); } RETVAL_ZVAL(&z_ret, 0, 1); return SUCCESS; } redis-6.0.2/sentinel_library.h0000644000175000000120000000057714515245367017147 0ustar pyatsukhnenkowheel#ifndef REDIS_SENTINEL_LIBRARY_H #define REDIS_SENTINEL_LIBRARY_H #include "common.h" #include "library.h" typedef redis_object redis_sentinel_object; zend_object *create_sentinel_object(zend_class_entry *ce); PHP_REDIS_API int sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); #endif /* REDIS_SENTINEL_LIBRARY_H */