Refactor session tests (#2492)

* 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
This commit is contained in:
Michael Grunder
2024-05-23 09:43:36 -07:00
committed by GitHub
parent d68c30f87d
commit b88e72b1e6
7 changed files with 862 additions and 588 deletions
+14 -16
View File
@@ -22,15 +22,6 @@ class Redis_Cluster_Test extends Redis_Test {
];
protected static $_arr_node_map = [];
/**
* @var string
*/
protected $sessionPrefix = 'PHPREDIS_CLUSTER_SESSION:';
/**
* @var string
*/
protected $sessionSaveHandler = 'rediscluster';
/* Tests we'll skip all together in the context of RedisCluster. The
* RedisCluster class doesn't implement specialized (non-redis) commands
@@ -709,12 +700,14 @@ class Redis_Cluster_Test extends Redis_Test {
public function testSession()
{
@ini_set('session.save_handler', 'rediscluster');
@ini_set('session.save_path', $this->getFullHostPath() . '&failover=error');
@ini_set('session.save_path', $this->sessionSavePath() . '&failover=error');
if (!@session_start()) {
return $this->markTestSkipped();
}
session_write_close();
$this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id()));
$this->assertKeyExists($this->sessionPrefix() . session_id());
}
@@ -748,16 +741,21 @@ class Redis_Cluster_Test extends Redis_Test {
ini_set('redis.pconnect.pooling_enabled', $prev_value);
}
protected function sessionPrefix(): string {
return 'PHPREDIS_CLUSTER_SESSION:';
}
protected function sessionSaveHandler(): string {
return 'rediscluster';
}
/**
* @inheritdoc
*/
protected function getFullHostPath()
{
$auth = $this->getAuthFragment();
protected function sessionSavePath(): string {
return implode('&', array_map(function ($host) {
return 'seed[]=' . $host;
}, self::$_arr_node_map)) . ($auth ? "&$auth" : '');
}, self::$_arr_node_map)) . '&' . $this->getAuthFragment();
}
/* Test correct handling of null multibulk replies */
+242 -446
View File
@@ -1,6 +1,7 @@
<?php defined('PHPREDIS_TESTRUN') or die("Use TestRedis.php to run tests!\n");
require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");
require_once(dirname($_SERVER['PHP_SELF'])."/SessionHelpers.php");
class Redis_Test extends TestSuite
{
@@ -23,16 +24,6 @@ class Redis_Test extends TestSuite
*/
public $redis;
/**
* @var string
*/
protected $sessionPrefix = 'PHPREDIS_SESSION:';
/**
* @var string
*/
protected $sessionSaveHandler = 'redis';
protected function getNilValue() {
return FALSE;
}
@@ -105,40 +96,31 @@ class Redis_Test extends TestSuite
}
}
protected function getAuthFragment() {
static $_authidx = 0;
$_authidx++;
protected function sessionPrefix(): string {
return 'PHPREDIS_SESSION:';
}
protected function sessionSaveHandler(): string {
return 'redis';
}
protected function sessionSavePath(): string {
return sprintf("tcp://%s:%d?%s", $this->getHost(), $this->getPort(),
$this->getAuthFragment());
}
protected function getAuthFragment() {
$this->getAuthParts($user, $pass);
if ($user && $pass) {
if ($_authidx % 2 == 0)
return "auth[user]=$user&auth[pass]=$pass";
else
return "auth[]=$user&auth[]=$pass";
return sprintf("auth[user]=%s&auth[pass]=%s", $user, $pass);
} else if ($pass) {
if ($_authidx % 3 == 0)
return "auth[pass]=$pass";
if ($_authidx % 2 == 0)
return "auth[]=$pass";
else
return "auth=$pass";
return sprintf("auth[pass]=%s", $pass);
} else {
return NULL;
return '';
}
}
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(),
@@ -7377,218 +7359,272 @@ return;
}
}
protected function sessionRunner() {
$this->getAuthParts($user, $pass);
return (new SessionHelpers\Runner())
->prefix($this->sessionPrefix())
->handler($this->sessionSaveHandler())
->savePath($this->sessionSavePath());
}
public function testSession_compression() {
$this->setSessionHandler();
foreach ($this->getCompressors() as $name => $val) {
$data = "testing_compression_$name";
$id = $this->generateSessionId();
$res = $this->startSessionProcess($id, 0, false, 300, true, null,
-1, 0, "testing_compression_$name", 1440,
$name);
$runner = $this->sessionRunner()
->maxExecutionTime(300)
->lockingEnabled(true)
->lockWaitTime(-1)
->lockExpires(0)
->data($data)
->compression($name);
$this->assertTrue($res);
$key = $this->sessionPrefix . $id;
$this->assertEquals('SUCCESS', $runner->execFg());
$this->redis->setOption(Redis::OPT_COMPRESSION, $val);
$this->assertTrue($this->redis->get($key) !== false);
$this->assertPatternMatch($this->redis->get($runner->getSessionKey()), "/.*$data.*/");
$this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
}
}
public function testSession_savedToRedis()
{
$this->setSessionHandler();
$runner = $this->sessionRunner();
$sessionId = $this->generateSessionId();
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
$this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId));
$this->assertTrue($sessionSuccessful);
$this->assertEquals('SUCCESS', $runner->execFg());
$this->assertKeyExists($this->redis, $runner->getSessionKey());
}
public function testSession_lockKeyCorrect()
{
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
protected function sessionWaitUsec() {
return ini_get('redis.session.lock_wait_time') *
ini_get('redis.session.lock_retries');
}
$this->startSessionProcess($sessionId, 5, true);
protected function sessionWaitSec() {
return $this->sessionWaitUsec() / 1000000.0;
}
$maxwait = (ini_get('redis.session.lock_wait_time') *
ini_get('redis.session.lock_retries') /
1000000.00);
public function testSession_lockKeyCorrect() {
$runner = $this->sessionRunner()->sleep(5);
$exist = $this->waitForSessionLockKey($sessionId, $maxwait);
$this->assertTrue($exist);
$this->assertTrue($runner->execBg());
if ( ! $runner->waitForLockKey($this->redis, $this->sessionWaitSec())) {
$this->externalCmdFailure($runner->getCmd(), $runner->output(),
"Failed waiting for session lock key '{$runner->getSessionLockKey()}'",
$runner->getExitCode());
}
}
public function testSession_lockingDisabledByDefault()
{
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
$this->startSessionProcess($sessionId, 5, true, 300, false);
usleep(100000);
$runner = $this->sessionRunner()
->lockingEnabled(false)
->sleep(5);
$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);
$this->assertEquals('SUCCESS', $runner->execFg());
$this->assertKeyMissing($this->redis, $runner->getSessionLockKey());
}
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'));
$runner = $this->sessionRunner()
->sleep(1)
->lockingEnabled(true);
$this->assertTrue($runner->execBg());
usleep($this->sessionWaitUsec() + 100000);
$this->assertKeyMissing($this->redis, $runner->getSessionLockKey());
}
public function testSession_lock_ttlMaxExecutionTime()
{
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
$this->startSessionProcess($sessionId, 10, true, 2);
$runner1 = $this->sessionRunner()
->sleep(10)
->maxExecutionTime(2);
$this->assertTrue($runner1->execBg());
usleep(100000);
$start = microtime(true);
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
$end = microtime(true);
$elapsedTime = $end - $start;
$runner2 = $this->sessionRunner()
->id($runner1->getId())
->sleep(0);
$this->assertLess($elapsedTime, 4);
$this->assertTrue($sessionSuccessful);
$st = microtime(true);
$this->assertEquals('SUCCESS', $runner2->execFg());
$el = microtime(true) - $st;
$this->assertLess($el, 4);
}
public function testSession_lock_ttlLockExpire()
{
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
$this->startSessionProcess($sessionId, 10, true, 300, true, null, -1, 2);
$runner1 = $this->sessionRunner()
->sleep(10)
->maxExecutionTime(300)
->lockingEnabled(true)
->lockExpires(2);
$this->assertTrue($runner1->execBg());
usleep(100000);
$start = microtime(true);
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
$end = microtime(true);
$elapsedTime = $end - $start;
$runner2 = $this->sessionRunner()
->id($runner1->getId())
->sleep(0);
$this->assertTrue($elapsedTime < 3);
$this->assertTrue($sessionSuccessful);
$st = microtime(true);
$this->assertEquals('SUCCESS', $runner2->execFg());
$this->assertLess(microtime(true) - $st, 3);
}
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);
public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() {
$id = 'test-id';
$this->assertTrue($writeSuccessful);
$this->assertEquals('secondProcess', $this->getSessionData($sessionId));
$runner = $this->sessionRunner()
->sleep(2)
->lockingEnabled(true)
->lockExpires(1)
->data('firstProcess');
$runner2 = $this->sessionRunner()
->id($runner->getId())
->sleep(0)
->lockingEnabled(true)
->lockExpires(10)
->data('secondProcess');
$this->assertTrue($runner->execBg());
usleep(1500000); // 1.5 sec
$this->assertEquals('SUCCESS', $runner2->execFg());
$this->assertEquals($runner->getData(), 'secondProcess');
}
public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock()
{
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
$writeSuccessful = $this->startSessionProcess($sessionId, 2, false, 300, true, null, -1, 1, 'firstProcess');
$runner = $this->sessionRunner()
->sleep(2)
->lockingEnabled(true)
->lockExpires(1)
->data('firstProcess');
$this->assertFalse($writeSuccessful);
$this->assertTrue('firstProcess' !== $this->getSessionData($sessionId));
$this->assertNotEquals('SUCCESS', $runner->execFg());
$this->assertNotEquals('firstProcess', $runner->getData());
}
public function testSession_correctLockRetryCount() {
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
$runner = $this->sessionRunner()
->sleep(10);
/* Start another process and wait until it has the lock */
$this->startSessionProcess($sessionId, 10, true);
if ( ! $this->waitForSessionLockKey($sessionId, 2)) {
$this->assertTrue(false);
return;
$this->assertTrue($runner->execBg());
if ( ! $runner->waitForLockKey($this->redis, 2)) {
$this->externalCmdFailure($runner->getCmd(), $runner->output(),
"Failed waiting for session lock key",
$runner->getExitCode());
}
$tm1 = microtime(true);
$ok = $this->startSessionProcess($sessionId, 0, false, 10, true, 100000, 10);
if ( ! $this->assertFalse($ok)) return;
$tm2 = microtime(true);
$runner2 = $this->sessionRunner()
->id($runner->getId())
->sleep(0)
->maxExecutionTime(10)
->lockingEnabled(true)
->lockWaitTime(100000)
->lockRetries(10);
$this->assertTrue($tm2 - $tm1 >= 1 && $tm2 - $tm1 <= 3);
$st = microtime(true);
$ex = $runner2->execFg();
if (stripos($ex, 'SUCCESS') !== false) {
$this->externalCmdFailure($runner2->getCmd(), $ex,
"Expected failure but lock was acquired!",
$runner2->getExitCode());
}
$et = microtime(true);
$this->assertBetween($et - $st, 1, 3);
}
public function testSession_defaultLockRetryCount()
{
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
$this->startSessionProcess($sessionId, 10, true);
public function testSession_defaultLockRetryCount() {
$runner = $this->sessionRunner()
->sleep(10);
$keyname = $this->sessionPrefix . $sessionId . '_LOCK';
$begin = microtime(true);
$runner2 = $this->sessionRunner()
->id($runner->getId())
->sleep(0)
->lockingEnabled(true)
->maxExecutionTime(10)
->lockWaitTime(20000)
->lockRetries(0);
if ( ! $this->waitForSessionLockKey($sessionId, 3)) {
$this->assertTrue(false);
return;
$this->assertTrue($runner->execBg());
if ( ! $runner->waitForLockKey($this->redis, 3)) {
$this->externalCmdFailure($runner->getCmd(), $runner->output(),
"Failed waiting for session lock key",
$runner->getExitCode());
}
$start = microtime(true);
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 20000, 0);
$end = microtime(true);
$elapsedTime = $end - $start;
$this->assertBetween($elapsedTime, 2, 3);
$this->assertFalse($sessionSuccessful);
$st = microtime(true);
$this->assertNotEquals('SUCCESS', $runner2->execFg());
$et = microtime(true);
$this->assertBetween($et - $st, 2, 3);
}
public function testSession_noUnlockOfOtherProcess()
{
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
$st = microtime(true);
$sleep = 3;
$runner = $this->sessionRunner()
->sleep($sleep)
->maxExecutionTime(3);
$tm1 = 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);
$this->assertTrue($runner->execBg());
if ( ! $runner->waitForLockKey($this->redis, 1)) {
$this->assert("Failed waiting for session lock key");
return;
}
/* 2. Attempt to lock the same session. This should force us to
* wait until the first lock is released. */
$runner2 = $this->sessionRunner()
->id($runner->getId())
->sleep(0);
$tm2 = microtime(true);
$ok = $this->startSessionProcess($sessionId, 0, false);
$this->assertEquals('SUCCESS', $runner2->execFg());
$tm3 = microtime(true);
/* 3. Verify that we did in fact have to wait for this lock */
$this->assertTrue($ok);
$this->assertTrue($tm3 - $tm2 >= $nsec - ($tm2 - $tm1));
/* 3. Verify we had to wait for this lock */
$this->assertTrue($tm3 - $tm2 >= $sleep - ($tm2 - $tm1));
}
public function testSession_lockWaitTime()
{
$this->setSessionHandler();
$sessionId = $this->generateSessionId();
$this->startSessionProcess($sessionId, 1, true, 300);
public function testSession_lockWaitTime() {
$runner = $this->sessionRunner()
->sleep(1)
->maxExecutionTime(300);
$runner2 = $this->sessionRunner()
->id($runner->getId())
->sleep(0)
->maxExecutionTime(300)
->lockingEnabled(true)
->lockWaitTime(3000000);
$this->assertTrue($runner->execBg());
usleep(100000);
$start = microtime(true);
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, 3000000);
$end = microtime(true);
$elapsedTime = $end - $start;
$st = microtime(true);
$this->assertEquals('SUCCESS', $runner2->execFg());
$et = microtime(true);
$this->assertTrue($elapsedTime > 2.5);
$this->assertTrue($elapsedTime < 3.5);
$this->assertTrue($sessionSuccessful);
$this->assertBetween($et - $st, 2.5, 3.5);
}
public function testMultipleConnect() {
@@ -7729,322 +7765,82 @@ return;
$this->assertFalse(@$this->redis->setOption(pow(2, 32), false));
}
protected function regenerateIdHelper(bool $lock, bool $destroy, bool $proxy) {
$data = uniqid('regenerate-id:');
$runner = $this->sessionRunner()
->sleep(0)
->maxExecutionTime(300)
->lockingEnabled(true)
->lockRetries(1)
->data($data);
$this->assertEquals('SUCCESS', $runner->execFg());
$new_id = $runner->regenerateId($lock, $destroy, $proxy);
$this->assertNotEquals($runner->getId(), $new_id);
$this->assertEquals($runner->getData(), $runner->getData());
}
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));
$this->regenerateIdHelper(false, false, false);
}
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_noLock_withDestroy() {
$this->regenerateIdHelper(false, true, false);
}
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_noDestroy() {
$this->regenerateIdHelper(true, false, false);
}
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));
$this->regenerateIdHelper(true, true, false);
}
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));
$this->regenerateIdHelper(false, false, true);
}
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));
$this->regenerateIdHelper(false, true, true);
}
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));
$this->regenerateIdHelper(true, false, true);
}
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));
$this->regenerateIdHelper(true, true, true);
}
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);
$runner = $this->sessionRunner()->lifetime(600);
$this->assertEquals('SUCCESS', $runner->execFg());
$this->assertEquals(600, $this->redis->ttl($runner->getSessionKey()));
}
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);
$runner1 = $this->sessionRunner()->lifetime(600);
$this->assertEquals('SUCCESS', $runner1->execFg());
$this->assertEquals(600, $ttl);
$runner2 = $this->sessionRunner()->id($runner1->getId())->lifetime(1800);
$this->assertEquals('SUCCESS', $runner2->execFg());
$this->assertEquals(1800, $this->redis->ttl($runner2->getSessionKey()));
}
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);
public function testSession_ttl_resetOnRead() {
$data = uniqid(__FUNCTION__);
$this->assertEquals(600, $ttl);
}
$runner = $this->sessionRunner()->lifetime(600)->data($data);
$this->assertEquals('SUCCESS', $runner->execFg());
$this->redis->expire($runner->getSessionKey(), 9999);
private function setSessionHandler()
{
$host = $this->getHost() ?: 'localhost';
@ini_set('session.save_handler', $this->sessionSaveHandler);
@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
* @param string $sessionCompression
*
* @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,
$sessionCompression = 'none')
{
if (strpos(php_uname(), 'Windows') !== false)
$this->markTestSkipped();
$commandParameters = [
$this->getFullHostPath(), $this->sessionSaveHandler, $sessionId,
$sleepTime, $maxExecutionTime, $lock_retries, $lock_expires,
$sessionData, $sessionLifetime, $locking_enabled ? 1 : 0,
$lock_wait_time ?? 0, $sessionCompression
];
$commandParameters = array_map('escapeshellarg', $commandParameters);
$commandParameters[] = $background ? '>/dev/null 2>&1 &' : '2>&1';
$command = self::getPhpCommand('startSession.php') . implode(' ', $commandParameters);
exec($command, $output);
if ($background)
return true;
$result = $output[0] == 'SUCCESS';
// var_dump(['command' => $command, 'output' => $output, 'result' => $result]);
return $result;
}
/**
* @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);
$test_args = getenv('TEST_PHP_ARGS');
if ($test_args !== false) {
$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 . ' ';
$this->assertEquals($data, $runner->getData());
$this->assertEquals(600, $this->redis->ttl($runner->getSessionKey()));
}
}
?>
+353
View File
@@ -0,0 +1,353 @@
<?php
namespace SessionHelpers;
class PhpSpawner {
protected static function appendPhpArgs(string $php): string {
$modules = shell_exec("$php --no-php-ini -m");
/* Determine if we need to specifically add extensions */
$extensions = array_filter(
['igbinary', 'msgpack', 'json', 'redis'],
function ($module) use ($modules) {
return strpos($modules, $module) === false;
}
);
/* If any are needed add them to the command */
if ($extensions) {
$php .= ' --no-php-ini';
foreach ($extensions as $extension) {
/* We want to use the locally built redis extension */
if ($extension == 'redis') {
$path = dirname(__DIR__) . '/modules/redis';
if (is_file("{$path}.so"))
$extension = $path;
}
$php .= " -dextension=$extension.so";
}
}
return $php;
}
/**
* 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
*/
public static function cmd(string $script): string {
static $cmd = NULL;
if ( ! $cmd) {
$cmd = getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY;
if ($test_args = getenv('TEST_PHP_ARGS')) {
$cmd .= ' ' . $test_args;
} else {
$cmd = self::appendPhpArgs($cmd);
}
}
return $cmd . ' ' . __DIR__ . '/' . $script . ' ';
}
}
class Runner {
const start_script = 'startSession.php';
const regenerate_id_script = 'regenerateSessionId.php';
const get_data_script = 'getSessionData.php';
private $required = ['host', 'handler', 'id'];
private $args = [
'handler' => null,
'save-path' => null,
'id' => null,
'sleep' => 0,
'max-execution-time' => 300,
'locking-enabled' => true,
'lock-wait-time' => null,
'lock-retries' => -1,
'lock-expires' => 0,
'data' => '',
'lifetime' => 1440,
'compression' => 'none',
];
private $prefix = NULL;
private $output_file;
private $exit_code = -1;
private $cmd = NULL;
private $pid;
private $output;
public function __construct() {
$this->args['id'] = $this->createId();
}
public function getExitCode(): int {
return $this->exit_code;
}
public function getCmd(): ?string {
return $this->cmd;
}
public function getId(): ?string {
return $this->args['id'];
}
public function prefix(string $prefix): self {
$this->prefix = $prefix;
return $this;
}
public function getSessionKey(): string {
return $this->prefix . $this->getId();
}
public function getSessionLockKey(): string {
return $this->getSessionKey() . '_LOCK';
}
protected function set($setting, $v): self {
$this->args[$setting] = $v;
return $this;
}
public function handler(string $handler): self {
return $this->set('handler', $handler);
}
public function savePath(string $path): self {
return $this->set('save-path', $path);
}
public function id(string $id): self {
return $this->set('id', $id);
}
public function sleep(int $sleep): self {
return $this->set('sleep', $sleep);
}
public function maxExecutionTime(int $time): self {
return $this->set('max-execution-time', $time);
}
public function lockingEnabled(bool $enabled): self {
return $this->set('locking-enabled', $enabled);
}
public function lockWaitTime(int $time): self {
return $this->set('lock-wait-time', $time);
}
public function lockRetries(int $retries): self {
return $this->set('lock-retries', $retries);
}
public function lockExpires(int $expires): self {
return $this->set('lock-expires', $expires);
}
public function data(string $data): self {
return $this->set('data', $data);
}
public function lifetime(int $lifetime): self {
return $this->set('lifetime', $lifetime);
}
public function compression(string $compression): self {
return $this->set('compression', $compression);
}
protected function validateArgs(array $required) {
foreach ($required as $req) {
if ( ! isset($this->args[$req]) || $this->args[$req] === null)
throw new \Exception("Command requires '$req' arg");
}
}
private function createId(): string {
if (function_exists('session_create_id'))
return session_create_id();
return uniqid();
}
private function getTmpFileName() {
return '/tmp/sessiontmp.txt';
return tempnam(sys_get_temp_dir(), 'session');
}
/*
* @param $client Redis client
* @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
*/
public function waitForLockKey($redis, $max_wait_sec) {
$now = microtime(true);
do {
if ($redis->exists($this->getSessionLockKey()))
return true;
usleep(10000);
} while (microtime(true) <= $now + $max_wait_sec);
return false;
}
private function appendCmdArgs(array $args): string {
$append = [];
foreach ($args as $arg => $val) {
if ( ! $val)
continue;
if (is_string($val))
$val = escapeshellarg($val);
$append[] = "--$arg";
$append[] = $val;
}
return implode(' ', $append);
}
private function buildPhpCmd(string $script, array $args): string {
return PhpSpawner::cmd($script) . ' ' . $this->appendCmdArgs($args);
}
private function startSessionCmd(): string {
return $this->buildPhpCmd(self::start_script, $this->args);
}
public function output(?int $timeout = NULL): ?string {
if ($this->output) {
var_dump("early return");
return $this->output;
}
if ( ! $this->output_file || ! $this->pid) {
throw new \Exception("Process was not started in the background");
}
$st = microtime(true);
do {
if (pcntl_waitpid($this->pid, $exit_code, WNOHANG) == 0)
break;
usleep(100000);
} while ((microtime(true) - $st) < $timeout);
if ( ! file_exists($this->output_file))
return "";
$this->output = file_get_contents($this->output_file);
$this->output_file = NULL;
$this->exit_code = $exit_code;
$this->pid = NULL;
return $this->output;
}
public function execBg(): bool {
if ($this->cmd)
throw new \Exception("Command already executed!");
$output_file = $this->getTmpFileName();
$this->cmd = $this->startSessionCmd();
$this->cmd .= " >$output_file 2>&1 & echo $!";
$pid = exec($this->cmd, $output, $exit_code);
$this->exit_code = $exit_code;
if ($this->exit_code || !is_numeric($pid))
return false;
$this->pid = (int)$pid;
$this->output_file = $output_file;
return true;
}
public function execFg() {
if ($this->cmd)
throw new \Exception("Command already executed!");
$this->cmd = $this->startSessionCmd() . ' 2>&1';
exec($this->cmd, $output, $exit_code);
$this->exit_code = $exit_code;
$this->output = implode("\n", array_filter($output));
return $this->output;
}
private function regenerateIdCmd($locking, $destroy, $proxy): string {
$this->validateArgs(['handler', 'id', 'save-path']);
$args = [
'handler' => $this->args['handler'],
'save-path' => $this->args['save-path'],
'id' => $this->args['id'],
'locking-enabled' => !!$locking,
'destroy' => !!$destroy,
'proxy' => !!$proxy,
];
return $this->buildPhpCmd(self::regenerate_id_script, $args);
}
public function regenerateId($locking = false, $destroy = false, $proxy = false) {
if ( ! $this->cmd)
throw new \Exception("Cannot regenerate id before starting session!");
$cmd = $this->regenerateIdCmd($locking, $destroy, $proxy);
exec($cmd, $output, $exit_code);
if ($exit_code != 0)
return false;
return $output[0];
}
private function getDataCmd(?int $lifetime): string {
$this->validateArgs(['handler', 'save-path', 'id']);
$args = [
'handler' => $this->args['handler'],
'save-path' => $this->args['save-path'],
'id' => $this->args['id'],
'lifetime' => is_int($lifetime) ? $lifetime : $this->args['lifetime'],
];
return $this->buildPhpCmd(self::get_data_script, $args);
}
public function getData(?int $lifetime = NULL): string {
$cmd = $this->getDataCmd($lifetime);
exec($cmd, $output, $exit_code);
if ($exit_code != 0) {
return implode("\n", $output);
}
return $output[0];
}
}
+160 -63
View File
@@ -43,19 +43,6 @@ class TestSuite
public function getPort() { return $this->i_port; }
public function getAuth() { return $this->auth; }
/**
* 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
@@ -80,13 +67,84 @@ class TestSuite
: $str_msg;
}
protected function printArg($v) {
if (is_null($v))
return '(null)';
else if ($v === false || $v === true)
return $v ? '(true)' : '(false)';
else if (is_string($v))
return "'$v'";
else
return print_r($v, true);
}
protected function findTestFunction($bt) {
$i = 0;
while (isset($bt[$i])) {
if (substr($bt[$i]['function'], 0, 4) == 'test')
return $bt[$i]['function'];
$i++;
}
return NULL;
}
protected function assertionTrace(?string $fmt = NULL, ...$args) {
$prefix = 'Assertion failed:';
$lines = [];
$bt = debug_backtrace();
$msg = $fmt ? vsprintf($fmt, $args) : NULL;
$fn = $this->findTestFunction($bt);
$lines []= sprintf("%s %s - %s", $prefix, self::make_bold($fn),
$msg ? $msg : '(no message)');
array_shift($bt);
for ($i = 0; $i < count($bt); $i++) {
$file = $bt[$i]['file'];
$line = $bt[$i]['line'];
$fn = $bt[$i+1]['function'] ?? $bt[$i]['function'];
$lines []= sprintf("%s %s:%d (%s)%s",
str_repeat(' ', strlen($prefix)), $file, $line,
$fn, $msg ? " $msg" : '');
if (substr($fn, 0, 4) == 'test')
break;
}
return implode("\n", $lines) . "\n";
}
protected function assert($fmt, ...$args) {
self::$errors []= $this->assertionTrace($fmt, ...$args);
}
protected function assertFalse($bool) {
if(!$bool)
if( ! $bool)
return true;
self::$errors []= $this->assertionTrace();
return false;
}
protected function assertKeyExists($redis, $key) {
if ($redis->exists($key))
return true;
$bt = debug_backtrace(false);
self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n",
$bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
self::$errors []= $this->assertionTrace("Key '%s' does not exist.", $key);
return false;
}
protected function assertKeyMissing($redis, $key) {
if ( ! $redis->exists($key))
return true;
self::$errors []= $this->assertionTrace("Key '%s' exists but shouldn't.", $key);
return false;
}
@@ -95,40 +153,42 @@ class TestSuite
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);
self::$errors []= $this->assertionTrace($msg);
return false;
}
protected function assertInArray($ele, $arr, $cb = NULL) {
if ($cb && !is_callable($cb))
die("Fatal: assertInArray callback must be callable!\n");
protected function assertInArray($ele, $arr, ?callable $cb = NULL) {
$cb ??= function ($v) { return true; };
if (($in = in_array($ele, $arr)) && (!$cb || $cb($arr[array_search($ele, $arr)])))
$key = array_search($ele, $arr);
if ($key !== false && ($valid = $cb($ele)))
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);
self::$errors []= $this->assertionTrace("%s %s %s", $this->printArg($ele),
$key === false ? 'missing from' : 'is invalid in',
$this->printArg($arr));
return false;
}
protected function assertArrayKey($arr, $key, $cb = NULL) {
if ($cb && !is_callable($cb))
die("Fatal: assertArrayKey callback must be callable\n");
protected function assertArrayKey($arr, $key, callable $cb = NULL) {
$cb ??= function ($v) { return true; };
if (($exists = isset($arr[$key])) && (!$cb || $cb($arr[$key])))
if (($exists = isset($arr[$key])) && $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);
if ($exists) {
$msg = sprintf("%s is invalid in %s", $this->printArg($arr[$key]),
$this->printArg($arr));
} else {
$msg = sprintf("%s is not a key in %s", $this->printArg($key),
$this->printArg($arr));
}
self::$errors []= $this->assertionTrace($msg);
return false;
}
@@ -140,9 +200,7 @@ class TestSuite
if ($cb($val))
return true;
$bt = debug_backtrace(false);
self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n--- VALUE ---\n%s\n",
$bt[0]["file"], $bt[0]["line"], $bt[1]["function"], print_r($val, true));
self::$errors []= $this->assertionTrace("%s is invalid.", $this->printArg($val));
return false;
}
@@ -163,62 +221,101 @@ class TestSuite
if ($threw && $match)
return true;
$bt = debug_backtrace(false);
// $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);
self::$errors []= $this->assertionTrace("[$ex]");
//
return false;
}
protected function assertLess($a, $b) {
if($a < $b)
return;
return true;
self::$errors []= $this->assertionTrace("%s >= %s", $a, $b);
return false;
}
protected function assertMore($a, $b) {
if($a > $b)
return true;
self::$errors [] = $this->assertionTrace("%s <= %s", $a, $b);
return false;
}
protected function externalCmdFailure($cmd, $output, $msg = NULL, $exit_code = NULL) {
$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"]);
$lines[] = sprintf("Assertion failed: %s:%d (%s)",
$bt[0]['file'], $bt[0]['line'],
self::make_bold($bt[0]['function']));
if ($msg)
$lines[] = sprintf(" Message: %s", $msg);
if ($exit_code !== NULL)
$lines[] = sprintf(" Exit code: %d", $exit_code);
$lines[] = sprintf( " Command: %s", $cmd);
if ($output)
$lines[] = sprintf(" Output: %s", $output);
self::$errors[] = implode("\n", $lines) . "\n";
}
protected function assertBetween($value, $min, $max, bool $exclusive = false) {
if ($exclusive) {
if ($value > $min && $value < $max)
return;
return true;
} else {
if ($value >= $min && $value <= $max)
return;
return true;
}
$bt = debug_backtrace(false);
self::$errors []= sprintf("Assertion failed (%s not between %s and %s): %s:%d (%s)\n",
print_r($value, true), print_r($min, true), print_r($max, true),
$bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
self::$errors []= $this->assertionTrace(sprintf("'%s' not between '%s' and '%s'",
$value, $min, $max));
return false;
}
protected function assertEquals($a, $b) {
if($a === $b)
return;
return true;
$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"]);
self::$errors[] = $this->assertionTrace("%s !== %s", $this->printArg($a),
$this->printArg($b));
return false;
}
public function assertNotEquals($a, $b) {
if($a !== $b)
return true;
self::$errors []= $this->assertionTrace("%s === %s", $this->printArg($a),
$this->printArg($b));
return false;
}
protected function assertPatternMatch($str_test, $str_regex) {
if (preg_match($str_regex, $str_test))
return;
return true;
$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"]);
self::$errors []= $this->assertionTrace("'%s' doesnt match '%s'",
$str_test, $str_regex);
return false;
}
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);
$bt[0]["file"], $bt[0]["line"],
$bt[1]["function"], $msg);
throw new TestSkippedException($msg);
}
+22 -11
View File
@@ -1,22 +1,33 @@
<?php
require_once __DIR__ . '/SessionHelpers.php';
error_reporting(E_ERROR | E_WARNING);
$redisHost = $argv[1];
$saveHandler = $argv[2];
$sessionId = $argv[3];
$sessionLifetime = $argv[4];
$opt = getopt('', ['handler:', 'save-path:', 'id:', 'lifetime:']);
if (empty($redisHost)) {
$redisHost = 'tcp://localhost:6379';
$handler = $opt['handler'] ?? NULL;
$save_path = $opt['save-path'] ?? NULL;
$id = $opt['id'] ?? NULL;
$lifetime = $opt['lifetime'] ?? NULL;
if ( ! $handler) {
fprintf(STDERR, "--handler is required\n");
exit(1);
} else if ( ! $save_path) {
fprintf(STDERR, "--save-path is required\n");
exit(1);
}
ini_set('session.save_handler', $saveHandler);
ini_set('session.save_path', $redisHost);
ini_set('session.gc_maxlifetime', $sessionLifetime);
ini_set('session.save_handler', $handler);
ini_set('session.save_path', $save_path);
ini_set('session.gc_maxlifetime', $lifetime);
session_id($sessionId);
session_id($id);
if (!session_start()) {
echo "session_start() was nut successful";
fprintf(STDERR, "session_start() was nut successful");
exit(1);
} else {
echo isset($_SESSION['redis_test']) ? $_SESSION['redis_test'] : 'Key redis_test not found';
exit(0);
}
+30 -23
View File
@@ -1,23 +1,30 @@
<?php
error_reporting(E_ERROR | E_WARNING);
$redisHost = $argv[1];
$saveHandler = $argv[2];
$sessionId = $argv[3];
$locking = !!$argv[4];
$destroyPrevious = !!$argv[5];
$sessionProxy = !!$argv[6];
$opt = getopt('', [
'handler:', 'save-path:', 'id:', 'locking-enabled:',
'destroy-previous:', 'proxy:'
]);
if (empty($redisHost)) {
$redisHost = 'tcp://localhost:6379';
$handler = $opt['handler'] ?? NULL;
$save_path = $opt['save-path'] ?? NULL;
$id = $opt['id'] ?? NULL;
$locking_enabled = !!($opt['locking-enabled'] ?? false);
$destroy_previous = !!($opt['destroy-previous'] ?? false);
$proxy = !!($opt['proxy'] ?? false);
if ( ! $handler) {
fprintf(STDERR, "--handler is required\n");
exit(1);
} else if ( ! $save_path) {
fprintf(STDERR, "--save-path is required\n");
exit(1);
}
ini_set('session.save_handler', $saveHandler);
ini_set('session.save_path', $redisHost);
if ($locking) {
ini_set('redis.session.locking_enabled', true);
}
ini_set('session.save_handler', $handler);
ini_set('session.save_path', $save_path);
ini_set('redis.session.locking_enabled', $locking_enabled);
if (interface_exists('SessionHandlerInterface')) {
class TestHandler implements SessionHandlerInterface
@@ -64,21 +71,21 @@ if (interface_exists('SessionHandlerInterface')) {
}
}
if ($sessionProxy) {
if ($proxy) {
$handler = new TestHandler();
session_set_save_handler($handler);
}
session_id($sessionId);
session_id($id);
if (!session_start()) {
$result = "FAILED: session_start()";
}
elseif (!session_regenerate_id($destroyPrevious)) {
$result = "FAILED: session_regenerate_id()";
}
else {
} else if (!session_regenerateId($destroy_previous)) {
$result = "FAILED: session_regenerateId()";
} else {
$result = session_id();
}
session_write_close();
echo $result;
session_write_close();
echo $result;
+41 -29
View File
@@ -1,40 +1,52 @@
<?php
error_reporting(E_ERROR | E_WARNING);
$redisHost = $argv[1];
$saveHandler = $argv[2];
$sessionId = $argv[3];
$sleepTime = $argv[4];
$maxExecutionTime = $argv[5];
$lock_retries = $argv[6];
$lock_expire = $argv[7];
$sessionData = $argv[8];
$sessionLifetime = $argv[9];
$lockingEnabled = $argv[10];
$lockWaitTime = $argv[11];
$sessionCompression = $argv[12];
$opt = getopt('', [
'handler:', 'save-path:', 'id:', 'sleep:', 'max-execution-time:' ,
'locking-enabled:', 'lock-wait-time:', 'lock-retries:', 'lock-expires:',
'data:', 'lifetime:', 'compression:'
]);
if (empty($redisHost)) {
$redisHost = 'tcp://localhost:6379';
$handler = $opt['handler'] ?? NULL;
$save_path = $opt['save-path'] ?? NULL;
$id = $opt['id'] ?? NULL;
$sleep = $opt['sleep'] ?? 0;
$max_execution_time = $opt['max-execution-time'] ?? 0;
$lock_retries = $opt['lock-retries'] ?? 0;
$lock_expire = $opt['lock-expires'] ?? 0;
$data = $opt['data'] ?? NULL;
$lifetime = $opt['lifetime'] ?? 0;
$locking_enabled = $opt['locking-enabled'] ?? NULL;
$lock_wait_time = $opt['lock-wait-time'] ?? 0;
$compression = $opt['compression'] ?? NULL;
if ( ! $handler) {
fprintf(STDERR, "--handler is required\n");
exit(1);
} else if ( ! $save_path) {
fprintf(STDERR, "--save-path is required\n");
exit(1);
}
ini_set('session.save_handler', $saveHandler);
ini_set('session.save_path', $redisHost);
ini_set('max_execution_time', $maxExecutionTime);
ini_set("{$saveHandler}.session.lock_retries", $lock_retries);
ini_set("{$saveHandler}.session.lock_expire", $lock_expire);
ini_set('session.gc_maxlifetime', $sessionLifetime);
ini_set("{$saveHandler}.session.locking_enabled", $lockingEnabled);
ini_set("{$saveHandler}.session.lock_wait_time", $lockWaitTime);
ini_set('redis.session.compression', $sessionCompression);
ini_set('session.save_handler', $handler);
ini_set('session.save_path', $save_path);
ini_set('max_execution_time', $max_execution_time);
ini_set("{$handler}.session.lock_retries", $lock_retries);
ini_set("{$handler}.session.lock_expire", $lock_expire);
ini_set('session.gc_maxlifetime', $lifetime);
ini_set("{$handler}.session.locking_enabled", $locking_enabled);
ini_set("{$handler}.session.lock_wait_time", $lock_wait_time);
ini_set('redis.session.compression', $compression);
session_id($sessionId);
$sessionStartSuccessful = session_start();
sleep($sleepTime);
if (!empty($sessionData)) {
$_SESSION['redis_test'] = $sessionData;
session_id($id);
$status = session_start();
sleep($sleep);
if ($data) {
$_SESSION['redis_test'] = $data;
}
session_write_close();
echo $sessionStartSuccessful ? 'SUCCESS' : 'FAILURE';
echo $status ? 'SUCCESS' : 'FAILURE';