diff --git a/.github/workflows/coverage_report.yml b/.github/workflows/coverage_report.yml
index 9347537..c5bd39b 100644
--- a/.github/workflows/coverage_report.yml
+++ b/.github/workflows/coverage_report.yml
@@ -4,25 +4,44 @@ on:
push:
branches: [master]
jobs:
+ install_tools:
+ name: Install tools
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v7
+ - name: Install tools
+ uses: ngmy/phive-install-action@master
+ - name: Upload tools to artifact store
+ uses: actions/upload-artifact@v7
+ with:
+ name: tools
+ path: tools
coverage:
name: coverage
+ needs: install_tools
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v7
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
- php-version: 8.2
+ php-version: 8.5
coverage: pcov
- name: Install composer dependencies
- uses: ramsey/composer-install@v3
+ uses: ramsey/composer-install@v4
with:
composer-options: "--prefer-dist --optimize-autoloader"
+ - name: Download tools from artifact store
+ uses: actions/download-artifact@v7
+ with:
+ name: tools
+ path: tools
- name: Build the code coverage report
run: composer coverage:clover
- name: Upload code coverage report
- uses: qltysh/qlty-action/coverage@v1
+ uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
files: clover.xml
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index aab9dea..ce1ab00 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -2,28 +2,51 @@
name: Tests
on: [push, pull_request]
jobs:
+ install_tools:
+ name: Install tools
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v7
+ - name: Install tools
+ uses: ngmy/phive-install-action@master
+ - name: Upload tools to artifact store
+ uses: actions/upload-artifact@v7
+ with:
+ name: tools
+ path: tools
test:
name: test
+ needs: install_tools
strategy:
fail-fast: false
matrix:
- operating-system: [ubuntu-latest, windows-latest, macOS-latest]
- php-versions: ['8.2', '8.3', '8.4']
+ operating-system: ["ubuntu-latest", "windows-latest", "macOs-latest"]
+ php-versions: ["8.5"]
runs-on: ${{ matrix.operating-system }}
steps:
- name: Set autocrlf on windows
if: matrix.operating-system == 'windows-latest'
run: git config --global core.autocrlf false
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v7
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
coverage: none
- name: Install composer dependencies
- uses: ramsey/composer-install@v3
+ uses: ramsey/composer-install@v4
with:
composer-options: "--prefer-dist --optimize-autoloader"
+ - name: Download tools from artifact store
+ uses: actions/download-artifact@v7
+ with:
+ name: tools
+ path: tools
+ - name: Check coding standard
+ run: composer cs:check
+ - name: Static analysis tool
+ run: composer analytics
- name: Test suite
- run: composer test
+ run: composer test
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index a64fced..94e1ead 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ clover.xml
composer.phar
composer.lock
coverage/
+tools/
vendor/
diff --git a/.phive/phars.xml b/.phive/phars.xml
new file mode 100644
index 0000000..b78ac6b
--- /dev/null
+++ b/.phive/phars.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index 81b8ad2..87042fc 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -1,16 +1,18 @@
getFinder()
- ->in(__DIR__ . '/src')
- ->in(__DIR__ . '/tests')
-;
+$finder = new PhpCsFixer\Finder()->in(__DIR__ . '/src')->in(__DIR__ . '/tests');
-return $config;
+return new PhpCsFixer\Config()->setRules(['@PER-CS3x0' => true])->setFinder($finder);
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d9f1119..143fa3b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,18 +4,29 @@ First of all, many thanks to spend your time on this library!
## Workflow
-1. Fork [susina/param-resolver](https://github.com/susina/param-resolver) repository, clone it locally and apply your patches.
-2. Run the test suite by `composer test` command and fix all red tests.
-3. Run static analysis tool by `composer analytics` command and fix all errors.
-4. Fix the coding standard by running `composer cs:fix`.
+### Pre-requisites
-We provide a __check__ command for all the previously described actions: run `composer check` before submitting a pull request.
+1. Have [PHP 8.5](https://php.net) installed.
+2. Have [Composer](https://getcomposer.org) installed to manage dependencies and autoloading.
+3. Have [Phive](https://phive.io) to install and manage our development tools (PhpUnit, PhpStan etc.) avoiding dependencies conflicts.
+
+1. Fork [susina/param-resolver](https://github.com/susina/param-resolver) repository.
+2. Run `composer install` to install dependencies and create the correct autoloading map.
+3. Run `phive install` to safely install our development tools.
+4. Apply your patches.
+5. Run the test suite by `composer test` command and fix all red tests.
+6. Run static analysis tool by `composer analytics` command and fix all errors.
+7. Fix the coding standard by running `composer cs:fix`.
+
+> [!TIP]
+> We provide a __check__ command which runs the test suite, analytics tool and coding standard fix.
+> So, before submitting a pull request you can simply run `composer check`.
## Running the Test Suite
While developing, the test part is very important: if you apply a patch to the existing code, the test suite must run without errors or failures and if you add a new functionality, no one will consider it without tests.
-Our test tool is [Pest](https://pestphp.com/) and we provide a script to launch it:
+Our test tool is [PhpUnit](https://phpunit.de/) and we provide a script to launch it:
```bash
composer test
@@ -23,23 +34,22 @@ composer test
## Code Coverage
-We provides three commands to print the code coverage report or to generate it in _html_ or _xml_ format:
+We provides two commands to generate the code coverage report in _html_ or _xml_ format:
-- `composer coverage` to print a coverage summary on your console
- `composer coverage:html` command generates a code coverage report in _html_ format, into the directory `coverage/`
- `composer coverage:clover` generates the report in _xml_ format, into `clover.xml` file.
## Static Analysis Tool
-To prevent as many bugs as possible, we use a static analysis tool called [Psalm](https://psalm.dev/).
+To prevent as many bugs as possible, we use a static analysis tool called [PHPStan](https://phpstan.org/).
To launch it, run the following command:
```bash
composer analytics
```
-After its analysis, Psalm outputs errors and issues with its suggestions on how to fix them.
+After its analysis, PHPStan outputs errors and issues with its suggestions on how to fix them.
## Coding Standard
@@ -57,4 +67,4 @@ and to show the errors without fixing them, run:
composer cs:check
```
-If you want to learn more about our code style, see [https://github.com/susina/coding-standard](https://github.com/susina/coding-standard).
+All the repositories inside Susina Project follow [PER 3.x](https://www.php-fig.org/per/coding-style/) coding style.
\ No newline at end of file
diff --git a/composer.json b/composer.json
index cb5d5ea..0123e56 100644
--- a/composer.json
+++ b/composer.json
@@ -10,7 +10,7 @@
}
],
"require": {
- "php": ">=8.2"
+ "php": ">=8.5"
},
"autoload": {
"psr-4": {
@@ -22,38 +22,26 @@
"Susina\\ParamResolver\\Tests\\": "tests/"
}
},
- "require-dev": {
- "psalm/phar": "^6",
- "susina/coding-standard": "^2",
- "pestphp/pest": "^3"
- },
"scripts": {
- "analytics": "php vendor/bin/psalm.phar",
+ "analytics": "php tools/phpstan",
"check": [
"@test",
"@analytics",
"@cs:fix"
],
- "coverage": "@test --coverage",
"coverage:html": "@test --coverage-html coverage/",
"coverage:clover": "@test --coverage-clover clover.xml",
- "cs:check": "php-cs-fixer fix -v --diff --dry-run",
- "cs:fix": "php-cs-fixer fix -v --diff",
- "test": "pest"
+ "cs:check": "php tools/php-cs-fixer fix -v --diff --dry-run",
+ "cs:fix": "php tools/php-cs-fixer fix -v --diff",
+ "test": "php tools/phpunit"
},
"scripts-descriptions": {
"analytics": "Run static analysis tool",
"check": "Perform all tests and analysis, required before submitting a pull request",
"cs:check": "Run code style analysis, without fixing errors",
"cs:fix": "Run code style analysis and fix errors",
- "coverage": "Show coverage report to the console",
"coverage:html": "Create a code coverage report in html format, into the `coverage/` directory",
"coverage:clover": "Create a code coverage report in xml format, into the `clover.xml` file",
"test": "Run the test suite"
- },
- "config": {
- "allow-plugins": {
- "pestphp/pest-plugin": true
- }
}
}
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..c308dcf
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,4 @@
+parameters:
+ level: 8
+ paths:
+ - src
diff --git a/phpunit.xml b/phpunit.xml
index 7d0904f..0c12bb9 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -11,7 +11,6 @@
- ./app
./src
diff --git a/psalm.xml b/psalm.xml
deleted file mode 100644
index 2d01da2..0000000
--- a/psalm.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/Exception/ParamResolverException.php b/src/Exception/ParamResolverException.php
index c6b46d4..64e3ddc 100644
--- a/src/Exception/ParamResolverException.php
+++ b/src/Exception/ParamResolverException.php
@@ -1,6 +1,8 @@
-config = $configuration;
$parameters = [];
foreach ($configuration as $key => $value) {
- $key = $this->resolveValue($key);
+ $key = $this->resolveKey($key);
$value = $this->resolveValue($value);
$parameters[$key] = $this->unescapeValue($value);
}
@@ -72,8 +73,9 @@ public function resolve(array $configuration): array
* Replaces parameter placeholders (%name%) by their values.
*
* @param mixed $value The value to be resolved
- * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
+ * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
*
+ * @throws ParamResolverException If a parameter is not found or if a circular reference is detected.
* @return mixed The resolved value
*/
private function resolveValue(mixed $value, array $resolving = []): mixed
@@ -81,7 +83,7 @@ private function resolveValue(mixed $value, array $resolving = []): mixed
if (is_array($value)) {
$args = [];
foreach ($value as $k => $v) {
- $args[$this->resolveValue($k, $resolving)] = $this->resolveValue($v, $resolving);
+ $args[$this->resolveKey($k, $resolving)] = $this->resolveValue($v, $resolving);
}
return $args;
@@ -98,8 +100,9 @@ private function resolveValue(mixed $value, array $resolving = []): mixed
* Resolves parameters inside a string
*
* @param string $value The string to resolve
- * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
+ * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
*
+ * @throws ParamResolverException If a parameter is not found or if a circular reference is detected.
* @return mixed The resolved value
*/
private function resolveString(string $value, array $resolving = []): mixed
@@ -112,9 +115,9 @@ private function resolveString(string $value, array $resolving = []): mixed
* otherwise, it is replaced with the resolved string or number.
*/
- /** @var mixed */
- $onlyKey = null;
- $replaced = preg_replace_callback('/%([^%\s]*+)%/', function (array $match) use ($resolving, $value, &$onlyKey) {
+ /** @var string */
+ $onlyKey = '';
+ $replaced = preg_replace_callback('/%([^%\s]*+)%/', function (array $match) use ($resolving, $value, &$onlyKey): string {
$key = $match[1];
$env = $this->parseEnvironmentParams($key);
@@ -122,7 +125,7 @@ private function resolveString(string $value, array $resolving = []): mixed
$key === '' => '%%',
$env !== null => $env,
isset($resolving[$key]) => throw new ParamResolverException("Circular reference detected for parameter '$key'."),
- default => null
+ default => null,
};
if ($out !== null) {
@@ -142,7 +145,7 @@ private function resolveString(string $value, array $resolving = []): mixed
return $this->resolveString($resolved, $resolving);
}, $value);
- if ($setKey = isset($onlyKey)) {
+ if ($setKey = ($onlyKey !== '')) {
$resolving[$onlyKey] = true;
}
@@ -194,7 +197,7 @@ private function get(int|string $propertyKey): mixed
* Scan recursively an array to find a value of a given key.
*
* @param int|string $propertyKey The array key
- * @param array $config The array to scan
+ * @param mixed[] $config The array to scan
*
* @return \Generator The value or null if not found
*/
@@ -228,4 +231,23 @@ private function parseEnvironmentParams(string $value): ?string
return Validator::validateEnvParam(substr($value, 4));
}
+
+ /**
+ * Resolve a value used as an array key.
+ *
+ * @param int|string $key
+ * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
+ * @throws ParamResolverException
+ * @return int|string
+ */
+ private function resolveKey(int|string $key, array $resolving = []): int|string
+ {
+ $resolved = $this->resolveValue($key, $resolving);
+
+ if (!is_string($resolved) && !is_int($resolved)) {
+ throw new ParamResolverException("Resolved key must be a string or an integer, got " . gettype($resolved));
+ }
+
+ return $resolved;
+ }
}
diff --git a/src/Validator.php b/src/Validator.php
index 9b5cabe..ccbe086 100644
--- a/src/Validator.php
+++ b/src/Validator.php
@@ -1,6 +1,8 @@
- 'bar', 'I\'m a %foo%'],
- ['foo' => 'bar', 'I\'m a bar'],
- ],
- [
- ['foo' => 'bar', '%foo%' => '%foo%'],
- ['foo' => 'bar', 'bar' => 'bar'],
- ],
- [
- ['foo' => 'bar', '%foo%' => ['%foo%' => ['%foo%' => '%foo%']]],
- ['foo' => 'bar', 'bar' => ['bar' => ['bar' => 'bar']]],
- ],
- [
- ['foo' => 'bar', 'I\'m a %%foo%%'],
- ['foo' => 'bar', 'I\'m a %foo%'],
- ],
- [
- ['foo' => 'bar', 'I\'m a %foo% %%foo %foo%'],
- ['foo' => 'bar', 'I\'m a bar %foo bar'],
- ],
- [
- ['foo' => ['bar' => ['ding' => 'I\'m a bar %%foo %%bar']]],
- ['foo' => ['bar' => ['ding' => 'I\'m a bar %foo %bar']]],
- ],
- [
- ['foo' => 'bar', 'baz' => '%%%foo% %foo%%% %%foo%% %%%foo%%%'],
- ['foo' => 'bar', 'baz' => '%bar bar% %foo% %bar%'],
- ],
- [
- ['baz' => '%%s?%%s', '%baz%'],
- ['baz' => '%s?%s', '%s?%s'],
- ],
- [
- ['host' => 'foo.bar', 'port' => 1337, '%host%:%port%'],
- ['host' => 'foo.bar', 'port' => 1337, 'foo.bar:1337'],
- ],
- [
- ['foo' => 'bar', '%foo%'],
- ['foo' => 'bar', 'bar'],
- ],
- [
- ['foo' => 'bar', '% foo %'],
- ['foo' => 'bar', '% foo %'],
- ],
- [
- ['foo' => 'bar', '{% set my_template = "foo" %}'],
- ['foo' => 'bar', '{% set my_template = "foo" %}'],
- ],
- [
- ['foo' => 'bar', '50% is less than 100%'],
- ['foo' => 'bar', '50% is less than 100%'],
- ],
- [
- ['foo' => ['bar' => 'baz', '%bar%' => 'babar'], 'babaz' => '%foo%'],
- ['foo' => ['bar' => 'baz', 'baz' => 'babar'], 'babaz' => ['bar' => 'baz', 'baz' => 'babar']],
- ],
- [
- ['foo' => ['bar' => 'baz'], 'babaz' => '%foo%'],
- ['foo' => ['bar' => 'baz'], 'babaz' => ['bar' => 'baz']],
- ]
-]);
diff --git a/tests/ParamResolverTest.php b/tests/ParamResolverTest.php
new file mode 100644
index 0000000..827990f
--- /dev/null
+++ b/tests/ParamResolverTest.php
@@ -0,0 +1,294 @@
+resolver = new ParamResolver();
+ }
+
+ #[DataProvider('providerForResolveParams')]
+ public function testResolveValues(array $conf, array $expected): void
+ {
+ $this->assertSame($expected, $this->resolver->resolve($conf));
+ }
+
+ #[DataProvider('providerForResolveParams')]
+ public function testResolveValuesStatic(array $conf, array $expected): void
+ {
+ $this->assertSame($expected, ParamResolver::create()->resolve($conf));
+ }
+
+ public function testResolveParametersWithEnvironmentVariables(): void
+ {
+ putenv('host=127.0.0.1');
+ putenv('user=root');
+
+ $config = [
+ 'HoMe' => 'myHome',
+ 'project' => 'myProject',
+ 'subhome' => '%HoMe%/subhome',
+ 'property1' => 1,
+ 'property2' => false,
+ 'directories' => [
+ 'project' => '%HoMe%/projects/%project%',
+ 'conf' => '%project%',
+ 'schema' => '%project%/schema',
+ 'template' => '%HoMe%/templates',
+ 'output%project%' => '/build',
+ ],
+ '%HoMe%' => 4,
+ 'host' => '%env.host%',
+ 'user' => '%env.user%',
+ ];
+
+ $expected = [
+ 'HoMe' => 'myHome',
+ 'project' => 'myProject',
+ 'subhome' => 'myHome/subhome',
+ 'property1' => 1,
+ 'property2' => false,
+ 'directories' => [
+ 'project' => 'myHome/projects/myProject',
+ 'conf' => 'myProject',
+ 'schema' => 'myProject/schema',
+ 'template' => 'myHome/templates',
+ 'outputmyProject' => '/build',
+ ],
+ 'myHome' => 4,
+ 'host' => '127.0.0.1',
+ 'user' => 'root',
+ ];
+
+ $this->assertSame($expected, $this->resolver->resolve($config));
+
+ //cleanup environment
+ putenv('host');
+ putenv('user');
+ }
+
+ public function testDoesNotCastToStringsTheReplacedValues(): void
+ {
+ $conf = $this->resolver->resolve(['foo' => true, 'expfoo' => '%foo%', 'bar' => null, 'expbar' => '%bar%']);
+
+ $this->assertTrue($conf['expfoo']);
+ $this->assertNull($conf['expbar']);
+ }
+
+ public function testInvalidPlaceholdersThrowsException(): void
+ {
+ $this->expectException(ParamResolverException::class);
+ $this->expectExceptionMessageIs("Parameter 'baz' not found.");
+
+ $this->resolver->resolve(['foo' => 'bar', '%baz%']);
+ }
+
+ public function testNotExistentPlaceholderThrowsException(): void
+ {
+ $this->expectException(ParamResolverException::class);
+ $this->expectExceptionMessageIs("Parameter 'foobar' not found.");
+
+ $this->resolver->resolve(['foo %foobar% bar']);
+ }
+
+ public function testSimpleCircularReferenceThrowsException(): void
+ {
+ $this->expectException(ParamResolverException::class);
+ $this->expectExceptionMessageIs("Circular reference detected for parameter 'bar'.");
+
+ $this->resolver->resolve(['foo' => '%bar%', 'bar' => '%foobar%', 'foobar' => '%foo%']);
+ }
+
+ public function testComplexCircularReferenceThrowsException(): void
+ {
+ $this->expectException(ParamResolverException::class);
+ $this->expectExceptionMessageIs("Circular reference detected for parameter 'bar'.");
+
+ $this->resolver->resolve(['foo' => 'a %bar%', 'bar' => 'a %foobar%', 'foobar' => 'a %foo%']);
+ }
+
+ public function testResolveWithEnvironmentVariables(): void
+ {
+ putenv('home=myHome');
+ putenv('schema=mySchema');
+ putenv('isBoolean=true');
+ putenv('integer=1');
+
+ $config = [
+ 'home' => '%env.home%',
+ 'property1' => '%env.integer%',
+ 'property2' => '%env.isBoolean%',
+ 'direcories' => [
+ 'projects' => '%home%/projects',
+ 'schema' => '%env.schema%',
+ 'template' => '%home%/templates',
+ 'output%env.home%' => '/build',
+ ],
+ ];
+
+ $expected = [
+ 'home' => 'myHome',
+ 'property1' => '1',
+ 'property2' => 'true',
+ 'direcories' => [
+ 'projects' => 'myHome/projects',
+ 'schema' => 'mySchema',
+ 'template' => 'myHome/templates',
+ 'outputmyHome' => '/build',
+ ],
+ ];
+
+ $this->assertEquals($expected, $this->resolver->resolve($config));
+
+ //cleanup environment
+ putenv('home');
+ putenv('schema');
+ putenv('isBoolean');
+ putenv('integer');
+ }
+
+ public function testResolveParametersWithEmptyEnvironmentVariable(): void
+ {
+ putenv('home=');
+
+ $config = [
+ 'home' => '%env.home%',
+ ];
+
+ $expected = [
+ 'home' => '',
+ ];
+
+ $this->assertEquals($expected, $this->resolver->resolve($config));
+
+ //cleanup environment
+ putenv('home');
+ }
+
+ public function testNotExistentEnvironmentVariablesThrowException(): void
+ {
+ $this->expectException(ParamResolverException::class);
+ $this->expectExceptionMessageIs("Environment variable 'foo' is not defined.");
+
+ $this->resolver->resolve(['home' => '%env.foo%']);
+ }
+
+ public function testNonStringParameterThrowsException(): void
+ {
+ $this->expectException(ParamResolverException::class);
+ $this->expectExceptionMessageIs('A string value must be composed of strings and/or numbers.');
+
+ $config = [
+ 'foo' => 'a %bar%',
+ 'bar' => [],
+ 'baz' => '%foo%',
+ ];
+
+ $this->resolver->resolve($config);
+ }
+
+ public function testResolveParamTwice(): void
+ {
+ $config = [
+ 'foo' => 'bar',
+ 'baz' => '%foo%',
+ ];
+
+ $this->assertSame(['foo' => 'bar', 'baz' => 'bar'], $this->resolver->resolve($config));
+ $this->assertSame([], $this->resolver->resolve($config));
+ }
+
+ public static function providerForResolveParams(): array
+ {
+ return [
+ [
+ ['foo'],
+ ['foo'],
+ ],
+ [
+ ['foo' => 'bar', 'I\'m a %foo%'],
+ ['foo' => 'bar', 'I\'m a bar'],
+ ],
+ [
+ ['foo' => 'bar', '%foo%' => '%foo%'],
+ ['foo' => 'bar', 'bar' => 'bar'],
+ ],
+ [
+ ['foo' => 'bar', '%foo%' => ['%foo%' => ['%foo%' => '%foo%']]],
+ ['foo' => 'bar', 'bar' => ['bar' => ['bar' => 'bar']]],
+ ],
+ [
+ ['foo' => 'bar', 'I\'m a %%foo%%'],
+ ['foo' => 'bar', 'I\'m a %foo%'],
+ ],
+ [
+ ['foo' => 'bar', 'I\'m a %foo% %%foo %foo%'],
+ ['foo' => 'bar', 'I\'m a bar %foo bar'],
+ ],
+ [
+ ['foo' => ['bar' => ['ding' => 'I\'m a bar %%foo %%bar']]],
+ ['foo' => ['bar' => ['ding' => 'I\'m a bar %foo %bar']]],
+ ],
+ [
+ ['foo' => 'bar', 'baz' => '%%%foo% %foo%%% %%foo%% %%%foo%%%'],
+ ['foo' => 'bar', 'baz' => '%bar bar% %foo% %bar%'],
+ ],
+ [
+ ['baz' => '%%s?%%s', '%baz%'],
+ ['baz' => '%s?%s', '%s?%s'],
+ ],
+ [
+ ['host' => 'foo.bar', 'port' => 1337, '%host%:%port%'],
+ ['host' => 'foo.bar', 'port' => 1337, 'foo.bar:1337'],
+ ],
+ [
+ ['foo' => 'bar', '%foo%'],
+ ['foo' => 'bar', 'bar'],
+ ],
+ [
+ ['foo' => 'bar', '% foo %'],
+ ['foo' => 'bar', '% foo %'],
+ ],
+ [
+ ['foo' => 'bar', '{% set my_template = "foo" %}'],
+ ['foo' => 'bar', '{% set my_template = "foo" %}'],
+ ],
+ [
+ ['foo' => 'bar', '50% is less than 100%'],
+ ['foo' => 'bar', '50% is less than 100%'],
+ ],
+ [
+ ['foo' => ['bar' => 'baz', '%bar%' => 'babar'], 'babaz' => '%foo%'],
+ ['foo' => ['bar' => 'baz', 'baz' => 'babar'], 'babaz' => ['bar' => 'baz', 'baz' => 'babar']],
+ ],
+ [
+ ['foo' => ['bar' => 'baz'], 'babaz' => '%foo%'],
+ ['foo' => ['bar' => 'baz'], 'babaz' => ['bar' => 'baz']],
+ ],
+ ];
+ }
+}
diff --git a/tests/Pest.php b/tests/Pest.php
deleted file mode 100644
index f4165e8..0000000
--- a/tests/Pest.php
+++ /dev/null
@@ -1 +0,0 @@
-resolver = new ParamResolver();
-});
-
-it('resolves parameters', function () {
- putenv('host=127.0.0.1');
- putenv('user=root');
-
- $config = [
- 'HoMe' => 'myHome',
- 'project' => 'myProject',
- 'subhome' => '%HoMe%/subhome',
- 'property1' => 1,
- 'property2' => false,
- 'directories' => [
- 'project' => '%HoMe%/projects/%project%',
- 'conf' => '%project%',
- 'schema' => '%project%/schema',
- 'template' => '%HoMe%/templates',
- 'output%project%' => '/build',
- ],
- '%HoMe%' => 4,
- 'host' => '%env.host%',
- 'user' => '%env.user%',
- ];
-
- $expected = [
- 'HoMe' => 'myHome',
- 'project' => 'myProject',
- 'subhome' => 'myHome/subhome',
- 'property1' => 1,
- 'property2' => false,
- 'directories' => [
- 'project' => 'myHome/projects/myProject',
- 'conf' => 'myProject',
- 'schema' => 'myProject/schema',
- 'template' => 'myHome/templates',
- 'outputmyProject' => '/build',
- ],
- 'myHome' => 4,
- 'host' => '127.0.0.1',
- 'user' => 'root',
- ];
-
- expect($expected)->toBe($this->resolver->resolve($config));
-
- //cleanup environment
- putenv('host');
- putenv('user');
-});
-
-it('resolves parameters via static instantiation', function () {
- putenv('host=127.0.0.1');
- putenv('user=root');
-
- $config = [
- 'HoMe' => 'myHome',
- 'project' => 'myProject',
- 'subhome' => '%HoMe%/subhome',
- 'property1' => 1,
- 'property2' => false,
- 'directories' => [
- 'project' => '%HoMe%/projects/%project%',
- 'conf' => '%project%',
- 'schema' => '%project%/schema',
- 'template' => '%HoMe%/templates',
- 'output%project%' => '/build',
- ],
- '%HoMe%' => 4,
- 'host' => '%env.host%',
- 'user' => '%env.user%',
- ];
-
- $expected = [
- 'HoMe' => 'myHome',
- 'project' => 'myProject',
- 'subhome' => 'myHome/subhome',
- 'property1' => 1,
- 'property2' => false,
- 'directories' => [
- 'project' => 'myHome/projects/myProject',
- 'conf' => 'myProject',
- 'schema' => 'myProject/schema',
- 'template' => 'myHome/templates',
- 'outputmyProject' => '/build',
- ],
- 'myHome' => 4,
- 'host' => '127.0.0.1',
- 'user' => 'root',
- ];
-
- expect($expected)->toBe(ParamResolver::create()->resolve($config));
-
- //cleanup environment
- putenv('host');
- putenv('user');
-});
-
-
-it('resolves values', function (array $conf, array $expected) {
- expect($expected)->toBe($this->resolver->resolve($conf));
-})->with('resolveParams');
-
-it('does not cast to strigs the replaced values', function () {
- $conf = $this->resolver->resolve(['foo' => true, 'expfoo' => '%foo%', 'bar' => null, 'expbar' => '%bar%']);
-
- expect($conf['expfoo'])->toBeTrue()->and($conf['expbar'])->toBeNull();
-});
-
-it('finds invalid placeholders', fn () => $this->resolver->resolve(['foo' => 'bar', '%baz%']))
- ->throws(ParamResolverException::class, "Parameter 'baz' not found.");
-
-it('finds not existent placeholder', fn () => $this->resolver->resolve(['foo %foobar% bar']))
- ->throws(ParamResolverException::class, "Parameter 'foobar' not found.");
-
-it('discovers simple circular reference', fn () => $this->resolver->resolve(['foo' => '%bar%', 'bar' => '%foobar%', 'foobar' => '%foo%']))
- ->throws(ParamResolverException::class, "Circular reference detected for parameter 'bar'.");
-
-it('discovers complex circular reference', fn () => $this->resolver->resolve(['foo' => 'a %bar%', 'bar' => 'a %foobar%', 'foobar' => 'a %foo%']))
- ->throws(ParamResolverException::class, "Circular reference detected for parameter 'bar'.");
-
-it('resolves environment variable parameters', function () {
- putenv('home=myHome');
- putenv('schema=mySchema');
- putenv('isBoolean=true');
- putenv('integer=1');
-
- $config = [
- 'home' => '%env.home%',
- 'property1' => '%env.integer%',
- 'property2' => '%env.isBoolean%',
- 'direcories' => [
- 'projects' => '%home%/projects',
- 'schema' => '%env.schema%',
- 'template' => '%home%/templates',
- 'output%env.home%' => '/build',
- ],
- ];
-
- $expected = [
- 'home' => 'myHome',
- 'property1' => '1',
- 'property2' => 'true',
- 'direcories' => [
- 'projects' => 'myHome/projects',
- 'schema' => 'mySchema',
- 'template' => 'myHome/templates',
- 'outputmyHome' => '/build',
- ],
- ];
-
- expect($this->resolver->resolve($config))->toBe($expected);
-
- //cleanup environment
- putenv('home');
- putenv('schema');
- putenv('isBoolean');
- putenv('integer');
-});
-
-it('resolves empty environment variable', function () {
- putenv('home=');
-
- $config = [
- 'home' => '%env.home%',
- ];
-
- $expected = [
- 'home' => '',
- ];
-
- expect($expected)->toBe($this->resolver->resolve($config));
-
- //cleanup environment
- putenv('home');
-});
-
-it('finds not existent environment variable', function () {
- putenv('home=myHome');
-
- $config = [
- 'home' => '%env.home%',
- 'property1' => '%env.foo%',
- ];
-
- $this->resolver->resolve($config);
-})->throws(ParamResolverException::class, "Environment variable 'foo' is not defined.");
-
-it('finds non string or number parameters', function () {
- $config = [
- 'foo' => 'a %bar%',
- 'bar' => [],
- 'baz' => '%foo%',
- ];
-
- $this->resolver->resolve($config);
-})->throws(ParamResolverException::class, 'A string value must be composed of strings and/or numbers.');
-
-it('resolve a param twice', function () {
- $config = [
- 'foo' => 'bar',
- 'baz' => '%foo%',
- ];
-
- expect(['foo' => 'bar', 'baz' => 'bar'])->toBe($this->resolver->resolve($config))
- ->and([])->toBe($this->resolver->resolve($config));
-});