897 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
michael-grunder 10b77a42d6 Implement GCRA command 2026-03-25 11:03:42 -07:00
michael-grunder e39d9b74f4 Modernize session locking
Add support for Redis' `DELEX` and Valkey's `DELIFEQ` when deleting the
session lock key. Local testing shows about a 10-15% improvement over
the current `EVAL[SHA]` strategy.

This commit adds a new INI settingg:
```ini
redis.session.lock_release_cmd = delex|delifeq|eval
```

By default we continue to use the `EVAL` logic and if a user specifies
another mechanism but the command doesn't exist, we warn the user and
fall back to EVAL.

This commit also refactors a few functions to avoid UB and simplify key
construction.
2026-03-01 14:52:39 -08:00
michael-grunder b97951cddc Rework TLS context logic
Instead of currying around a `php_stream_context` object, just retain
the context array provided by the user itself like we do with other
connection information like host and port. This lets users reconnect in
a loop without leaking memory.

```php
$redis = new \Redis;
while (true) {
    // Previously each reconnect call would leak the
    // `php_stream_context` structure.
    $redis->connect('tls://127.0.0.1', 9999, 1, null, 0, 0, [
        'stream' => ['verify_peer' => false, 'verify_peer_name' => false],
    ]);

    $redis->ping();

    $redis->close();
}
```
2026-02-18 09:46:55 -08:00
Niels Dossche 7d4fa6137c Replace legacy ZEND_WRONG_PARAM_COUNT() alias with zend_wrong_param_count() 2025-11-16 14:07:06 -08:00
Niels Dossche d24528e190 Replace legacy zval_dtor() alias with zval_ptr_dtor_nogc() 2025-11-16 14:07:06 -08:00
michael-grunder 635d87d535 Implement XDELEX command 2025-11-12 09:14:43 -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 6e214c1698 Cloning our objects should not segfault
In a future release we can actually implement cloning logic along with
lazy reconnection but for now, just throw a fatal error.

Fixes #1760
2025-10-31 11:09:45 -07:00
Pavlo Yatsukhnenko 7d3b2e4d6d Add hGetWithMeta method 2025-10-06 16:22:59 -07:00
michael-grunder 6ce3bd533a Implement VRANGE command and add a test 2025-10-02 11:12:39 -07:00
michael-grunder 92137ffd3f We actually do return bool in sismember so do the same here 2025-09-01 09:41:12 -07:00
michael-grunder 92dd256f98 Implement VISMEMBER command. 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 0fda9f293b Implement VCARD, VDIM, and VINFO
All of these commands have the same form `<cmd> key`. `VINFO` is a bit
of an outlier however that uses simple strings as opposed to bulk
strings for the key names, meaning we had to create a custom handler.

See #2543
2025-09-01 09:41:12 -07:00
michael-grunder b1b0c19142 Implement DELIFEQ command
Implement the command and add a test.
2025-08-24 06:37:28 -07:00
michael-grunder 9802fc0e46 Rework REDIS_SAVE_CALLBACK to be a function. 2025-08-21 08:53:51 -07:00
michael-grunder 58e1a04f76 Remove wrapper macro which hides branching logic
This wrapper macro implicitly defines an `} else {` block but this is
not clear at the callsite which obsures what is actually going on.

There's no real advantage to the wrapping macro. Instead just call the
underlying macro in an explicit else branch.
2025-08-21 08:53:51 -07:00
michael-grunder 8f0931bbed Rework REDIS_PROCESS_REQUEST to be a function. 2025-08-21 08:53:51 -07:00
michael-grunder 950d2bc79d Rework REDIS_PROCESS_KW_CMD as a function 2025-08-21 08:53:51 -07:00
michael-grunder 601ebbff2b Rework REDIS_PROCESS_CMD into a static function 2025-08-21 08:53:51 -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 75acbb0984 Remove unused macros + simplify some logic 2025-07-18 08:43:22 -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
Anton Smirnov 8dada174c4 Add an INI setting returning 5.x legacy behavior -- readonly session on lock failure 2025-07-09 20:07:57 -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
michael-grunder 593ba012ac Check for dragonfly_version in HELLO response
DragonflyDB will report to be Redis but also include `dragonfly_version`
in the hello response, which we can use to identify the fork.

Also fix parsing of the `HELLO` response for `serverName()` and
`serverVersion()`. Starting in Redis 8.0 there seem to always be modules
running, which the previous function was not expecting or parsing.
2025-05-05 09:31:11 -07:00
Michael Grunder 0445e683e7 Refactor getWithMeta logic (#2643)
* Refactor `getWithMeta`

* Consolidate `getWithMeta()` test.

* Review comments
2025-03-31 12:42:29 -07:00
Jakub Onderka 4f6a3ed1e7 New option 'database' for Redis class constructor (#2597)
* New option 'database' for Redis class constructor

Selecting database is very common action after connecting to Redis. This simplifies lazy connecting to Redis, when requested database will be selected after first command.

* More specific exception message when invalid auth or database number is provided

Before it was just 'Redis server went away'

* Rename reselect_db method to redis_select_db and slightly optimise it
2025-03-26 18:05:33 -07:00
Pavlo Yatsukhnenko cbaf095ff7 Allow calling methods only in atomic mode 2025-03-20 10:38:56 -07:00
Pavlo Yatsukhnenko 056c2dbee7 Introduce Redis::serverName and Redis::serverVersion methods
Right now we can't implement `HELLO` command to switch protocol
because we don't support new reply types that come with RESP3.
But we can use `HELLO` reply to expose some server information.
2025-03-20 10:38:56 -07:00
Pavlo Yatsukhnenko 9036ffca6a Add getWithMeta method 2025-02-25 16:27:10 +02:00
michael-grunder faa4bc2086 Don't cast a uint64_t to a long.
We recently updated PhpRedis to handle `SCAN` cursors > 2^63 as strings
(as internally PHP integers are longs).

However, the `redis_build_scan_cmd` took the cursor as a long, which
would overflow if the value was > `2^63`.

This commit simply changes the function to take a `uint64_t` and call
our specific `redis_append_sstr_u64` so we send the cursor to Redis
correctly.

Fixes #2454.
2025-01-13 12:13:44 -08:00
Jakub Onderka f68544f703 Refactor and avoid allocation in rawcommand method 2024-12-10 09:52:35 -08:00
Jakub Onderka a551fdc94c Switch from linked list to growing array for reply callbacks
Reduce allocation and deallocation count and also memory usage when using pipelining
2024-12-09 10:40:39 -08:00
Jakub Onderka 571ffbc8e0 Switch pipeline_cmd from smart_str to smart_string
As we don't need to extract zend_string from pipeline_cmd, we can use simple smart_string structure
2024-12-01 11:39:55 -08:00
Jakub Onderka 5156e03202 If no command is issued in multi mode, return immutable empty array 2024-11-26 10:39:23 -08:00
Jakub Onderka 60b5a8860a Use immutable empty array in Redis::exec 2024-11-26 10:39:23 -08:00
Jakub Onderka b665925eed Use smart str for constructing pipeline cmd 2024-11-25 09:41:27 -08:00
michael-grunder 4cd3f59356 Implement KeyDB's EXPIREMEMBER[AT] commands 2024-11-15 08:59:10 -08:00