375 Commits

Author SHA1 Message Date
Pavlo Yatsukhnenko c2d2254e56 Switch to Fast Parameter Parsing API 2026-06-04 13:25:11 -07:00
michael-grunder 2c5ef19257 Introduce new RedisCmd based command construction
* Introduce a new `RedisCmd` struct to dynamically append RESP arguments
  such that we don't have to precalculate the number of arguments the
  command will have up front.

  Additionally the new `RedisCmd allows both a `void *` context pointer
  but also can attach a `void (*ctx_dtor)(void*)` destructor so we are
  still able to clean up any allocated context when commands fail.

  This moves the context cleanup out of every individual reply handler
  and into the generic processing wrappers.

* Create a small group of `resp_str` helper functions for lower level
  concatination of RESP protocol data over the wire.

* Lots of small modernization of the codebase such as using
  `zend_string*` instead of (`char *`, `size_t`) pairs.

* Greatly simplify `crosslot` handling logic
2026-06-04 12:16:35 -07:00
Arshid b8b29687c1 Update redis_commands.c
Co-authored-by: Michael Grunder <michael.grunder@gmail.com>
2026-05-01 09:11:14 -07:00
arshidkv12 fe67b8cb33 fix: fix: correct snprintf format specifier for long value 2026-05-01 09:11:14 -07:00
michael-grunder 5b19731649 Update GCRA optional argument from NUM_REQUESTS to TOKENS.
See: https://github.com/redis/redis/pull/14950
2026-04-08 09:33:00 -07:00
michael-grunder 10b77a42d6 Implement GCRA command 2026-03-25 11:03:42 -07:00
michael-grunder 93cb921158 Minor DELEX refactor
We had a `delExType` enum which was later made redundant by
`redisEqType` as they have the same semantics.

This commit just removes `delExType` and uses the new enum.
2026-02-02 00:17:43 -08:00
Pavlo Yatsukhnenko 7c1c125974 Fix issue with numeric strings as hash fields 2026-01-12 11:08:45 -08:00
michael-grunder 23cc785989 Implement XACKDEL 2025-11-18 10:10:48 -08:00
Niels Dossche d24528e190 Replace legacy zval_dtor() alias with zval_ptr_dtor_nogc() 2025-11-16 14:07:06 -08:00
Niels Dossche 8e1c2789a4 Replace legacy zval_is_true() alias with zend_is_true() 2025-11-16 14:07:06 -08:00
michael-grunder 635d87d535 Implement XDELEX command 2025-11-12 09:14:43 -08:00
michael-grunder 41922b6740 Implement MSETEX for RedisCluster
We also need to revisit the multi key command logic (MSET, DEL, and
friends). We may want to create a "per slot" distribution mechanism.
2025-11-12 08:41:21 -08:00
michael-grunder 86eabb86eb Implement MSETEX and refactor SET.
* Implement the new `MSETEX` Redis command.
* Refactor parsing of extended `SET` arguments into unified helper
  functions, deduplicating logic.
* Add new `SET` arguments `IFNE`, `IFDEQ`, and `IFDNE`. We already
  support Valkey's existing `IFEQ` argument, which Redis has added.

Fixes #2742
2025-11-12 08:41:21 -08:00
michael-grunder 00c62de277 Implement DELEX command 2025-11-06 09:48:09 -08:00
michael-grunder e2dd13ce7b Implement DIGEST and _digest helper (for php >= 8.1)
Redis implemented new CAS semantics which work both with values and the
XXH3 digest of those values.

This commit implements the Redis command itself and a helper which
computes the XXH3 digest locally. Note that we can only be sure to have
the `XXH3` hashing algorithm in PHP >= 8.1 so the `_digest` helper is
limited to PHP 8.1 or newer.
2025-11-06 09:48:09 -08:00
michael-grunder 0abe2c295e Fix HMGET where hash fields have an integer prefix
We were incorrectly ignoring errors when calling `is_numeric_string`,
meaning we would truncate hash fields that had integer prefixes.

```php
$redis->hmget('hash', ['123notaninteger']);
// Would actually execute:
// HMGET hash 123
```

Fixes #2731
2025-10-09 12:11:20 -07:00
michael-grunder 6ce3bd533a Implement VRANGE command and add a test 2025-10-02 11:12:39 -07:00
michael-grunder 35df8ad7c2 Attempt to fix an overflow bug in ZADD on Windows
Theory: In 64 bit windows `long` is 32 bits wide meaning that using a
long to append `ZADD` scores can truncate.

Possible fix for #2697
2025-09-09 16:30:43 -07:00
michael-grunder b8de91c9e0 Fix errors and a warning
* PHP < 8.0 took a `char*` as `php_json_decode` input, whereas newer
  versions take a const char * so ifdef around this.

* Fix compilation errors due to `false` not being defined. So as to make
  a minimal change we can just use 0 and 1
2025-09-01 09:41:12 -07:00
michael-grunder d80b725824 Implement VGETATTR command 2025-09-01 09:41:12 -07:00
michael-grunder 7f9b1f416e Implement VLINKS command 2025-09-01 09:41:12 -07:00
michael-grunder 92716ed0c5 Implement VSETATTR command 2025-09-01 09:41:12 -07:00
michael-grunder 65927b53b1 We can use redis_kv_cmd instead of a specific vrem command. 2025-09-01 09:41:12 -07:00
michael-grunder dc91631b3f Implement VREM command
See #2543
2025-09-01 09:41:12 -07:00
michael-grunder 1deca62841 Implement VRANDMEMBER
`VRANDMEMBER` has the exact same semantics of `SRANDMEMBER` so make
`SRANDMEMBER` a keyword based command and use it for `VRANDMEMBER`.

See #2543
2025-09-01 09:41:12 -07:00
michael-grunder 96378b70fd Implement VEMB and slightly rework VINFO
Unfortunately `VEMB` has a unique `RESP2` reply as far as I can tell,
where it sends the embedding mode (int8, bin, fp32) as a simple string.

This would cause any of PhpRedis' generic reply handlers to turn that
into `true` which isn't useful. For that reason we need a custom reply
handler.

Additionally slightly rework `VINFO` to short circuit and return failure
if we read anything other than a bulk string or an integer reply type.
Otherwise we may get out of sync on the socket.

See #2543
2025-09-01 09:41:12 -07:00
michael-grunder 659dc763e0 Refactor redis_replicaof_cmd
It was the last place we were using `REDIS_SPPRINTF` in
`redis_commands.c`.
2025-08-27 16:37:08 -07:00
michael-grunder 8d369f4d62 Implement GEOSEARCH[STORE] BYPOLYGON.
Valkey 9.0.0 implemented a new variant of `GEOSEARCH` where you supply
the verticies to an arbitrary polygon.

Since we can't modify the `geosearch` prototype using it is a little
wonky (you need to just pass empty strings for position and unit).

```php
$redis->geosearch('ca:cities', '', [
    -121.90, 39.65, -121.77, 39.65, -121.77, 39.80, -121.90, 39.80
], '');
$redis->geosearchstore('ca:cities', 'dst', '', [
    -121.90, 39.65, -121.77, 39.65, -121.77, 39.80, -121.90, 39.80
], '');
```
2025-08-24 06:37:51 -07:00
michael-grunder f4ec5e2bb0 Use ZEND_STRL instead of literal, len 2025-08-21 08:53:51 -07:00
michael-grunder 03837f0230 Remove pointless casts
You never have to explicitly cast between `void*` and any other pointer
type.
2025-08-21 08:53:51 -07:00
michael-grunder d564e8cf3c Fix dead assignment 2025-08-21 08:53:51 -07:00
michael-grunder 6e5faf4226 Refactor EVAL[SHA] command and add a regression test
* We can make the code simpler by using `zend_empty_array` when no args
  are passed as well as the new argument parsing macros and newer internal
  redis command appending functions that take zend strings.

* Add a regression test for when we execute `EVAL[SHA]` with arguments
  but do not send any keys. This was causing UB in RedisCluster (#2681).
2025-08-05 13:20:36 -07:00
michael-grunder f61e8cd7ba Fix RedisCluster segfault
We were previously only picking a random slot if the user didn't pass
any arguments at all whereas we want to pick a random slot if they don't
pass any *keys*.

This change just universally picks a random slot at the beginning and
then if any keys are processed those keys will override the random
selection.

Fixes #2681
2025-08-05 08:28:43 -07:00
michael-grunder d1d690053f Implement VSIM command
This command is similar to `VADD` in that it's pretty simple but allows
for a great many options.

In it's most basic form:

```php
// To get similarity of a different element
$redis->vsim('myvec', 'some-element');

