Skip to content

Commit

Permalink
Merge pull request #97 from weierophinney/feature/configure-replacements
Browse files Browse the repository at this point in the history
Allow configuration of additional replacements
  • Loading branch information
Ocramius committed Jul 14, 2022
2 parents f53849b + a1479a9 commit d74d2da
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 4 deletions.
46 changes: 46 additions & 0 deletions README.md
Expand Up @@ -44,6 +44,52 @@ Run the following to install this library:
$ composer require laminas/laminas-zendframework-bridge
```

## Configuration

- Since 1.6.0

You may provide additional replacements for the configuration post processor.
This is particularly useful if your application uses third-party components that include class names that the post processor otherwise rewrites, and which you want to never rewrite.

Configuration is via the following structure:

```php
return [
'laminas-zendframework-bridge' => [
'replacements' => [
'to-replace' => 'replacement',
// ...
],
],
];
```

As an example, if your configuration included the following dependency mapping:

```php
return [
'controller_plugins' => [
'factories' => [
'customZendFormBinder' => \CustomZendFormBinder\Controller\Plugin\Factory\BinderPluginFactory::class,
],
],
];
```

And you wanted the two strings that contain the verbiage `ZendForm` to remain untouched, you could define the following replacements mapping:

```php
return [
'laminas-zendframework-bridge' => [
'replacements' => [
// Never rewrite!
'customZendFormBinder' => 'customZendFormBinder',
'CustomZendFormBinder' => 'CustomZendFormBinder',
],
],
];
```

## Support

* [Issues](https://github.com/laminas/laminas-zendframework-bridge/issues/)
Expand Down
11 changes: 8 additions & 3 deletions psalm-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.21.0@d8bec4c7aaee111a532daec32fb09de5687053d1">
<files psalm-version="4.9.3@4c262932602b9bbab5020863d1eb22d49de0dbf4">
<file src="config/replacements.php">
<DuplicateArrayKey occurrences="3">
<code>'ZendAcl' =&gt; 'LaminasAcl'</code>
Expand All @@ -8,7 +8,10 @@
</DuplicateArrayKey>
</file>
<file src="src/Autoloader.php">
<MixedArgumentTypeCoercion occurrences="2"/>
<MixedArgumentTypeCoercion occurrences="2">
<code>RewriteRules::namespaceReverse()</code>
<code>RewriteRules::namespaceRewrite()</code>
</MixedArgumentTypeCoercion>
</file>
<file src="src/ConfigPostProcessor.php">
<InvalidArgument occurrences="1">
Expand Down Expand Up @@ -59,7 +62,7 @@
<MixedArrayTypeCoercion occurrences="1">
<code>$aliases[$name]</code>
</MixedArrayTypeCoercion>
<MixedAssignment occurrences="25">
<MixedAssignment occurrences="27">
<code>$a[$key]</code>
<code>$a[$key]</code>
<code>$a[]</code>
Expand All @@ -75,9 +78,11 @@
<code>$key</code>
<code>$name</code>
<code>$newKey</code>
<code>$newValue</code>
<code>$notIn[]</code>
<code>$result</code>
<code>$rewritten[$key]</code>
<code>$rewritten[$key]</code>
<code>$rewritten[$newKey]</code>
<code>$rewritten[$newKey][]</code>
<code>$serviceInstance</code>
Expand Down
40 changes: 39 additions & 1 deletion src/ConfigPostProcessor.php
Expand Up @@ -2,6 +2,8 @@

namespace Laminas\ZendFrameworkBridge;

use RuntimeException;

use function array_intersect_key;
use function array_key_exists;
use function array_pop;
Expand Down Expand Up @@ -35,6 +37,7 @@ class ConfigPostProcessor

public function __construct()
{
// This value will be reset during __invoke(); setting here to prevent Psalm errors.
$this->replacements = new Replacements();

/* Define the rulesets for replacements.
Expand Down Expand Up @@ -96,9 +99,16 @@ function ($value, array $keys) {
*/
public function __invoke(array $config, array $keys = [])
{
$rewritten = [];
$this->replacements = $this->initializeReplacements($config);
$rewritten = [];

foreach ($config as $key => $value) {
// Do not rewrite configuration for the bridge
if ($key === 'laminas-zendframework-bridge') {
$rewritten[$key] = $value;
continue;
}

// Determine new key from replacements
$newKey = is_string($key) ? $this->replace($key, $keys) : $key;

Expand Down Expand Up @@ -423,4 +433,32 @@ private function replaceDependencyServices(array $config)

return $config;
}

private function initializeReplacements(array $config): Replacements
{
$replacements = $config['laminas-zendframework-bridge']['replacements'] ?? [];
if (! is_array($replacements)) {
throw new RuntimeException(sprintf(
'Invalid laminas-zendframework-bridge.replacements configuration;'
. ' value MUST be an array; received %s',
is_object($replacements) ? get_class($replacements) : gettype($replacements)
));
}

foreach ($replacements as $lookup => $replacement) {
if (
! is_string($lookup)
|| ! is_string($replacement)
|| preg_match('/^\s*$/', $lookup)
|| preg_match('/^\s*$/', $replacement)
) {
throw new RuntimeException(
'Invalid lookup or replacement in laminas-zendframework-bridge.replacements configuration;'
. ' all keys and values MUST be non-empty strings.'
);
}
}

return new Replacements($replacements);
}
}
107 changes: 107 additions & 0 deletions test/ConfigPostProcessorTest.php
Expand Up @@ -4,6 +4,7 @@

use Laminas\ZendFrameworkBridge\ConfigPostProcessor;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use stdClass;

use function sprintf;
Expand Down Expand Up @@ -137,4 +138,110 @@ public function invalidServiceManagerConfiguration()
],
];
}

public function testWillUseReplacementsFromConfigurationWhenPresent(): void
{
$config = [
'laminas-zendframework-bridge' => [
'replacements' => [
'MyFoo' => 'YourFoo',
'MyBar' => 'SomeBar',
],
],
'dependencies' => [
'factories' => [
'MyFoo' => 'MyBar',
'Zend\Cache\MyClass' => 'My\Factory\For\Caching',
'ShouldNotRewrite' => 'My\Factory\ShouldNotRewrite',
],
],
];

$expected = [
'laminas-zendframework-bridge' => [
'replacements' => [
'MyFoo' => 'YourFoo',
'MyBar' => 'SomeBar',
],
],
'dependencies' => [
'factories' => [
'YourFoo' => 'SomeBar',
'Laminas\Cache\MyClass' => 'My\Factory\For\Caching',
'ShouldNotRewrite' => 'My\Factory\ShouldNotRewrite',
],
'aliases' => [
'MyFoo' => 'YourFoo',
'Zend\Cache\MyClass' => 'Laminas\Cache\MyClass',
],
],
];

$processor = new ConfigPostProcessor();
$result = $processor($config);

$this->assertEquals($expected, $result);
}

/** @psalm-return iterable<string, array{0: array, 1: string}> */
public function invalidReplacementsConfiguration(): iterable
{
yield 'non-array-value' => [
['laminas-zendframework-bridge' => ['replacements' => new stdClass()]],
'MUST be an array',
];

yield 'empty-key' => [
['laminas-zendframework-bridge' => ['replacements' => [
'' => 'Laminas\FooBar',
]]],
'MUST be non-empty strings',
];

yield 'whitespace-key' => [
['laminas-zendframework-bridge' => ['replacements' => [
" \t\n" => 'Laminas\FooBar',
]]],
'MUST be non-empty strings',
];

yield 'integer-key' => [
['laminas-zendframework-bridge' => ['replacements' => [
1 => 'Laminas\FooBar',
]]],
'MUST be non-empty strings',
];

yield 'non-string-value' => [
['laminas-zendframework-bridge' => ['replacements' => [
'Zend' => new stdClass(),
]]],
'MUST be non-empty strings',
];

yield 'empty-value' => [
['laminas-zendframework-bridge' => ['replacements' => [
'Zend' => '',
]]],
'MUST be non-empty strings',
];

yield 'whitespace-value' => [
['laminas-zendframework-bridge' => ['replacements' => [
'Zend' => " \t\n",
]]],
'MUST be non-empty strings',
];
}

/** @dataProvider invalidReplacementsConfiguration */
public function testInvalidReplacementsConfigurationWillResultInExceptions(
array $config,
string $expectedExceptionMessage
): void {
$processor = new ConfigPostProcessor();
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage($expectedExceptionMessage);
$processor($config);
}
}

0 comments on commit d74d2da

Please sign in to comment.