* Tighten up `assertTrue` and `assertFalse` such that they test that
passed arguments `===` `true` and `===` `false` respectively, instead
of testing for truth-like or false-like.
* Start modernizing our unit tests to use explicit types for arguments,
return types, member variables, etc.
* Multiple assertion fixes that were exposed when making `assertTrue`
and `assertFalse` more explicit.
* Some formatting cleanup to style for incorrect indentation, etc, that
had crept in over many years.
* Add some more assertion helpers like `assertNull`, `assertGT`,
`assertGTE`, `assertLT`, and `assertLTE`.
Our tests have a ton of instances where we do something like:
```php
$this->assert(TRUE === $this->redis->command1());
$this->assert($this->redis->command2() === 42);
```
Which should be written like this:
```php
$this->assertTrue($this->command1());
$this->assertEquals(42, $this->command2());
```
Additionally it changes some assertions to use more relevant assertions
like `assertInArray` rather than `assertTrue(in_array())`.
* Add `assertEqualsCanonicalizing` assertion similar to what PHPUnit
has.
* Add `assertStringContains` helper assertion.
* Refactor session tests
* Update these external scripts to take formal arguments with `getopt` to
make it more straightforward what each of the currently positional
arguments are actually for.
* Create small helper classes for invoking these external scripts.
Instead of `startSessionProcess` that takes a dozen argument all but
three of which have defaults, we can use a construct like this:
```php
$runner = $this->sessionRunner()
->maxExecutionTime(300)
->lockingEnabled(true)
->lockWaitTime(-1)
->lockExpires(0)
->data($data)
->compression($name);
// Invokes startSession.php with above args.
$result = $runner->execFg();
// Invokes regenerateSessionId.php with above args
$new_id = $runner->regenerateId();
// Invokes getSessionData.php for this session ID.
$data = $runner->getData();
```
* Add a bit of logic to TestSuite to dump more information about the
source of an assertion to make it easier to track down problems when
we assert outside of a top level public `test_*` method.
* Create a few new assertions like `assertKeyExists` and
`assertKeyMissing` which will generate much nicer assertions as
opposed to
```php
$this->assertTrue($this->redis->exists($some_key));
```
* If our externally spawned session scripts fail output the exact call
that was made along with all arguments as well as the output that we
received to make it easier to narrow down.
* snake_case -> camelCase
* Add compression support for PHP Sessions
Previously, compression was available for standard data but not for
session handling. This update enables the compression of PHP sessions,
allowing for more efficient Redis memory usage.
* Move session compress/uncompress logic to helper functions
* Change session_compress_data to always set the out arguments and adjust PS_READ_FUNC
Every so often our tests will fail because we attempt to interact with
one of the daemonized server instances before it has enough time to
actually start.
Usually this happens when we try to use the cli tool to execute
`--cluster-create`, but it can occur elsewhere as well.
This commit adds a step that waits for every instance that we started to
actually be up before trying to create the cluster and run subsequent
unit tests.
Additionally it switches from `$(seq a b)` to the `{a..b}` brace
expansion.
Add Valkey to our server matrix in addition to making the jobs a bit
more efficient by only installing the specific server we're testing on
each run.
For now we allow tests to fail against Valkey as they don't yet have an
official release. Once there is an official release we'll remove the
`continue-on-error` setting for Valkey.
Previously, the redis.session.early_refresh feature was implemented for
Redis Cluster, utilizing GETEX for the initial session read to minimize
the number of commands sent to the Redis server. However, this enhancement
was not applied to non-cluster sessions. This update addresses this
discrepancy, ensuring consistent behavior between Redis and Redis Cluster.
When a node timeout occurs, then phpredis will try to connect to another
node, whose answer probably will be MOVED redirect. After this we need
more time to accomplish the redirection, otherwise we get "Timed out
attempting to find data in the correct node" error message.
Fixes#795#888#1142#1385#1633#1707#1811#2407
Mention support for KeyDB in README.md.
Remove credit for Owlient from the first paragraph. Owlient was acquired by Ubisoft in 2011, so presumably no longer benefit from such prominent credit.
This commit fixes our unit tests so they also pass against the KeyDB
server. We didn't ned to change all that much. Most of it was just
adding a version/keydb check.
The only change to PhpRedis itself was to relax the reply requirements
for XAUTOCLAIM. Redis 7.0.0 added a third "these elements were recently
removed" reply which KeyDB does not have.
Fixes#2466
Replace `SOCKET_WRITE_COMMAND` with `redis_sock_write` because it can't be used
with pre-defined commands (it frees memory in case of failed writing operation).
After replacement `SOCKET_WRITE_COMMAND` becomes redundant so remove it.
PHP 8.4 has some breaking changes with respect to where PHP's random methods and
helpers are. This commit fixes those issues while staying backward compatible.
Fixes#2463
We also need to update the `RedisCluster` logic to handle very large
curosr values, in addition to handling them for the `Redis` and
`RedisArray` classes.
See #2454, #2458
Technically Redis may return any unsigned 64 bit integer as a scan
cursor. This presents a problem for PHP in that PHP's integers are
signed. Because of that if a scan cursor is > 2^63 it will overflow and
fail to work properly.
This commit updates our SCAN family of commands to deliver cursors in
their string form.
```php
public function scan(null|int|string $iterator, ...);
```
On initial entry into our SCAN family we convert either a NULL or empty
string cursor to zero, and send the initial scan command.
As Redis replies with cursors we either represent them as a long (if
they are <= ZEND_ULONG_MAX) and as a string if greater. This should
mean the fix is minimally breaking as the following code will still
work:
```php
$it = NULL;
do {
print_r($redis->scan($it));
} while ($it !== 0);
```
The `$it !== 0` still works because the zero cursor will be represented
as an integer. Only absurdly large (> 2^63) values are represented as a
string.
Fixes#2454
We actually had two different bits of logic to handle EXPIRY values in
the `SET` command. One for the legacy `SET` -> `SETEX` mapping and
another for the newer `SET foo bar EX <expiry>`.
Additionally the error message could be confusing. Passing 3.1415 for
an `EX` expiry would fail as we didn't allow floats.
This commit consolidates expiry parsing to our existing helper function
as well as improves the `php_error_docref` warning in the event that the
user passes invalid data. The warning will now tell the user the type
they tried to pass as an EXPIRY to make it easier to track down what's
going wrong.
Fixes#2448
* Add what value failed to pass our callback assertion so we can see
what we actually got from the server.
* WAITAOF requires Redis >= 7.2.0 so don't run it if the server is older
than that.