// To get similarity for a vector of scores
```

As seen above the method attempts to infer element or vector from the
argument passed to $member`. However, since we do serialize the member
when doing `ELE` mode, the user can also specify `ELE` explicitly in the
options array to force an `ELE` search sending serialized values.

```php
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
$redis->vsim('myvec', [3.14, 2.71], ['ELE']);
```

See #2543
2025-07-31 08:30:47 -07:00
michael-grunder 286fa63064 Implement VADD command
This is for Redis 8.0's vector sets.

The command itself can be quite complex with all of the various options but
pretty simple using all defaults.

```php
$redis->vadd('myvec', [3.14, 2.17], 'myelement');
```

The implementation takes a default argument `$options` which can be an array in
order to specify the myriad of other knobs users can send. We just do a bit of
validation on inputs (e.g. certain numeric options must be positive) and make
sure the command is constructed in a valid way (e.g. REDUCE <dim> must come
before the floating point values).

By default we deliver `FP32` blobs but allow the user to send `VALUES` in the
options array which will cause PhpRedis to send N individual values. Sending
values is slower but might be nice for debugging (e.g. watching monitor)

See #2543
2025-07-31 00:57:28 -07:00
michael-grunder ca80ee0e67 Fix passing NULL for hash expiry argument
The stubs specify `?string $mode = NULL` but we were using `Z_PARAM_STR`
causing an arginfo mismatch.

