diff --git a/src/Server/Session/Session.php b/src/Server/Session/Session.php index e02fcc7c..b2e91ba0 100644 --- a/src/Server/Session/Session.php +++ b/src/Server/Session/Session.php @@ -22,23 +22,20 @@ class Session implements SessionInterface { /** - * @param array $data Stores all session data. - * Keys are snake_case by convention for MCP-specific data. - * * Official keys are: * - initialized: bool * - client_info: array|null * - protocol_version: string|null * - log_level: string|null + * + * @var array */ + private array $data; + public function __construct( - protected SessionStoreInterface $store, - protected Uuid $id = new UuidV4(), - protected array $data = [], + private SessionStoreInterface $store, + private Uuid $id = new UuidV4(), ) { - if ($rawData = $this->store->read($this->id)) { - $this->data = json_decode($rawData, true) ?? []; - } } public function getId(): Uuid @@ -59,7 +56,7 @@ public function save(): bool public function get(string $key, mixed $default = null): mixed { $key = explode('.', $key); - $data = $this->data; + $data = $this->readData(); foreach ($key as $segment) { if (\is_array($data) && \array_key_exists($segment, $data)) { @@ -75,6 +72,7 @@ public function get(string $key, mixed $default = null): mixed public function set(string $key, mixed $value, bool $overwrite = true): void { $segments = explode('.', $key); + $this->readData(); $data = &$this->data; while (\count($segments) > 1) { @@ -94,7 +92,7 @@ public function set(string $key, mixed $value, bool $overwrite = true): void public function has(string $key): bool { $key = explode('.', $key); - $data = $this->data; + $data = $this->readData(); foreach ($key as $segment) { if (\is_array($data) && \array_key_exists($segment, $data)) { @@ -112,6 +110,7 @@ public function has(string $key): bool public function forget(string $key): void { $segments = explode('.', $key); + $this->readData(); $data = &$this->data; while (\count($segments) > 1) { @@ -143,7 +142,7 @@ public function pull(string $key, mixed $default = null): mixed public function all(): array { - return $this->data; + return $this->readData(); } public function hydrate(array $attributes): void @@ -156,4 +155,22 @@ public function jsonSerialize(): array { return $this->all(); } + + /** + * @return array + */ + private function readData(): array + { + if (isset($this->data)) { + return $this->data; + } + + $rawData = $this->store->read($this->id); + + if (false === $rawData) { + return $this->data = []; + } + + return $this->data = json_decode($rawData, true, flags: \JSON_THROW_ON_ERROR); + } } diff --git a/tests/Unit/Server/Session/SessionTest.php b/tests/Unit/Server/Session/SessionTest.php new file mode 100644 index 00000000..db331a05 --- /dev/null +++ b/tests/Unit/Server/Session/SessionTest.php @@ -0,0 +1,36 @@ +getMockBuilder(InMemorySessionStore::class) + ->disableOriginalConstructor() + ->onlyMethods(['read']) + ->getMock(); + $store->expects($this->once())->method('read')->willReturn(json_encode(['foo' => 'bar'])); + + $session = new Session($store); + $result = $session->all(); + $this->assertEquals(['foo' => 'bar'], $result); + + // Call again to make sure we dont read from Store + $result = $session->all(); + $this->assertEquals(['foo' => 'bar'], $result); + } +}