Skip to content

Instantly share code, notes, and snippets.

@ArrayIterator
Last active March 19, 2026 01:53
Show Gist options
  • Select an option

  • Save ArrayIterator/74caf97b09de996c0876412237859537 to your computer and use it in GitHub Desktop.

Select an option

Save ArrayIterator/74caf97b09de996c0876412237859537 to your computer and use it in GitHub Desktop.
Stateless Session Implementation
<?php
declare(strict_types=1);
class SessionPayload {
public function __construct(
public string $token,
public int $userId,
public int $timestamp,
public int $expiredAt,
public string $random16 // binary string
) {}
public function isExpired(): bool {
$now = time();
return $this->timestamp <= 0 ||
$this->expiredAt <= 0 ||
$this->timestamp >= $now ||
$this->expiredAt <= $now;
}
public function isNeedRenew(int $renewDurationSecs): bool {
$now = time();
return $this->expiredAt > $now && ($this->expiredAt - $now) <= $renewDurationSecs;
}
public function __toString(): string {
return $this->token;
}
}
<?php
declare(strict_types=1);
class SessionTokenizer {
private string $combinedKey;
public const LENGTH = 208; // 104 bytes * 2 (hex)
public function __construct(string $secret, string $salt) {
$this->combinedKey = $secret . $salt;
}
public function generate(int $userId, int $durationSecs): SessionPayload {
$now = time();
$expiredAt = $now + $durationSecs;
// 1. Generate Random 16 bytes (seperti UUID v4 bytes)
$random16 = random_bytes(16);
// 2. Prepare Big-Endian Bytes
$user8 = pack('J', $userId); // 'J' = unsigned long 64-bit big-endian
$time8 = pack('J', $now);
$exp8 = pack('J', $expiredAt);
// 3. Inner Signature (Identity Binding)
$idBinding = hash_hmac('sha256', $user8, $this->combinedKey, true);
// 4. Construct Buffer (72 bytes payload)
// [0..16] random + [16..48] idBinding + [48..56] time + [56..64] exp + [64..72] user
$payloadPart = $random16 . $idBinding . $time8 . $exp8 . $user8;
// 5. Final Outer Sign (32 bytes signature)
$finalSign = hash_hmac('sha256', $payloadPart, $this->combinedKey, true);
// 6. Final Token (104 bytes -> 208 hex chars)
$tokenHex = bin2hex($payloadPart . $finalSign);
return new SessionPayload($tokenHex, $userId, $now, $expiredAt, $random16);
}
public function parse(string $tokenHex): ?SessionPayload {
if (strlen($tokenHex) !== self::LENGTH) {
return null; // throw Exception
}
$bytes = hex2bin($tokenHex);
if (!$bytes) return null;
// Split Buffer
$payloadPart = substr($bytes, 0, 72);
$outerSignature = substr($bytes, 72, 32);
// 1. Verify Outer Signature
$expectedOuterSign = hash_hmac('sha256', $payloadPart, $this->combinedKey, true);
if (!hash_equals($expectedOuterSign, $outerSignature)) {
return null; // Integrity check failed
}
// 2. Extract Components
$random16 = substr($payloadPart, 0, 16);
$innerSignature = substr($payloadPart, 16, 32);
// Unpack Big-Endian
$timestamp = unpack('J', substr($payloadPart, 48, 8))[1];
$expiredAt = unpack('J', substr($payloadPart, 56, 8))[1];
$user8 = substr($payloadPart, 64, 8);
$userId = unpack('J', $user8)[1];
// 3. Verify Inner Signature
$expectedInnerSign = hash_hmac('sha256', $user8, $this->combinedKey, true);
if (!hash_equals($expectedInnerSign, $innerSignature)) {
return null; // Identity binding failed
}
return new SessionPayload($tokenHex, $userId, $timestamp, $expiredAt, $random16);
}
}
#!/bin/env php
<?php
$tokenizer = new SessionTokenizer("super-secret-key", "some-salt");
$totalTests = 300;
$successCount = 0;
echo "πŸš€ Starting Stress Test: Generating and Parsing $totalTests Tokens..." . PHP_EOL;
echo str_repeat("-", 50) . PHP_EOL;
$startTime = microtime(true);
for ($i = 1; $i <= $totalTests; $i++) {
$uid = mt_rand(1, 9999999);
$duration = 3600;
$session = $tokenizer->generate($uid, $duration);
$parsed = $tokenizer->parse($session->token);
if ($parsed && $parsed->userId === $uid) {
$successCount++;
if ($i % 50 === 0) {
echo "βœ… Processed $i tokens... (Latest UID: $uid)" . PHP_EOL;
}
} else {
echo "❌ FAILED at loop $i for UID: $uid" . PHP_EOL;
}
}
$endTime = microtime(true);
$executionTime = round($endTime - $startTime, 4);
echo str_repeat("-", 50) . PHP_EOL;
echo "πŸ“Š SUMMARY REPORT" . PHP_EOL;
echo "Total Iterations : $totalTests" . PHP_EOL;
echo "Successful Parse : $successCount / $totalTests" . PHP_EOL;
echo "Execution Time : $executionTime seconds" . PHP_EOL;
echo "Avg Time/Token : " . ($executionTime / $totalTests) . "s" . PHP_EOL;
if ($successCount === $totalTests) {
echo "πŸŽ‰ RESULT: 100% Consistent! Logic Rust-to-PHP Porting Safe!." . PHP_EOL;
} else {
echo "⚠️ RESULT: Mismatch logic, there are invalid byte pack/unpack!" . PHP_EOL;
}
@ArrayIterator
Copy link
Copy Markdown
Author

Example Usage

$tokenizer = new SessionTokenizer("super-secret-key", "some-salt");

// Generate token untuk User ID 123 selama 1 jam
$session = $tokenizer->generate(123, 3600);
echo "Token: " . $session->token . PHP_EOL;

// Parse balik
$parsed = $tokenizer->parse($session->token);
if ($parsed && !$parsed->isExpired()) {
    echo "User ID: " . $parsed->userId;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment