diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 948ea04..7de7c9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,7 +132,7 @@ jobs: run: tar -xvf /tmp/github-actions/build.tar ./ - name: PHP Mess Detector - uses: php-actions/phpmd@v1 + uses: php-actions/phpmd@v2 with: php_version: ${{ matrix.php }} path: src/ diff --git a/composer.json b/composer.json index cf6ee88..63dbf65 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "phpgt/typesafegetter": "^1.3" }, "require-dev": { - "phpstan/phpstan": "^1.10", + "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^10.1", "phpmd/phpmd": "^2.13", "squizlabs/php_codesniffer": "^3.7" diff --git a/composer.lock b/composer.lock index 0160b61..a6e3f05 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d32bb34f857ad466056beaa4e80cf68a", + "content-hash": "90f742569783bb1b522aa97fe7d3d045", "packages": [ { "name": "phpgt/typesafegetter", @@ -589,20 +589,20 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.25", + "version": "2.1.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e310849a19e02b8bfcbb63147f495d8f872dd96f" + "reference": "8f2e03099cac24ff3b379864d171c5acbfc6b9a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e310849a19e02b8bfcbb63147f495d8f872dd96f", - "reference": "e310849a19e02b8bfcbb63147f495d8f872dd96f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8f2e03099cac24ff3b379864d171c5acbfc6b9a2", + "reference": "8f2e03099cac24ff3b379864d171c5acbfc6b9a2", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -643,7 +643,7 @@ "type": "github" } ], - "time": "2025-04-27T12:20:45+00:00" + "time": "2025-05-02T15:32:28+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2840,12 +2840,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=8.1" }, - "platform-dev": {}, - "plugin-api-version": "2.6.0" + "platform-dev": [], + "plugin-api-version": "2.3.0" } diff --git a/phpcs.xml b/phpcs.xml index b771656..ad96b86 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -25,7 +25,6 @@ - diff --git a/src/Flash.php b/src/Flash.php new file mode 100644 index 0000000..0e1e7c6 --- /dev/null +++ b/src/Flash.php @@ -0,0 +1,32 @@ +session->get("queue.$name"); + if(!$queue) { + $queue = new SplQueue(); + $this->session->set("queue.$name", $queue); + } + + $queue->enqueue(new FlashMessage($name, $message)); + } + + public function consume(string $name):?FlashMessage { + /** @var null|SplQueue $queue */ + $queue = $this->session->get("queue.$name"); + try { + return $queue->dequeue(); + } + catch(RuntimeException $e) { + $this->session->remove("queue.$name"); + return null; + } + } + +} diff --git a/src/FlashMessage.php b/src/FlashMessage.php new file mode 100644 index 0000000..7475eff --- /dev/null +++ b/src/FlashMessage.php @@ -0,0 +1,9 @@ +config["use_trans_sid"] && !$this->config["use_cookies"]) { + if(($this->config["use_trans_sid"] ?? null) + && !$this->config["use_cookies"]) { return $_GET[$this->config["name"]] ?? session_create_id(); } return session_create_id() ?: ""; @@ -196,7 +197,7 @@ private function getSessionOptions( "serialize_handler" => "php_serialize", "use_only_cookies" => $config["use_only_cookies"], "use_cookies" => $config["use_cookies"], - "use_trans_sid" => $config["use_trans_sid"], + "use_trans_sid" => $config["use_trans_sid"] ?? false, "cookie_lifetime" => $config["cookie_lifetime"], "cookie_path" => $config["cookie_path"], "cookie_domain" => $config["cookie_domain"], diff --git a/src/SessionStore.php b/src/SessionStore.php index e1a32f3..e934303 100644 --- a/src/SessionStore.php +++ b/src/SessionStore.php @@ -1,20 +1,24 @@ + * @SuppressWarnings(PHPMD.TooManyPublicMethods) + */ +class SessionStore + extends ArrayIterator + implements SessionContainer, TypeSafeGetter, Countable { use NullableTypeSafeGetter; protected string $name; protected Session $session; /** @var array */ protected array $stores; - /** @var array */ - protected array $data; protected ?SessionStore $parentStore; public function __construct( @@ -26,23 +30,23 @@ public function __construct( $this->session = $session; $this->parentStore = $parentStore; $this->stores = []; - $this->data = []; - } - - public function count():int { - return count($this->data); + parent::__construct(); } public function setData(string $key, mixed $value):void { - $this->data[$key] = $value; + $this->offsetSet($key, $value); } public function getData(string $key):mixed { - return $this->data[$key] ?? null; + if(!$this->offsetExists($key)) { + return null; + } + + return $this->offsetGet($key); } public function containsData(string $key):bool { - return isset($this->data[$key]); + return $this->offsetExists($key); } public function containsStore(string $key):bool { @@ -50,7 +54,7 @@ public function containsStore(string $key):bool { } public function removeData(string $key):void { - unset($this->data[$key]); + $this->offsetUnset($key); } public function removeStore(string $key):void { diff --git a/test/FlashTest.php b/test/FlashTest.php new file mode 100644 index 0000000..8056d6d --- /dev/null +++ b/test/FlashTest.php @@ -0,0 +1,69 @@ +expects(self::once()) + ->method("get") + ->with("queue.test") + ->willReturn(null); + $sessionStore->expects(self::once()) + ->method("set") + ->with( + self::equalTo("queue.test"), + self::isInstanceOf(SplQueue::class) + ); + + $sut = new Flash($sessionStore); + $sut->put("test", "Test message"); + } + + public function testConsume_none():void { + $queue = self::createMock(SplQueue::class); + $queue->expects(self::once()) + ->method("dequeue") + ->willThrowException(new RuntimeException("Can't shift from an empty datastructure")); + $sessionStore = self::createMock(SessionStore::class); + $sessionStore->expects(self::once()) + ->method("get") + ->with("queue.test") + ->willReturn($queue); + $sut = new Flash($sessionStore); + $flashMessage = $sut->consume("test"); + self::assertNull($flashMessage); + } + + public function testConsume():void { + $fm1 = new FlashMessage("test", "First"); + $fm2 = new FlashMessage("test", "Second"); + + $queue = self::createMock(SplQueue::class); + $queue->expects(self::exactly(3)) + ->method("dequeue") + ->willReturnOnConsecutiveCalls($fm1, $fm2); + + $sessionStore = self::createMock(SessionStore::class); + $sessionStore->expects(self::exactly(3)) + ->method("get") + ->with("queue.test") + ->willReturn($queue); + $sessionStore->expects(self::once()) + ->method("remove") + ->with("queue.test"); + $sut = new Flash($sessionStore); + + $flashMessage = $sut->consume("test"); + self::assertInstanceOf(FlashMessage::class, $flashMessage); + self::assertSame("First", $flashMessage->message); + $flashMessage = $sut->consume("test"); + self::assertInstanceOf(FlashMessage::class, $flashMessage); + self::assertSame("Second", $flashMessage->message); + $flashMessage = $sut->consume("test"); + self::assertNull($flashMessage); + } +} diff --git a/test/phpunit/SessionStoreTest.php b/test/phpunit/SessionStoreTest.php index 27417b0..2124cb3 100644 --- a/test/phpunit/SessionStoreTest.php +++ b/test/phpunit/SessionStoreTest.php @@ -151,4 +151,25 @@ public function testCount():void { self::assertCount(3, $sut); } + + + public function testSessionStoreIsIterable():void { + $session = $this->createMock(Session::class); + $sut = new SessionStore("test", $session); + + $rawData = [ + "key1" => "value1", + "key2" => "value2", + "key3" => "value3", + ]; + + foreach($rawData as $key => $value) { + $sut->set($key, $value); + } + + foreach($sut as $key => $value) { + self::assertArrayHasKey($key, $rawData); + self::assertSame($rawData[$key], $value); + } + } }