Skip to content

Commit

Permalink
Support both positional and named parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
zluiten committed Aug 17, 2021
1 parent 0efd86c commit 48d452b
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 56 deletions.
28 changes: 23 additions & 5 deletions docs/basic/bean-parameters.md
Expand Up @@ -2,10 +2,16 @@

Bean instances can be parameterized by a given configuration. To access the configuration add a `#[Parameter]` attribute to your Bean method.

The `#[Parameter(name: 'paramName', key: 'config.key')]` attribute requires at least the `param` (which must match a param name of the Bean method) and the `key` which will be used to look for in the configuration array.
Configuration parameters can be passed to the Bean configuration method as [named](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments)
or as positional arguments. A `Parameter` will be used as a named argument when the `name` argument is set.

In the following example the value of configuration key `configKey` gets passed to the bean configuation for the argument named `$test`.
Configuration parameters are passed to the bean configuration method using [named arguments](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments):
So `#[Parameter(name: 'argName', key: 'config.key)]` is a named parameter whereas `#[Parameter(key: 'config.key')]` is a positional parameter.
As one might expect, the order of the positional parameters matter. Therefore, the recommended way of configuring parameters is using named parameters.
Named and positional parameters can be mixed.

The `#[Parameter]` attribute requires at least `key` which will be used to look for in the configuration array.

In the following example the value of configuration key `configKey` gets passed to the Bean configuration for the argument named `$test`.

```php
<?php
Expand All @@ -19,13 +25,24 @@ use bitExpert\Disco\Helper\SampleService;
class MyConfiguration
{
#[Bean]
#[Parameter(name: 'test', key: 'configKey')]
#[Parameter(name: 'test', key: 'configKey1')]
public function mySampleService(string $test = '') : SampleService
{
$service = new SampleService();
$service->setTest($test);
return $service;
}

#[Bean]
#[Parameter(name: 'anotherTest', key: 'configKey2')]
#[Parameter(key: 'configKey1')]
public function mySampleService(string $test = '', string $anotherTest = '') : SampleService
{
$service = new SampleService();
$service->setTest($test);
$service->setAnotherTest($anotherTest);
return $service;
}
}
```

Expand All @@ -34,7 +51,7 @@ The configuration array gets passed to the `\bitExpert\Disco\AnnotationBeanFacto
```php
<?php

$parameters = ['configKey' => 'This is a test.'];
$parameters = ['configKey1' => 'This is a test.' 'configKey2' => 'This is another test.'];

