From aaee4523a565a897582045618f5b74a8dbd53cf8 Mon Sep 17 00:00:00 2001 From: Yani Date: Wed, 21 Dec 2022 06:45:33 +0100 Subject: [PATCH 1/3] add ArrayAccess to Session class + throw error on invalid property retrieval --- src/Session.php | 68 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/Session.php b/src/Session.php index 02db40e..72cee55 100644 --- a/src/Session.php +++ b/src/Session.php @@ -5,9 +5,10 @@ namespace Compwright\PhpSession; use Countable; +use ArrayAccess; use RuntimeException; -class Session implements Countable +class Session implements ArrayAccess, Countable { protected string $name; @@ -50,7 +51,15 @@ public function __get(string $name) throw new RuntimeException('Session not initialized'); } +<<<<<<< HEAD // @phpstan-ignore-next-line +======= + if (!isset($this->contents[$name])) { + \trigger_error('Undefined property: ' . self::class . '::$' . $name, \E_USER_WARNING); + return null; + } + +>>>>>>> 75ce8a0 (add ArrayAccess to Session class + throw error on invalid property retrieval) return $this->contents[$name]; } @@ -90,10 +99,67 @@ public function __unset(string $name): void throw new RuntimeException('Cannot alter session after it is closed'); } + if (!isset($this->contents[$name])) { + \trigger_error('Undefined property: ' . self::class . '::$' . $name, \E_USER_WARNING); + return; + } + + $this->modified = true; + unset($this->contents[$name]); + } + + public function offsetSet($name, $value): void + { + if (!$this->isInitialized()) { + throw new RuntimeException('Session not initialized'); + } + + if (!$this->writeable) { + throw new RuntimeException('Cannot alter session after it is closed'); + } + + $this->modified = true; + $this->contents[$name] = $value; + } + + public function offsetExists($name): bool + { + if (!$this->isInitialized()) { + throw new RuntimeException('Session not initialized'); + } + + return isset($this->contents[$name]); + } + + public function offsetUnset($name): void + { + if (!$this->isInitialized()) { + throw new RuntimeException('Session not initialized'); + } + + if (!$this->writeable) { + throw new RuntimeException('Cannot alter session after it is closed'); + } + + if (!isset($this->contents[$name])) { + \trigger_error('Undefined array key "' . $name . '"', \E_USER_WARNING); + return; + } + $this->modified = true; unset($this->contents[$name]); } + public function offsetGet($name): mixed + { + if (!isset($this->contents[$name])) { + \trigger_error('Undefined array key "' . $name . '"', \E_USER_WARNING); + return null; + } + + return $this->contents[$name]; + } + /** * @param ?array $contents */ From 1e4f15dc959d68808d5aaa2ff43d85b6099d25d1 Mon Sep 17 00:00:00 2001 From: Yani Date: Fri, 23 Dec 2022 05:16:19 +0100 Subject: [PATCH 2/3] remove custom errors in Session::class --- src/Session.php | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/Session.php b/src/Session.php index 72cee55..0077a42 100644 --- a/src/Session.php +++ b/src/Session.php @@ -51,15 +51,7 @@ public function __get(string $name) throw new RuntimeException('Session not initialized'); } -<<<<<<< HEAD // @phpstan-ignore-next-line -======= - if (!isset($this->contents[$name])) { - \trigger_error('Undefined property: ' . self::class . '::$' . $name, \E_USER_WARNING); - return null; - } - ->>>>>>> 75ce8a0 (add ArrayAccess to Session class + throw error on invalid property retrieval) return $this->contents[$name]; } @@ -99,11 +91,6 @@ public function __unset(string $name): void throw new RuntimeException('Cannot alter session after it is closed'); } - if (!isset($this->contents[$name])) { - \trigger_error('Undefined property: ' . self::class . '::$' . $name, \E_USER_WARNING); - return; - } - $this->modified = true; unset($this->contents[$name]); } @@ -141,22 +128,12 @@ public function offsetUnset($name): void throw new RuntimeException('Cannot alter session after it is closed'); } - if (!isset($this->contents[$name])) { - \trigger_error('Undefined array key "' . $name . '"', \E_USER_WARNING); - return; - } - $this->modified = true; unset($this->contents[$name]); } public function offsetGet($name): mixed { - if (!isset($this->contents[$name])) { - \trigger_error('Undefined array key "' . $name . '"', \E_USER_WARNING); - return null; - } - return $this->contents[$name]; } From 7ac637f44fef714d1ad225dc676306a2240c99d4 Mon Sep 17 00:00:00 2001 From: Yani Date: Fri, 23 Dec 2022 05:25:30 +0100 Subject: [PATCH 3/3] add ArrayAccess tests --- features/access.feature | 11 +++++- tests/behavior/AccessContext.php | 68 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/features/access.feature b/features/access.feature index 9960f38..1aaddb8 100644 --- a/features/access.feature +++ b/features/access.feature @@ -2,19 +2,28 @@ Feature: Session access Scenario: Check data does not exist When data does not exist Then property check returns false + And array access check returns false Scenario: Check data exist When data exists Then property check returns true + And array access check returns true Scenario: Read data that exists When data exists Then property read returns data + And array access read returns data Scenario: Read data that does not exist When data does not exist Then property read triggers error And property read returns null + And array access read triggers error + And array access read returns null Scenario: Null coalesce when data does not exist When data does not exist Then property read with null coalesce returns null - Scenario: Write data + And array access read with null coalesce returns null + Scenario: Write property data When data does not exist Then property write succeeds + Scenario: Write array access data + When data does not exist + Then array access write succeeds diff --git a/tests/behavior/AccessContext.php b/tests/behavior/AccessContext.php index fd91a08..d3c4bc5 100644 --- a/tests/behavior/AccessContext.php +++ b/tests/behavior/AccessContext.php @@ -101,4 +101,72 @@ public function propertyWriteSucceeds(): void Assert::assertCount(1, $this->session); Assert::assertTrue(isset($this->session->bar)); } + + /** + * @Then array access check returns true + */ + public function arrayAccessCheckReturnsTrue(): void + { + Assert::assertTrue(isset($this->session['foo'])); + } + + /** + * @Then array access check returns false + */ + public function arrayAccessCheckReturnsFalse(): void + { + Assert::assertFalse(isset($this->session['foo'])); + } + + /** + * @Then array access read returns data + */ + public function arrayAccessReadReturnsData(): void + { + Assert::assertEquals('bar', $this->session['foo']); + } + + /** + * @Then array access read triggers error + */ + public function arrayAccessReadTriggersNoticeError(): void + { + try { + $errorThrown = false; + $bar = $this->session['bar']; + // @phpstan-ignore-next-line + } catch (Throwable $e) { + $errorThrown = true; + } finally { + Assert::assertTrue($errorThrown); + } + } + + /** + * @Then array access read returns null + */ + public function arrayAccessReadReturnsNull(): void + { + $bar = @$this->session['foo']; + Assert::assertSame(null, $bar); + } + + /** + * @Then array access read with null coalesce returns null + */ + public function arrayAccessReadWithNullCoalesceReturnsNull(): void + { + $bar = $this->session['foo'] ?? null; + Assert::assertSame(null, $bar); + } + + /** + * @Then array access write succeeds + */ + public function arrayAccessWriteSucceeds(): void + { + $this->session['bar'] = 'baz'; + Assert::assertCount(1, $this->session); + Assert::assertTrue(isset($this->session['bar'])); + } }