diff --git a/README.md b/README.md index 905b396..05c7347 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,95 @@ It also promises to be immutable after first read. ## Usage +### Load config from file + +We support writing your config in php files + +Example: ```php + 'value', + 'nested' => [ + 'key' => 'nested-value' + 'bool-key' => false, + ], +]; +``` -$config = new \Stefna\Config\FileConfig('path-to-php-config.php'); +```php +$config = new \Stefna\Config\FileConfig('path-to-php/config.php'); // config file is not read until it's needed -$config->getBool('boolKey'); +$config->getBool('nested.bool-key') === false; +$config->getString('config-key') === 'value'; ``` +### Load multiple files into config + +```php + 'value', + 'nested' => [ + 'key' => 'nested-value' + 'bool-key' => false, + ], +]; +``` +```php + 'production-value', + 'nested' => [ + 'extra-key' => 42, + ], +]; +``` + +```php +$config = new \Stefna\Config\FileCollectionConfig('path-to-php/'); +$config->addFile('common.php'); +$config->addFile('production.php'); + +// config files is not read until it's needed + +$config->getInt('nested.extra-key') === 42; +$config->getString('config-key') === 'product-value'; +``` + +### Mutable config + +We do provide a mutable config that allows you to override values in the "root" config +this is meant to be used when testing applications but still allow the "root" configuration to stay immutable + + +```php +$rootConfig = new \Stefna\Config\FileCollectionConfig('path-to-php/'); +$rootConfig->addFile('common.php'); +$rootConfig->addFile('production.php'); + +$config = new \Stefna\Config\MutableConfig($rootConfig); + +$config->setConfigValue('config-key', 'overridden-value'); + +$config->getString('config-key') === 'overridden-value'; + +$config->resetConfigValue('config-key'); + +$config->getString('config-key') === 'production-value'; +``` + + ## Contribute We are always happy to receive bug/security reports and bug/security fixes diff --git a/src/MutableConfig.php b/src/MutableConfig.php new file mode 100644 index 0000000..efffb31 --- /dev/null +++ b/src/MutableConfig.php @@ -0,0 +1,33 @@ + */ + private array $config = []; + + public function __construct( + private readonly Config $rootConfig, + ) {} + + public function getRawValue(string $key): mixed + { + if (isset($this->config[$key])) { + return $this->config[$key]; + } + return $this->rootConfig->get($key); + } + + public function setConfigValue(string $key, mixed $value): void + { + $this->config[$key] = $value; + } + + public function resetConfigValue(string $key): void + { + unset($this->config[$key]); + } +} diff --git a/tests/AbstractConfigTestCase.php b/tests/AbstractConfigTestCase.php new file mode 100644 index 0000000..3275aaa --- /dev/null +++ b/tests/AbstractConfigTestCase.php @@ -0,0 +1,147 @@ +getConfig(); + $obj = $config->getArrayAsObject('testArray', ArrayConfig::class); + + $this->assertInstanceOf(ArrayConfig::class, $obj); + $this->assertSame(1, $obj->getInt('random')); + } + + public function testGetArrayObject(): void + { + $config = $this->getConfig(); + + $obj = $config->getArrayAsObject('testArray', TestStub::class); + + $this->assertInstanceOf(TestStub::class, $obj); + $this->assertSame(1, $obj->random); + } + + public function testGetArrayObjectWithInvalidClass(): void + { + $config = $this->getConfig(); + + $this->expectException(\BadMethodCallException::class); + + $config->getArrayAsObject('testArray', \ArrayObject::class); + } + + public function testGetArray(): void + { + $config = $this->getConfig(); + + $arr = $config->getArray('testArray'); + + $this->assertIsArray($arr); + $this->assertSame('1', $arr['random']); + } + + public function testGetArrayWithDefaultValue(): void + { + $config = $this->getConfig(); + + $arr = $config->getArray('not-found', ['number' => 1]); + + $this->assertIsArray($arr); + $this->assertSame(1, $arr['number']); + } + + public function testGetArrayWithInvalidValueReturnsEmptyArray(): void + { + $config = $this->getConfig(); + + $arr = $config->getArray('testString'); + $this->assertEmpty($arr); + } + + public function testGetArrayNotFoundEmptyArray(): void + { + $config = $this->getConfig(); + + $arr = $config->getArray('not-found'); + $this->assertEmpty($arr); + } + + public function testGet(): void + { + $config = $this->getConfig(); + + $this->assertSame('string', $config->get('testString')); + } + + public function testGetWithDefault(): void + { + $config = $this->getConfig(); + + $this->assertSame(1, $config->get('not-found', 1)); + } + + public function testGetNestedValueByDotKey(): void + { + $config = $this->getConfig(); + + $this->assertSame(1, $config->getInt('deep.nesting.int')); + $this->assertSame('test', $config->getString('deep.nesting.string')); + } + + public function testGetNestedValueByDotKeyNotFound(): void + { + $config = $this->getConfig(); + + $this->assertNull($config->getInt('deep.nesting.not-found.int')); + } + + public function testGetNestedValueHasLowerPriorityThanExactMatch(): void + { + $config = $this->getConfig(); + + $this->assertSame(1, $config->get('nesting.test.value')); + } + + public function testDotKeyReturnEmptyOnExtraDots(): void + { + $config = $this->getConfig(); + + $this->assertNull($config->get('nesting.')); + $this->assertNull($config->get('.nesting')); + } + + protected function getDefaultArrayConfig(): ArrayConfig + { + return new ArrayConfig([ + 'testString' => 'string', + 'testNumberAsString' => '43', + 'testNumber' => 42, + 'testBool' => true, + 'testBoolAsString' => 'on', + 'testArray' => [ + 'random' => '1', + ], + 'deep' => [ + 'nesting' => [ + 'int' => 1, + 'string' => 'test', + ], + ], + 'nesting.test.value' => 1, + 'nesting' => [ + 'test' => [ + 'value' => 2, + ], + ], + ]); + } + + abstract protected function getConfig(): Config; +} diff --git a/tests/ArrayConfigTest.php b/tests/ArrayConfigTest.php index 8c2409c..0a049c7 100644 --- a/tests/ArrayConfigTest.php +++ b/tests/ArrayConfigTest.php @@ -6,139 +6,10 @@ use Stefna\Config\ArrayConfig; use Stefna\Config\Tests\Stub\TestStub; -final class ArrayConfigTest extends TestCase +final class ArrayConfigTest extends AbstractConfigTestCase { - public function testGetArrayConfigAsConfigObject(): void - { - $config = $this->getConfig(); - $obj = $config->getArrayAsObject('testArray', ArrayConfig::class); - - $this->assertInstanceOf(ArrayConfig::class, $obj); - $this->assertSame(1, $obj->getInt('random')); - } - - public function testGetArrayObject(): void - { - $config = $this->getConfig(); - - $obj = $config->getArrayAsObject('testArray', TestStub::class); - - $this->assertInstanceOf(TestStub::class, $obj); - $this->assertSame(1, $obj->random); - } - - public function testGetArrayObjectWithInvalidClass(): void - { - $config = $this->getConfig(); - - $this->expectException(\BadMethodCallException::class); - - $config->getArrayAsObject('testArray', \ArrayObject::class); - } - - public function testGetArray(): void - { - $config = $this->getConfig(); - - $arr = $config->getArray('testArray'); - - $this->assertIsArray($arr); - $this->assertSame('1', $arr['random']); - } - - public function testGetArrayWithDefaultValue(): void - { - $config = $this->getConfig(); - - $arr = $config->getArray('not-found', ['number' => 1]); - - $this->assertIsArray($arr); - $this->assertSame(1, $arr['number']); - } - - public function testGetArrayWithInvalidValueReturnsEmptyArray(): void - { - $config = $this->getConfig(); - - $arr = $config->getArray('testString'); - $this->assertEmpty($arr); - } - - public function testGetArrayNotFoundEmptyArray(): void - { - $config = $this->getConfig(); - - $arr = $config->getArray('not-found'); - $this->assertEmpty($arr); - } - - public function testGet(): void - { - $config = $this->getConfig(); - - $this->assertSame('string', $config->get('testString')); - } - - public function testGetWithDefault(): void - { - $config = $this->getConfig(); - - $this->assertSame(1, $config->get('not-found', 1)); - } - - public function testGetNestedValueByDotKey(): void - { - $config = $this->getConfig(); - - $this->assertSame(1, $config->getInt('deep.nesting.int')); - $this->assertSame('test', $config->getString('deep.nesting.string')); - } - - public function testGetNestedValueByDotKeyNotFound(): void - { - $config = $this->getConfig(); - - $this->assertNull($config->getInt('deep.nesting.not-found.int')); - } - - public function testGetNestedValueHasLowerPriorityThanExactMatch(): void - { - $config = $this->getConfig(); - - $this->assertSame(1, $config->get('nesting.test.value')); - } - - public function testDotKeyReturnEmptyOnExtraDots(): void - { - $config = $this->getConfig(); - - $this->assertNull($config->get('nesting.')); - $this->assertNull($config->get('.nesting')); - } - public function getConfig(): ArrayConfig { - return new ArrayConfig([ - 'testString' => 'string', - 'testNumberAsString' => '43', - 'testNumber' => 42, - 'testBool' => true, - 'testBoolAsString' => 'on', - 'testArray' => [ - 'random' => '1', - ], - 'deep' => [ - 'nesting' => [ - 'int' => 1, - 'string' => 'test', - ], - ], - 'nesting.test.value' => 1, - 'nesting' => [ - 'test' => [ - 'value' => 2, - ], - ], - ]); + return $this->getDefaultArrayConfig(); } } diff --git a/tests/ChainConfigTest.php b/tests/ChainConfigTest.php index bbc13c6..6b95f9f 100644 --- a/tests/ChainConfigTest.php +++ b/tests/ChainConfigTest.php @@ -7,107 +7,8 @@ use Stefna\Config\ChainConfig; use Stefna\Config\Tests\Stub\TestStub; -final class ChainConfigTest extends TestCase +final class ChainConfigTest extends AbstractConfigTestCase { - public function testGetArrayObject(): void - { - $config = $this->getConfig(); - - $obj = $config->getArrayAsObject('testArray', TestStub::class); - - $this->assertInstanceOf(TestStub::class, $obj); - $this->assertSame(1, $obj->random); - } - - public function testGetArrayObjectWithInvalidClass(): void - { - $config = $this->getConfig(); - - $this->expectException(\BadMethodCallException::class); - - $config->getArrayAsObject('testArray', \ArrayObject::class); - } - - public function testGetArray(): void - { - $config = $this->getConfig(); - - $arr = $config->getArray('testArray'); - - $this->assertIsArray($arr); - $this->assertSame('1', $arr['random']); - } - - public function testGetArrayWithDefaultValue(): void - { - $config = $this->getConfig(); - - $arr = $config->getArray('not-found', ['number' => 1]); - - $this->assertIsArray($arr); - $this->assertSame(1, $arr['number']); - } - - public function testGetArrayWithInvalidValueReturnsEmptyArray(): void - { - $config = $this->getConfig(); - - $arr = $config->getArray('testString'); - $this->assertEmpty($arr); - } - - public function testGetArrayNotFoundEmptyArray(): void - { - $config = $this->getConfig(); - - $arr = $config->getArray('not-found'); - $this->assertEmpty($arr); - } - - public function testGet(): void - { - $config = $this->getConfig(); - - $this->assertSame('string', $config->get('testString')); - } - - public function testGetWithDefault(): void - { - $config = $this->getConfig(); - - $this->assertSame(1, $config->get('not-found', 1)); - } - - public function testGetNestedValueByDotKey(): void - { - $config = $this->getConfig(); - - $this->assertSame(1, $config->getInt('deep.nesting.int')); - $this->assertSame('test', $config->getString('deep.nesting.string')); - } - - public function testGetNestedValueByDotKeyNotFound(): void - { - $config = $this->getConfig(); - - $this->assertNull($config->getInt('deep.nesting.not-found.int')); - } - - public function testGetNestedValueHasLowerPriorityThanExactMatch(): void - { - $config = $this->getConfig(); - - $this->assertSame(1, $config->get('nesting.test.value')); - } - - public function testDotKeyReturnEmptyOnExtraDots(): void - { - $config = $this->getConfig(); - - $this->assertNull($config->get('nesting.')); - $this->assertNull($config->get('.nesting')); - } - public function testGetValueFromSecondConfigArray(): void { $config = $this->getConfig(); @@ -119,28 +20,7 @@ public function testGetValueFromSecondConfigArray(): void public function getConfig(): ChainConfig { return new ChainConfig( - new ArrayConfig([ - 'testString' => 'string', - 'testNumberAsString' => '43', - 'testNumber' => 42, - 'testBool' => true, - 'testBoolAsString' => 'on', - 'testArray' => [ - 'random' => '1', - ], - 'deep' => [ - 'nesting' => [ - 'int' => 1, - 'string' => 'test', - ], - ], - 'nesting.test.value' => 1, - 'nesting' => [ - 'test' => [ - 'value' => 2, - ], - ], - ]), + $this->getDefaultArrayConfig(), new ArrayConfig([ 'secondConfig' => [ 'nest' => true, diff --git a/tests/MutableConfigTest.php b/tests/MutableConfigTest.php new file mode 100644 index 0000000..9c9f534 --- /dev/null +++ b/tests/MutableConfigTest.php @@ -0,0 +1,59 @@ +getConfig(); + $key = 'testString'; + $originalValue = $config->get($key); + $newValue = 'overrideValue'; + + $config->setConfigValue($key, $newValue); + + $this->assertNotSame($originalValue, $newValue); + $this->assertSame($newValue, $config->getString($key)); + } + + public function testOverrideDotKeyValues(): void + { + $config = $this->getConfig(); + $key = 'deep.nesting.int'; + $originalValue = $config->get($key); + $newValue = 42; + + $config->setConfigValue($key, $newValue); + + $this->assertNotSame($originalValue, $newValue); + $this->assertSame($newValue, $config->getInt($key)); + } + + public function testOverrideTestValue(): void + { + $config = $this->getConfig(); + $key = 'nesting.test'; + $originalValue = $config->get($key); + $newValue = 'overrideValue'; + + $config->setConfigValue($key, $newValue); + + $this->assertNotSame($originalValue, $newValue); + $this->assertSame($newValue, $config->get($key)); + + $config->resetConfigValue($key); + + $this->assertSame($originalValue, $config->get($key)); + } + + public function getConfig(): MutableConfig + { + return new MutableConfig($this->getDefaultArrayConfig()); + } +}