Fixes #2674
2025-07-28 10:33:49 -07:00
Pavlo Yatsukhnenko a6922a07fe Update redis_commands.c
Co-authored-by: Michael Grunder <michael.grunder@gmail.com>
2025-07-24 09:38:01 -07:00
Pavlo Yatsukhnenko 6b2f088d49 Fix hset fields handling 2025-07-24 09:38:01 -07:00
Pavlo Yatsukhnenko 7805da7542 Fix HSetEx expiry argument handling 2025-07-24 09:08:33 -07:00
Michael Grunder ce5b0facc2 Implement HGETEX, HSETEX, HGETDEL, and refactor HMGET (#2667)
* Rework HMGET and implement HGETEX

Instead of using a bespoke NULL terminated `zval**` array for the
context array we can use a `HashTable`. This might be a tiny bit more
expensive but Zend hashtables are quite efficient and this should also
be less error prone.

* Rework our `HashTable` context array to store keys

Instead of sending an array of values we can instead add the fields as
keys to our context array. That way when we combine the keys with the
Redis provided values we can do it in-place and then just give the
HashTable to the user to then do with what they want.

* Implement HGETDEL command.

* Fix edge cases to abide by legacy behavior.

Previously we coerced integer strings into integer keys when zipping
`HMGET` responses. This commit adds logic so we continue to do this and
do not change semantics.

* Implement `HGETDEL` and `HGETEX` for `RedisCluster`.

This commit implements the new commands and reworks the `HMGET` reply
handler to use the new context `HashTable`.

* Fix an edge case where we get zero multiblk elements

* Tests for `HGETEX` and `HGETDEL`

* Minor logic improvement

We don't need to check if `c->reply_len > 0` in the last else block
since we have already determined it must be.

* Implement `HSETEX` for `Redis` and `RedisCluster`

* Use `zval_get_tmp_string` ro populating non-long keys
2025-07-16 16:46:09 -07:00
michael-grunder 7350768cd9 Implement several hash expiration commands
Commands implemented:

`H[P]EXPIRE`
`H[P]TTL`
`H[P]EXPIREAT`
`H[P]EXPIRETIME`
`HPERSIST`
2025-05-07 08:16:14 -07:00
Jakub Onderka 0a85bd824a Simplify redis_unpack method calling
This method always unpack given string to zval, so it is not necessary to check output value
2025-04-02 13:02:52 -07:00
Pavlo Yatsukhnenko f73f5fcce5 Fix arguments order for SET command
Redis and Valkey doesn't consider command as invalid if order of arguments
is changed but other servers like DragonflyDB does.
In this commit `SET` command is fixed to more strictly follow the specs.
Also fixed usage of `zend_tmp_string` for `ifeq` argument.
2025-03-16 12:10:53 -07:00
michael-grunder f9ce9429ef Introduce Redis::OPT_PACK_IGNORE_NUMBERS option.
Adds an option that instructs PhpRedis to not serialize or compress
numeric values. Specifically where `Z_TYPE_P(z) == IS_LONG` or
`Z_TYPE_P(z) == IS_DOUBLE`.

This flag lets the user enable serialization and/or compression while
still using the various increment/decrement command (`INCR`, `INCRBY`,
`DECR`, `DECRBY`, `INCRBYFLOAT`, `HINCRBY`, and `HINCRBYFLOAT`).

Because PhpRedis can't be certain that this option was enabled when
writing keys, there is a small runtime cost on the read-side that tests
whether or not the value its reading is a pure integer or floating point
value.

See #23
2025-02-05 14:12:42 -08:00
michael-grunder a2eef77f44 Implement Valkey >= 8.1 IFEQ set option
Implement the new `IFEQ` `SET` option that will be included in `Valkey`
8.1.

See: valkey-io/valkey#1324
2025-01-20 08:04:27 -08:00
Bentley O'Kane-Chase 35c5988027 Formatting improvements 2024-12-17 17:08:36 -08:00
Bentley O'Kane-Chase 138d07b67c Print cursor as unsigned 64 bit integer 2024-12-17 17:08:36 -08:00
Jakub Onderka aba09933db Avoid unnecessary allocation in redis_hset_cmd
This will slightly reduce memory usage for HSET command
2024-11-26 18:28:09 -08:00
Jakub Onderka 4082dd07f7 Avoid unnecessary allocation in redis_hdel_cmd
This will slightly reduce memory usage for HDEL command
2024-11-26 18:28:09 -08:00