diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index a0a7091..6fa1ab4 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -1,5 +1,7 @@ true, - // 'void_return' => true, + 'declare_strict_types' => true, + 'void_return' => true, ]; $options = [ diff --git a/UPGRADING.md b/UPGRADING.md index 9a59d9a..05203c0 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,5 +1,11 @@ # Upgrade Guide +## Version 2.2.0 to 2.3.0 + +* The minimum required version of CodeIgniter is now `4.3` and PHP `8.2`. +* All source files now declare `strict_types=1`. +* `BaseHandler` methods `set()`, `forget()`, `flush()`, and `persistPendingProperties()` now have `void` return types. Any custom handler extending `BaseHandler` must update its method signatures to include `: void`. + ## Version 1 to 2 *** diff --git a/composer.json b/composer.json index a51f78d..9c878d6 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ }, "require-dev": { "codeigniter4/devkit": "^1.3", - "codeigniter4/framework": "^4.2.3" + "codeigniter4/framework": "^4.3" }, "minimum-stability": "dev", "prefer-stable": true, diff --git a/docs/limitations.md b/docs/limitations.md index 9822665..d59c9c7 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -10,9 +10,9 @@ The following are known limitations of the library: 2. **Deferred writes (`deferWrites => true`)**: All settings are batched and written to storage at the end of the request (during the `post_system` event). This minimizes the number of database queries and file writes, improving performance. However, there are important considerations: - - Write operations will not appear in CodeIgniter's Debug Toolbar, since the `post_system` event executes after toolbar data collection. - - If the request terminates early (fatal error, `exit()`, etc.) before `post_system`, pending writes are lost. - - Write failures are logged but handled silently - no exceptions are thrown back to the calling code. + - Write operations will not appear in CodeIgniter's Debug Toolbar, since the `post_system` event executes after toolbar data collection. + - If the request terminates early (fatal error, `exit()`, etc.) before `post_system`, pending writes are lost. + - Write failures are logged but handled silently - no exceptions are thrown back to the calling code. 3. **First-level property access only**: You can only access the first level of a config property directly. In most config classes this is not an issue, since properties are simple values. However, some config files (like `Database`) contain properties that diff --git a/psalm.xml b/psalm.xml index 6b0fb24..142455e 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ sets([SetList::DEAD_CODE, LevelSetList::UP_TO_PHP_74, PHPUnitSetList::PHPUNIT_80]); + $rectorConfig->sets([ + SetList::DEAD_CODE, + LevelSetList::UP_TO_PHP_82, + PHPUnitSetList::PHPUNIT_CODE_QUALITY, + PHPUnitSetList::PHPUNIT_100, + ]); + $rectorConfig->parallel(); // The paths to refactor (can also be supplied with CLI arguments) $rectorConfig->paths([ @@ -65,7 +71,7 @@ } // Set the target version for refactoring - $rectorConfig->phpVersion(PhpVersion::PHP_74); + $rectorConfig->phpVersion(PhpVersion::PHP_82); // Auto-import fully qualified class names $rectorConfig->importNames(); diff --git a/src/Commands/ClearSettings.php b/src/Commands/ClearSettings.php index 73c6bcc..dcde19f 100644 --- a/src/Commands/ClearSettings.php +++ b/src/Commands/ClearSettings.php @@ -1,5 +1,7 @@ getHandlerNames($config); diff --git a/src/Config/Services.php b/src/Config/Services.php index f4fca95..bf77f18 100644 --- a/src/Config/Services.php +++ b/src/Config/Services.php @@ -1,5 +1,7 @@ forge->addField('id'); $this->forge->addField([ @@ -51,7 +53,7 @@ public function up() $this->forge->createTable($this->config->database['table'], true); } - public function down() + public function down(): void { $this->forge->dropTable($this->config->database['table']); } diff --git a/src/Database/Migrations/2021-11-14-143905_AddContextColumn.php b/src/Database/Migrations/2021-11-14-143905_AddContextColumn.php index 9bf7fe2..27a0fb3 100644 --- a/src/Database/Migrations/2021-11-14-143905_AddContextColumn.php +++ b/src/Database/Migrations/2021-11-14-143905_AddContextColumn.php @@ -1,5 +1,7 @@ forge->addColumn($this->config->database['table'], [ 'context' => [ @@ -30,7 +32,7 @@ public function up() ]); } - public function down() + public function down(): void { $this->forge->dropColumn($this->config->database['table'], 'context'); } diff --git a/src/Handlers/ArrayHandler.php b/src/Handlers/ArrayHandler.php index b1a2320..aee8f26 100644 --- a/src/Handlers/ArrayHandler.php +++ b/src/Handlers/ArrayHandler.php @@ -1,5 +1,7 @@ getStored($class, $property, $context); } - public function set(string $class, string $property, $value = null, ?string $context = null) + public function set(string $class, string $property, $value = null, ?string $context = null): void { $this->setStored($class, $property, $value, $context); } - public function forget(string $class, string $property, ?string $context = null) + public function forget(string $class, string $property, ?string $context = null): void { $this->forgetStored($class, $property, $context); } - public function flush() + public function flush(): void { $this->general = []; $this->contexts = []; @@ -189,7 +191,7 @@ protected function setupDeferredWrites(bool $enabled): void $this->deferWrites = $enabled; if ($this->deferWrites) { - Events::on('post_system', [$this, 'persistPendingProperties']); + Events::on('post_system', $this->persistPendingProperties(...)); } } } diff --git a/src/Handlers/BaseHandler.php b/src/Handlers/BaseHandler.php index 696f854..95f96fd 100644 --- a/src/Handlers/BaseHandler.php +++ b/src/Handlers/BaseHandler.php @@ -1,5 +1,7 @@ deferWrites) { $this->markPending($class, $property, $value, $context); @@ -137,10 +137,8 @@ private function persist(string $class, string $property, $value, ?string $conte /** * Deletes the record from persistent storage, if found, * and from the local cache. - * - * @return void */ - public function forget(string $class, string $property, ?string $context = null) + public function forget(string $class, string $property, ?string $context = null): void { $this->hydrate($context); @@ -175,10 +173,8 @@ private function persistForget(string $class, string $property, ?string $context /** * Deletes all records from persistent storage, if found, * and from the local cache. - * - * @return void */ - public function flush() + public function flush(): void { $this->builder->truncate(); @@ -228,10 +224,8 @@ private function hydrate(?string $context): void * Persists all pending properties to the database. * Called automatically at the end of request via post_system * event when deferWrites is enabled. - * - * @return void */ - public function persistPendingProperties() + public function persistPendingProperties(): void { if ($this->pendingProperties === []) { return; diff --git a/src/Handlers/FileHandler.php b/src/Handlers/FileHandler.php index dec21eb..ec6d65c 100644 --- a/src/Handlers/FileHandler.php +++ b/src/Handlers/FileHandler.php @@ -1,5 +1,7 @@ hydrate($class, $context); @@ -101,11 +101,9 @@ public function set(string $class, string $property, $value = null, ?string $con * Deletes the record from persistent storage, if found, * and from the local cache. * - * @return void - * * @throws RuntimeException For file write failures */ - public function forget(string $class, string $property, ?string $context = null) + public function forget(string $class, string $property, ?string $context = null): void { $this->hydrate($class, $context); @@ -128,11 +126,9 @@ public function forget(string $class, string $property, ?string $context = null) * Deletes all settings files from persistent storage * and clears the local cache. * - * @return void - * * @throws RuntimeException For file deletion failures */ - public function flush() + public function flush(): void { // Delete all .php files in main directory (null context files) $files = glob($this->path . '*.php', GLOB_NOSORT); diff --git a/src/Helpers/setting_helper.php b/src/Helpers/setting_helper.php index 1282130..2b9cc2a 100644 --- a/src/Helpers/setting_helper.php +++ b/src/Helpers/setting_helper.php @@ -1,5 +1,7 @@ prepareClassAndProperty($key); @@ -90,10 +90,8 @@ public function set(string $key, $value = null, ?string $context = null) * Removes a setting from the persistent storage, * effectively returning the value to the default value * found in the config file, if any. - * - * @return void */ - public function forget(string $key, ?string $context = null) + public function forget(string $key, ?string $context = null): void { [$class, $property] = $this->prepareClassAndProperty($key); @@ -105,10 +103,8 @@ public function forget(string $key, ?string $context = null) /** * Removes all settings from the persistent storage, * Useful during testing. Use with caution. - * - * @return void */ - public function flush() + public function flush(): void { foreach ($this->getWriteHandlers() as $handler) { $handler->flush(); diff --git a/tests/Commands/ClearSettingsTest.php b/tests/Commands/ClearSettingsTest.php index a0d88b0..96b3e69 100644 --- a/tests/Commands/ClearSettingsTest.php +++ b/tests/Commands/ClearSettingsTest.php @@ -1,5 +1,7 @@ assertStringContainsString('Settings cleared from array handler', $output); } - public function testSingleHandlerFile() + public function testSingleHandlerFile(): void { PhpStreamWrapper::register(); PhpStreamWrapper::setContent("y\n"); @@ -64,7 +66,7 @@ public function testSingleHandlerFile() $this->assertStringContainsString('Settings cleared from file handler', $output); } - public function testMultipleHandlers() + public function testMultipleHandlers(): void { PhpStreamWrapper::register(); PhpStreamWrapper::setContent("y\n"); @@ -82,7 +84,7 @@ public function testMultipleHandlers() $this->assertStringContainsString('Settings cleared from array and file handlers', $output); } - public function testThreeHandlers() + public function testThreeHandlers(): void { PhpStreamWrapper::register(); PhpStreamWrapper::setContent("y\n"); @@ -100,7 +102,7 @@ public function testThreeHandlers() $this->assertStringContainsString('file handler', $output); } - public function testNoWriteableHandlers() + public function testNoWriteableHandlers(): void { PhpStreamWrapper::register(); PhpStreamWrapper::setContent("y\n"); @@ -118,7 +120,7 @@ public function testNoWriteableHandlers() $this->assertStringContainsString('No handlers available to clear', $output); } - public function testEmptyHandlersArray() + public function testEmptyHandlersArray(): void { PhpStreamWrapper::register(); PhpStreamWrapper::setContent("y\n"); @@ -135,7 +137,7 @@ public function testEmptyHandlersArray() $this->assertStringContainsString('No handlers available to clear', $output); } - public function testUserCancelsOperation() + public function testUserCancelsOperation(): void { PhpStreamWrapper::register(); PhpStreamWrapper::setContent("n\n"); // User answers 'n' to prompt @@ -154,7 +156,7 @@ public function testUserCancelsOperation() $this->assertStringNotContainsString('Settings cleared', $output); } - public function testUserConfirmsOperation() + public function testUserConfirmsOperation(): void { PhpStreamWrapper::register(); PhpStreamWrapper::setContent("y\n"); // User answers 'y' to prompt @@ -173,21 +175,21 @@ public function testUserConfirmsOperation() $this->assertStringContainsString('Settings cleared from array handler', $output); } - public function testActuallyFlushesSettings() + public function testActuallyFlushesSettings(): void { PhpStreamWrapper::register(); PhpStreamWrapper::setContent("y\n"); + // Configure handler before getting service + $config = config('Settings'); + $config->handlers = ['array']; + // Set some settings $settings = service('settings'); $settings->set('Example.siteName', 'Test'); $this->assertSame('Test', $settings->get('Example.siteName')); - // Run clear command - $config = config('Settings'); - $config->handlers = ['array']; - command('settings:clear'); PhpStreamWrapper::restore(); diff --git a/tests/DatabaseHandlerTest.php b/tests/DatabaseHandlerTest.php index 80be565..d55836a 100644 --- a/tests/DatabaseHandlerTest.php +++ b/tests/DatabaseHandlerTest.php @@ -1,5 +1,7 @@ settings = new Settings($config); $this->table = $config->database['table']; - $this->group = $config->database['group']; } /** @@ -69,7 +65,7 @@ private function persistDeferredWrites(Settings $settings): void $handlers['database']->persistPendingProperties(); } - public function testSetInsertsNewRows() + public function testSetInsertsNewRows(): void { $this->settings->set('Example.siteName', 'Foo'); @@ -81,7 +77,7 @@ public function testSetInsertsNewRows() ]); } - public function testInvalidGroup() + public function testInvalidGroup(): void { $this->expectException(InvalidArgumentException::class); @@ -95,7 +91,7 @@ public function testInvalidGroup() $this->settings->set('Example.siteName', true); } - public function testSetDefaultGroup() + public function testSetDefaultGroup(): void { /** @var \CodeIgniter\Settings\Config\Settings $config */ $config = config('Settings'); @@ -114,7 +110,7 @@ public function testSetDefaultGroup() $this->assertTrue($this->settings->get('Example.siteName')); } - public function testSetInsertsBoolTrue() + public function testSetInsertsBoolTrue(): void { $this->settings->set('Example.siteName', true); @@ -128,7 +124,7 @@ public function testSetInsertsBoolTrue() $this->assertTrue($this->settings->get('Example.siteName')); } - public function testSetInsertsBoolFalse() + public function testSetInsertsBoolFalse(): void { $this->settings->set('Example.siteName', false); @@ -142,7 +138,7 @@ public function testSetInsertsBoolFalse() $this->assertFalse($this->settings->get('Example.siteName')); } - public function testSetInsertsNull() + public function testSetInsertsNull(): void { $this->settings->set('Example.siteName', null); @@ -156,7 +152,7 @@ public function testSetInsertsNull() $this->assertNull($this->settings->get('Example.siteName')); } - public function testSetInsertsArray() + public function testSetInsertsArray(): void { $data = ['foo' => 'bar']; $this->settings->set('Example.siteName', $data); @@ -171,7 +167,7 @@ public function testSetInsertsArray() $this->assertSame($data, $this->settings->get('Example.siteName')); } - public function testSetInsertsObject() + public function testSetInsertsObject(): void { $data = (object) ['foo' => 'bar']; $this->settings->set('Example.siteName', $data); @@ -186,7 +182,7 @@ public function testSetInsertsObject() $this->assertSame((array) $data, (array) $this->settings->get('Example.siteName')); } - public function testSetUpdatesExistingRows() + public function testSetUpdatesExistingRows(): void { $this->hasInDatabase($this->table, [ 'class' => 'Tests\Support\Config\Example', @@ -205,7 +201,7 @@ public function testSetUpdatesExistingRows() ]); } - public function testWorksWithoutConfigClass() + public function testWorksWithoutConfigClass(): void { $this->settings->set('Nada.siteName', 'Bar'); @@ -218,7 +214,7 @@ public function testWorksWithoutConfigClass() $this->assertSame('Bar', $this->settings->get('Nada.siteName')); } - public function testForgetSuccess() + public function testForgetSuccess(): void { $this->hasInDatabase($this->table, [ 'class' => 'Tests\Support\Config\Example', @@ -236,7 +232,7 @@ public function testForgetSuccess() ]); } - public function testForgetWithNoStoredRecord() + public function testForgetWithNoStoredRecord(): void { $this->settings->forget('Example.siteName'); @@ -246,7 +242,7 @@ public function testForgetWithNoStoredRecord() ]); } - public function testFlush() + public function testFlush(): void { // Default value in the config file $this->assertSame('Settings Test', $this->settings->get('Example.siteName')); @@ -267,7 +263,7 @@ public function testFlush() $this->assertSame('Settings Test', $this->settings->get('Example.siteName')); } - public function testSetWithContext() + public function testSetWithContext(): void { $this->settings->set('Example.siteName', 'Banana', 'environment:test'); @@ -283,7 +279,7 @@ public function testSetWithContext() /** * @see https://github.com/codeigniter4/settings/issues/20 */ - public function testSetUpdatesContextOnly() + public function testSetUpdatesContextOnly(): void { $this->settings->set('Example.siteName', 'Humpty'); $this->settings->set('Example.siteName', 'Jack', 'context:male'); @@ -314,7 +310,7 @@ public function testSetUpdatesContextOnly() ]); } - public function testDeferredWritesReducesDatabaseQueries() + public function testDeferredWritesReducesDatabaseQueries(): void { // Create new settings instance with deferred writes enabled $deferredSettings = $this->createDeferredSettings(); @@ -362,7 +358,7 @@ public function testDeferredWritesReducesDatabaseQueries() ]); } - public function testDeferredWritesForgetDeletesAfterPersist() + public function testDeferredWritesForgetDeletesAfterPersist(): void { // First, insert a record to delete $this->settings->set('Example.siteName', 'InitialValue'); @@ -395,7 +391,7 @@ public function testDeferredWritesForgetDeletesAfterPersist() ]); } - public function testDeferredWritesDeleteThenSet() + public function testDeferredWritesDeleteThenSet(): void { // First, insert a record $this->settings->set('Example.siteName', 'InitialValue'); @@ -431,7 +427,7 @@ public function testDeferredWritesDeleteThenSet() ]); } - public function testNoDuplicatesWhenUpdatingExistingRecords() + public function testNoDuplicatesWhenUpdatingExistingRecords(): void { // Pre-populate database with existing records $this->settings->set('Example.siteName', 'InitialValue1'); @@ -493,7 +489,7 @@ public function testNoDuplicatesWhenUpdatingExistingRecords() ]); } - public function testNoDuplicatesWithMixedNewAndExistingRecords() + public function testNoDuplicatesWithMixedNewAndExistingRecords(): void { // Pre-populate database with some existing records $this->settings->set('Example.siteName', 'ExistingValue'); diff --git a/tests/FileHandlerTest.php b/tests/FileHandlerTest.php index 5aab11e..503846c 100644 --- a/tests/FileHandlerTest.php +++ b/tests/FileHandlerTest.php @@ -1,5 +1,7 @@ persistPendingProperties(); } - public function testSetCreatesDirectory() + public function testSetCreatesDirectory(): void { $this->assertDirectoryExists($this->path); $this->assertIsWritable($this->path); } - public function testSetCreatesFile() + public function testSetCreatesFile(): void { $this->settings->set('Example.siteName', 'Foo'); @@ -92,49 +94,49 @@ public function testSetCreatesFile() $this->assertStringEndsWith('.php', $files[0]); } - public function testSetStoresString() + public function testSetStoresString(): void { $this->settings->set('Example.siteName', 'Foo'); $this->assertSame('Foo', $this->settings->get('Example.siteName')); } - public function testSetStoresBoolTrue() + public function testSetStoresBoolTrue(): void { $this->settings->set('Example.siteName', true); $this->assertTrue($this->settings->get('Example.siteName')); } - public function testSetStoresBoolFalse() + public function testSetStoresBoolFalse(): void { $this->settings->set('Example.siteName', false); $this->assertFalse($this->settings->get('Example.siteName')); } - public function testSetStoresNull() + public function testSetStoresNull(): void { $this->settings->set('Example.siteName', null); $this->assertNull($this->settings->get('Example.siteName')); } - public function testSetStoresInteger() + public function testSetStoresInteger(): void { $this->settings->set('Example.siteName', 42); $this->assertSame(42, $this->settings->get('Example.siteName')); } - public function testSetStoresFloat() + public function testSetStoresFloat(): void { $this->settings->set('Example.siteName', 3.14); - $this->assertSame(3.14, $this->settings->get('Example.siteName')); + $this->assertEqualsWithDelta(3.14, $this->settings->get('Example.siteName'), PHP_FLOAT_EPSILON); } - public function testSetStoresArray() + public function testSetStoresArray(): void { $data = ['foo' => 'bar', 'baz' => 'qux']; $this->settings->set('Example.siteName', $data); @@ -142,7 +144,7 @@ public function testSetStoresArray() $this->assertSame($data, $this->settings->get('Example.siteName')); } - public function testSetStoresObject() + public function testSetStoresObject(): void { $data = (object) ['foo' => 'bar']; $this->settings->set('Example.siteName', $data); @@ -151,7 +153,7 @@ public function testSetStoresObject() $this->assertSame((array) $data, (array) $result); } - public function testSetUpdatesExistingValue() + public function testSetUpdatesExistingValue(): void { $this->settings->set('Example.siteName', 'Foo'); $this->assertSame('Foo', $this->settings->get('Example.siteName')); @@ -164,19 +166,19 @@ public function testSetUpdatesExistingValue() $this->assertCount(1, $files); } - public function testGetNonExistentReturnsNull() + public function testGetNonExistentReturnsNull(): void { $this->assertNull($this->settings->get('Example.nonExistent')); } - public function testWorksWithoutConfigClass() + public function testWorksWithoutConfigClass(): void { $this->settings->set('Nada.siteName', 'Bar'); $this->assertSame('Bar', $this->settings->get('Nada.siteName')); } - public function testForgetRemovesValue() + public function testForgetRemovesValue(): void { $this->settings->set('Example.siteName', 'Foo'); $this->assertSame('Foo', $this->settings->get('Example.siteName')); @@ -187,7 +189,7 @@ public function testForgetRemovesValue() $this->assertSame('Settings Test', $this->settings->get('Example.siteName')); } - public function testForgetWithNoStoredRecord() + public function testForgetWithNoStoredRecord(): void { // Should not throw an exception $this->settings->forget('Example.siteName'); @@ -196,7 +198,7 @@ public function testForgetWithNoStoredRecord() $this->assertSame('Settings Test', $this->settings->get('Example.siteName')); } - public function testFlushRemovesAllFiles() + public function testFlushRemovesAllFiles(): void { $this->settings->set('Example.siteName', 'Foo'); $this->settings->set('Example.siteEmail', 'test@example.com'); @@ -213,7 +215,7 @@ public function testFlushRemovesAllFiles() $this->assertSame('Settings Test', $this->settings->get('Example.siteName')); } - public function testFlushRemovesFilesAndContextDirectories() + public function testFlushRemovesFilesAndContextDirectories(): void { // Create files in main directory (null context) $this->settings->set('Example.siteName', 'Main'); @@ -243,14 +245,14 @@ public function testFlushRemovesFilesAndContextDirectories() $this->assertSame('Settings Test', $this->settings->get('Example.siteName', 'production')); } - public function testSetWithContext() + public function testSetWithContext(): void { $this->settings->set('Example.siteName', 'Banana', 'environment:test'); $this->assertSame('Banana', $this->settings->get('Example.siteName', 'environment:test')); } - public function testSetUpdatesContextOnly() + public function testSetUpdatesContextOnly(): void { $this->settings->set('Example.siteName', 'Humpty'); $this->settings->set('Example.siteName', 'Jack', 'context:male'); @@ -262,7 +264,7 @@ public function testSetUpdatesContextOnly() $this->assertSame('Jane', $this->settings->get('Example.siteName', 'context:female')); } - public function testContextFallsBackToGeneral() + public function testContextFallsBackToGeneral(): void { $this->settings->set('Example.siteName', 'General'); @@ -270,7 +272,7 @@ public function testContextFallsBackToGeneral() $this->assertSame('General', $this->settings->get('Example.siteName', 'context:nonexistent')); } - public function testMultiplePropertiesInSameFile() + public function testMultiplePropertiesInSameFile(): void { $this->settings->set('Example.siteName', 'Foo'); $this->settings->set('Example.siteEmail', 'test@example.com'); @@ -283,7 +285,7 @@ public function testMultiplePropertiesInSameFile() $this->assertCount(1, $files); } - public function testDifferentClassesCreateDifferentFiles() + public function testDifferentClassesCreateDifferentFiles(): void { $this->settings->set('Example.siteName', 'Foo'); $this->settings->set('Nada.siteName', 'Bar'); @@ -296,7 +298,7 @@ public function testDifferentClassesCreateDifferentFiles() $this->assertCount(2, $files); } - public function testPersistenceAcrossInstances() + public function testPersistenceAcrossInstances(): void { // Set value in first instance $this->settings->set('Example.siteName', 'Persistent'); @@ -312,7 +314,7 @@ public function testPersistenceAcrossInstances() $this->assertSame('Persistent', $newSettings->get('Example.siteName')); } - public function testFileContentIsValidPHP() + public function testFileContentIsValidPHP(): void { $this->settings->set('Example.siteName', 'Test'); @@ -321,6 +323,8 @@ public function testFileContentIsValidPHP() $content = file_get_contents($files[0]); + $this->assertIsString($content); + // Should start with PHP tag $this->assertStringStartsWith('assertIsArray($data); } - public function testUsesClassNameForFilename() + public function testUsesClassNameForFilename(): void { $this->settings->set('Example.siteName', 'Test'); @@ -342,7 +346,7 @@ public function testUsesClassNameForFilename() $this->assertFileExists($expectedFile); } - public function testContextUsesHashedSubdirectory() + public function testContextUsesHashedSubdirectory(): void { $context = 'environment:production'; $this->settings->set('Example.siteName', 'Prod', $context); @@ -354,7 +358,7 @@ public function testContextUsesHashedSubdirectory() $this->assertFileExists($expectedFile); } - public function testEmptyStringContextIsDifferentFromNull() + public function testEmptyStringContextIsDifferentFromNull(): void { // Set with null context $this->settings->set('Example.siteName', 'Null', null); @@ -376,7 +380,7 @@ public function testEmptyStringContextIsDifferentFromNull() $this->assertSame('Empty', $this->settings->get('Example.siteName', '')); } - public function testConcurrentReadsDontLoadFileTwice() + public function testConcurrentReadsDontLoadFileTwice(): void { $this->settings->set('Example.siteName', 'Test'); $this->settings->set('Example.siteEmail', 'test@example.com'); @@ -395,7 +399,7 @@ public function testConcurrentReadsDontLoadFileTwice() $this->assertSame('test@example.com', $value2); } - public function testHasReturnsTrueWhenValueExists() + public function testHasReturnsTrueWhenValueExists(): void { $this->settings->set('Example.siteName', 'Test'); @@ -407,7 +411,7 @@ public function testHasReturnsTrueWhenValueExists() $this->assertTrue($handlers['file']->has('Tests\Support\Config\Example', 'siteName')); } - public function testHasReturnsFalseWhenValueDoesNotExist() + public function testHasReturnsFalseWhenValueDoesNotExist(): void { $reflection = new ReflectionClass($this->settings); $handlersProperty = $reflection->getProperty('handlers'); @@ -420,7 +424,7 @@ public function testHasReturnsFalseWhenValueDoesNotExist() * Simulate writes from different PHP processes (each with separate in-memory state) * by manually modifying the file between operations */ - public function testMergesChangesFromDifferentProcesses() + public function testMergesChangesFromDifferentProcesses(): void { // Process A writes siteName $this->settings->set('Example.siteName', 'First'); @@ -457,7 +461,7 @@ public function testMergesChangesFromDifferentProcesses() $this->assertSame('Second', $newSettings->get('Example.siteTitle')); } - public function testDeferredWritesReducesFileWrites() + public function testDeferredWritesReducesFileWrites(): void { // Create new settings instance with deferred writes enabled $deferredSettings = $this->createDeferredSettings(); @@ -488,7 +492,7 @@ public function testDeferredWritesReducesFileWrites() $this->assertSame('Value3', $data['siteTitle']['value']); } - public function testDeferredWritesForgetDeletesAfterPersist() + public function testDeferredWritesForgetDeletesAfterPersist(): void { // First, create a file with a value $this->settings->set('Example.siteName', 'InitialValue'); @@ -525,7 +529,7 @@ public function testDeferredWritesForgetDeletesAfterPersist() $this->assertArrayNotHasKey('siteName', $data); } - public function testDeferredWritesDeleteThenSet() + public function testDeferredWritesDeleteThenSet(): void { // First, create a file with a value $this->settings->set('Example.siteName', 'InitialValue'); diff --git a/tests/HelperTest.php b/tests/HelperTest.php index 9ac17ea..e2183e8 100644 --- a/tests/HelperTest.php +++ b/tests/HelperTest.php @@ -1,5 +1,7 @@ assertInstanceOf(Settings::class, setting()); } - public function testThrowsExceptionWithInvalidField() + public function testThrowsExceptionWithInvalidField(): void { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessage('$key must contain both the class and field name, i.e. Foo.bar'); @@ -33,7 +35,7 @@ public function testThrowsExceptionWithInvalidField() setting('Foobar'); } - public function testSetsNull() + public function testSetsNull(): void { setting('Foo.bam', null); @@ -41,14 +43,14 @@ public function testSetsNull() $this->assertNull(setting('Foo.bam')); } - public function testReturnsValueDotArray() + public function testReturnsValueDotArray(): void { service('settings')->set('Foo.bar', 'baz'); $this->assertSame('baz', setting('Foo.bar')); } - public function testSettingValueDotArray() + public function testSettingValueDotArray(): void { service('settings')->set('Foo.bar', 'baz'); diff --git a/tests/SettingsTest.php b/tests/SettingsTest.php index a519142..577ab87 100644 --- a/tests/SettingsTest.php +++ b/tests/SettingsTest.php @@ -1,5 +1,7 @@ handlers = []; @@ -22,7 +24,7 @@ public function testSettingsUsesParameter() $this->assertSame([], $result); } - public function testServiceUsesConfig() + public function testServiceUsesConfig(): void { Services::resetSingle('settings'); @@ -35,17 +37,17 @@ public function testServiceUsesConfig() $this->assertSame([], $result); } - public function testSettingsGetsFromConfig() + public function testSettingsGetsFromConfig(): void { $this->assertSame(config('Example')->siteName, $this->settings->get('Example.siteName')); } - public function testSettingsNotFound() + public function testSettingsNotFound(): void { $this->assertSame(config('Example')->siteName, $this->settings->get('Example.siteName')); } - public function testGetWithContext() + public function testGetWithContext(): void { $this->settings->set('Example.siteName', 'NoContext'); $this->settings->set('Example.siteName', 'YesContext', 'testing:true'); @@ -54,14 +56,14 @@ public function testGetWithContext() $this->assertSame('YesContext', $this->settings->get('Example.siteName', 'testing:true')); } - public function testGetWithoutContextUsesGlobal() + public function testGetWithoutContextUsesGlobal(): void { $this->settings->set('Example.siteName', 'NoContext'); $this->assertSame('NoContext', $this->settings->get('Example.siteName', 'testing:true')); } - public function testForgetWithContext() + public function testForgetWithContext(): void { $this->settings->set('Example.siteName', 'Bar'); $this->settings->set('Example.siteName', 'Amnesia', 'category:disease'); diff --git a/tests/_support/Config/Example.php b/tests/_support/Config/Example.php index e0137c8..f98bbce 100644 --- a/tests/_support/Config/Example.php +++ b/tests/_support/Config/Example.php @@ -1,5 +1,7 @@