$beanFactory = new \bitExpert\Disco\AnnotationBeanFactory(
MyConfiguration::class,
Expand All @@ -45,6 +62,7 @@ $beanFactory = new \bitExpert\Disco\AnnotationBeanFactory(
$sampleService = $beanFactory->get('mySampleService');

echo $sampleService->test; // Output: This is a test.
echo $sampleService->anotherTest; // Output: This is another test.
```

## Default Parameter Values
Expand Down
5 changes: 2 additions & 3 deletions src/bitExpert/Disco/AnnotationBeanFactory.php
Expand Up @@ -32,14 +32,13 @@ class AnnotationBeanFactory implements BeanFactory
*
* @param class-string<Object> $configClassName
* @param array<string, mixed> $parameters
* @param BeanFactoryConfiguration $config
* @param BeanFactoryConfiguration|null $config
*/
public function __construct(
string $configClassName,
array $parameters = [],
BeanFactoryConfiguration $config = null
)
{
) {
if ($config === null) {
$config = new BeanFactoryConfiguration(sys_get_temp_dir());
}
Expand Down
20 changes: 10 additions & 10 deletions src/bitExpert/Disco/Annotations/Parameter.php
Expand Up @@ -24,37 +24,37 @@
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Parameter
{
private string $name;

private string $key;

private ?string $name;

private mixed $defaultValue;

private bool $required;

/**
* @param string $name
* @param string $key
* @param bool $required
* @param mixed|null $default
* @param mixed $default
* @param string|null $name
*/
public function __construct(string $name, string $key, bool $required = true, mixed $default = null)
public function __construct(string $key, bool $required = true, mixed $default = null, ?string $name = null)
{
Assert::minLength($name, 1);
Assert::minLength($key, 1);
Assert::nullOrMinLength($name, 1);

$this->name = $name;
$this->key = $key;
$this->name = $name;
$this->defaultValue = $default;
$this->required = $required;
}

/**
* Return the name of the parameter
* Return the name of the argument or null in case of a positioned argument
*
* @return string
* @return string|null
*/
public function getName(): string
public function getName(): ?string
{
return $this->name;
}
Expand Down
5 changes: 1 addition & 4 deletions src/bitExpert/Disco/BeanFactoryConfiguration.php
Expand Up @@ -144,10 +144,7 @@ public function getProxyManagerConfiguration(): Configuration
{
$proxyManagerConfiguration = new Configuration();
$proxyManagerConfiguration->setProxiesTargetDir($this->proxyTargetDir);

if ($this->proxyWriterGenerator instanceof GeneratorStrategyInterface) {
$proxyManagerConfiguration->setGeneratorStrategy($this->proxyWriterGenerator);
}
$proxyManagerConfiguration->setGeneratorStrategy($this->proxyWriterGenerator);

if ($this->proxyAutoloader instanceof AutoloaderInterface) {
$proxyManagerConfiguration->setProxyAutoloader($this->proxyAutoloader);
Expand Down
Expand Up @@ -32,9 +32,10 @@ protected static function convertMethodParamsToString(
array $methodParameters,
GetParameter $parameterValuesMethod
): string {
$parameters = [];
$positionalArgs = [];
$namedArgs = [];

foreach ($methodParameters as $methodParameter) {
/** @var Parameter $methodParameter */
$defaultValue = $methodParameter->getDefaultValue();
switch (\gettype($defaultValue)) {
case 'string':
Expand All @@ -50,12 +51,32 @@ protected static function convertMethodParamsToString(
break;
}

$template = ($defaultValue === '') ? '$this->%s("%s", %s)' : '$this->%s("%s", %s, %s)';
$required = $methodParameter->isRequired() ? 'true' : 'false';
$methodName = $parameterValuesMethod->getName();
$parameters[] = \sprintf($template, $methodName, $methodParameter->getKey(), $required, $defaultValue);
$argName = $methodParameter->getName();

if (null === $argName) {
$template = ($defaultValue === '') ? '$this->%s("%s", %s)' : '$this->%s("%s", %s, %s)';
$positionalArgs[] = \sprintf(
$template,
$methodName,
$methodParameter->getKey(),
$required,
$defaultValue
);
} else {
$template = ($defaultValue === '') ? '%s: $this->%s("%s", %s)' : '%s: $this->%s("%s", %s, %s)';
$namedArgs[] = \sprintf(
$template,
$argName,
$methodName,
$methodParameter->getKey(),
$required,
$defaultValue
);
}
}

return \implode(', ', $parameters);
return \implode(', ', [...$positionalArgs, ...$namedArgs]);
}
}
57 changes: 53 additions & 4 deletions tests/bitExpert/Disco/AnnotationBeanFactoryUnitTest.php
Expand Up @@ -260,14 +260,15 @@ public function beanFactoryPostProcessorCanBeConfiguredWithParameterizedDependen
{
$this->beanFactory = new AnnotationBeanFactory(
BeanConfigurationWithPostProcessorAndParameterizedDependency::class,
['test' => 'injectedValue']
['configKey1' => 'injectedValue1', 'configKey2' => 'injectedValue2']
);
BeanFactoryRegistry::register($this->beanFactory);

/** @var SampleService $bean */
$bean = $this->beanFactory->get('nonSingletonNonLazyRequestBean');
self::assertInstanceOf(stdClass::class, $bean->test);
self::assertEquals('injectedValue', $bean->test->property);
self::assertEquals('injectedValue1', $bean->test->property1);
self::assertEquals('injectedValue2', $bean->test->property2);
}

/**
Expand All @@ -277,14 +278,62 @@ public function parameterPassedToBeanFactoryGetsInjectedInBean(): void
{
$this->beanFactory = new AnnotationBeanFactory(
BeanConfigurationWithParameters::class,
['test' => 'injectedValue']
['configKey' => 'injectedValue']
);
BeanFactoryRegistry::register($this->beanFactory);

$bean = $this->beanFactory->get('sampleServiceWithParam');
self::assertEquals('injectedValue', $bean->test);
}

/**
* @test
*/
public function parametersPassedToBeanFactoryGetsInjectedInBeanWithPositionalParams(): void
{
$this->beanFactory = new AnnotationBeanFactory(
BeanConfigurationWithParameters::class,
['configKey1' => 'injectedValue1', 'configKey2' => 'injectedValue2']
);
BeanFactoryRegistry::register($this->beanFactory);

$bean = $this->beanFactory->get('sampleServiceWithPositionalParams');
self::assertEquals('injectedValue1', $bean->test);
self::assertEquals('injectedValue2', $bean->anotherTest);
}

/**
* @test
*/
public function parametersPassedToBeanFactoryGetsInjectedInBeanWithNamedParams(): void
{
$this->beanFactory = new AnnotationBeanFactory(
BeanConfigurationWithParameters::class,
['configKey1' => 'injectedValue1', 'configKey2' => 'injectedValue2']
);
BeanFactoryRegistry::register($this->beanFactory);

$bean = $this->beanFactory->get('sampleServiceWithNamedParams');
self::assertEquals('injectedValue1', $bean->test);
self::assertEquals('injectedValue2', $bean->anotherTest);
}

/**
* @test
*/
public function parametersPassedToBeanFactoryGetsInjectedInBeanWithMixedPositionalAndNamedParams(): void
{
$this->beanFactory = new AnnotationBeanFactory(
BeanConfigurationWithParameters::class,
['configKey1' => 'injectedValue1', 'configKey2' => 'injectedValue2']
);
BeanFactoryRegistry::register($this->beanFactory);

$bean = $this->beanFactory->get('sampleServiceWithMixedPositionalAndNamedParams');
self::assertEquals('injectedValue1', $bean->test);
self::assertEquals('injectedValue2', $bean->anotherTest);
}

/**
* @test
*/
Expand All @@ -293,7 +342,7 @@ public function nestedParameterKeyPassedToBeanFactoryGetsInjectedInBean(): void
$this->beanFactory = new AnnotationBeanFactory(
BeanConfigurationWithParameters::class,
[
'test' => [
'config' => [
'nested' => [
'key' => 'injectedValue'
]
Expand Down
16 changes: 8 additions & 8 deletions tests/bitExpert/Disco/Annotations/ParameterUnitTest.php
Expand Up @@ -28,7 +28,7 @@ public function emptyNameWillThrowAnnotationException(): void
{
$this->expectException(InvalidArgumentException::class);

new Parameter('', 'myParam');
new Parameter(key: 'myParam', name: '');
}

/**
Expand All @@ -38,25 +38,25 @@ public function emptyKeyWillThrowAnnotationException(): void
{
$this->expectException(InvalidArgumentException::class);

new Parameter('name', '');
new Parameter(key: '');
}

/**
* @test
*/
public function nameIsSet(): void
{
$parameter = new Parameter(name: 'paramName', key: 'key');
$parameter = new Parameter(name: 'argName', key: 'key');

self::assertSame('paramName', $parameter->getName());
self::assertSame('argName', $parameter->getName());
}

/**
* @test
*/
public function keyIsSet(): void
{
$parameter = new Parameter(name: 'paramName', key: 'key');
$parameter = new Parameter(name: 'argName', key: 'key');

self::assertSame('key', $parameter->getKey());
}
Expand All @@ -78,7 +78,7 @@ public function defaultValueDefaultsToNull(): void
*/
public function defaultValueIsParsed(mixed $defaultValue): void
{
$parameter = new Parameter(name: 'paramName', key: 'myParam', default: $defaultValue);
$parameter = new Parameter(name: 'argName', key: 'myParam', default: $defaultValue);

self::assertSame($defaultValue, $parameter->getDefaultValue());
}
Expand All @@ -88,7 +88,7 @@ public function defaultValueIsParsed(mixed $defaultValue): void
*/
public function requireDefaultsToTrue(): void
{
$parameter = new Parameter(name: 'paramName', key: 'myParam');
$parameter = new Parameter(name: 'argName', key: 'myParam');

self::assertTrue($parameter->isRequired());
}
Expand All @@ -100,7 +100,7 @@ public function requireDefaultsToTrue(): void
*/
public function requireIsParsed(bool $requireValue): void
{
$parameter = new Parameter(name: 'paramName', key: 'myParam', required: $requireValue);
$parameter = new Parameter(name: 'argName', key: 'myParam', required: $requireValue);

self::assertSame($requireValue, $parameter->isRequired());
}
Expand Down

0 comments on commit 48d452b

Please sign in to comment.