Skip to content

Commit

Permalink
Merge pull request #3 from rich-id/v1.1.0
Browse files Browse the repository at this point in the history
Prepare v1.1.0
  • Loading branch information
NicolasGuilloux authored Dec 24, 2021
2 parents 09d8e9d + 68553c6 commit 7ee7fb5
Show file tree
Hide file tree
Showing 29 changed files with 569 additions and 103 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## Version 1.1.0

- Add service tag injection
- Add method annotation to add MethodCall to the service definition

## Version 1.0.1

- Fix annotation dependency where it was
36 changes: 33 additions & 3 deletions docs/ServiceArgumentInjection.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Sometimes it is required to inject a service that is not autowired, or a specifi

=== In the constructor

To do so, add the `Argument` annotation/attribute on the class with the appropriate values. Here is an example where we inject the `doctrine` service and the default locale into the constructor:
To do so, add the `Argument` annotation/attribute on the class with the appropriate values. Here is an example where we inject the `doctrine` service, the default locale and all the tagged command into the constructor:

[source, php]
----
Expand All @@ -13,10 +13,11 @@ use RichId\AutoconfigureBundle\Annotation as Service;
#[
Service\Argument('$doctrine', 'doctrine'),
Service\Argument('$locale', 'kernel.default_locale', type='parameter'),
Service\Argyment('commands', 'console.command', type='services_by_tag'),
]
class RandomService
{
public function __construct($doctrine, string $locale)
public function __construct($doctrine, string $locale, array $commands)
{
...
}
Expand All @@ -25,7 +26,7 @@ class RandomService

=== In a public property

To do so, add the `Property` annotation/attribute on the class with the appropriate values. Here is an example where we inject the `doctrine` service and the default locale into the corresponding public properties:
To do so, add the `Property` annotation/attribute on the class with the appropriate values. Here is an example where we inject the `doctrine` service, the default locale and all the tagged commands into the corresponding public properties:

[source, php]
----
Expand All @@ -34,10 +35,39 @@ use RichId\AutoconfigureBundle\Annotation as Service;
#[
Service\Property('doctrine', 'doctrine'),
Service\Property('locale', 'kernel.default_locale', type='parameter'),
Service\Property('commands', 'console.command', type='services_by_tag'),
]
class RandomService
{
public $doctrine;
public string $locale;
public array $commands;
}
----

=== In a public method

To do so, add the `Method` annotation/attribute on the class with the appropriate values. Here is an example where we inject the 2 values to a method and all the tagged commands to another one.

[source, php]
----
use RichId\AutoconfigureBundle\Annotation as Service;
#[
Service\Method('setValues', 'doctrine'),
Service\Method('setValues', 'kernel.default_locale', type='parameter', position=1),
Service\Method('setCommands', 'console.command', type='services_by_tag'),
]
class RandomService
{
public function setValues($doctrine, string $locale): void
{
// ...
}
public function setCommands(array $commands): void
{
// ...
}
}
----
24 changes: 24 additions & 0 deletions src/Annotation/AbstractServiceInjectionAnnotation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace RichId\AutoconfigureBundle\Annotation;

abstract class AbstractServiceInjectionAnnotation implements AutoconfigureAnnotation
{
public const SERVICE_TYPE = 'service';
public const PARAMETER_TYPE = 'parameter';
public const SERVICES_BY_TAG = 'services_by_tag';

/** @var string */
public $value;

/** @var string */
public $type;

public function __construct(string $value, string $type = self::SERVICE_TYPE)
{
$this->value = $value;
$this->type = $type;
}
}
15 changes: 3 additions & 12 deletions src/Annotation/Argument.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,15 @@
* @NamedArgumentConstructor()
*/
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS)]
final class Argument implements AutoconfigureAnnotation
final class Argument extends AbstractServiceInjectionAnnotation
{
public const SERVICE_TYPE = 'service';
public const PARAMETER_TYPE = 'parameter';

/** @var string */
public $argument;

/** @var string */
public $value;

/** @var string */
public $type;

public function __construct(string $argument, string $value, string $type = self::SERVICE_TYPE)
{
parent::__construct($value, $type);

$this->argument = $argument;
$this->value = $value;
$this->type = $type;
}
}
36 changes: 36 additions & 0 deletions src/Annotation/Method.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace RichId\AutoconfigureBundle\Annotation;

use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;

/**
* Class Method.
*
* Bind a service or a parameter to the designated method.
*
* @author Nicolas Guilloux <[email protected]>
* @copyright 2014 - 2021 Rich ID (https://www.rich-id.fr)
*
* @Annotation({"CLASS"})
* @NamedArgumentConstructor()
*/
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS)]
final class Method extends AbstractServiceInjectionAnnotation
{
/** @var string */
public $method;

/** @var int */
public $position = 0;

public function __construct(string $method, string $value, string $type = self::SERVICE_TYPE, int $position = 0)
{
parent::__construct($value, $type);

$this->method = $method;
$this->position = $position;
}
}
15 changes: 3 additions & 12 deletions src/Annotation/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,15 @@
* @NamedArgumentConstructor()
*/
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS)]
final class Property implements AutoconfigureAnnotation
final class Property extends AbstractServiceInjectionAnnotation
{
public const SERVICE_TYPE = 'service';
public const PARAMETER_TYPE = 'parameter';

/** @var string */
public $property;

/** @var string */
public $value;

/** @var string */
public $type;

public function __construct(string $property, string $value, string $type = self::SERVICE_TYPE)
{
parent::__construct($value, $type);

$this->property = $property;
$this->value = $value;
$this->type = $type;
}
}
4 changes: 2 additions & 2 deletions src/Configurators/Basics/ServiceAutoConfiguratorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace RichId\AutoconfigureBundle\Configurators\Basics;

use RichId\AutoconfigureBundle\Model\ServiceConfiguration;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

/**
Expand All @@ -17,7 +17,7 @@
interface ServiceAutoConfiguratorInterface
{
public function autoconfigure(
Container $container,
ContainerBuilder $container,
Definition $definition,
ServiceConfiguration $configuration
): void;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace RichId\AutoconfigureBundle\Configurators\Partials;

use RichId\AutoconfigureBundle\Annotation\AbstractServiceInjectionAnnotation;
use RichId\AutoconfigureBundle\Configurators\Basics\ServiceAutoConfiguratorInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

abstract class AbstractServiceInjectionAutoConfigurator implements ServiceAutoConfiguratorInterface
{
protected function resolveObject(ContainerBuilder $container, array $options)
{
$type = $options['type'] ?? null;
$value = $options['value'] ?? null;

switch ($type) {
case AbstractServiceInjectionAnnotation::SERVICE_TYPE:
return new Reference($value);

case AbstractServiceInjectionAnnotation::PARAMETER_TYPE:
return $container->getParameter($value);

case AbstractServiceInjectionAnnotation::SERVICES_BY_TAG:
$serviceTags = $container->findTaggedServiceIds($value);

return \array_map(
static function (string $serviceId): Reference {
return new Reference($serviceId);
},
\array_keys($serviceTags)
);

default:
throw new \UnexpectedValueException('The type used a service configuration is wrong.');
}
}
}
28 changes: 4 additions & 24 deletions src/Configurators/Partials/ArgumentAutoConfigurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,20 @@

namespace RichId\AutoconfigureBundle\Configurators\Partials;

use RichId\AutoconfigureBundle\Annotation\Argument;
use RichId\AutoconfigureBundle\Configurators\Basics\ServiceAutoConfiguratorInterface;
use RichId\AutoconfigureBundle\Model\ServiceConfiguration;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

/**
* Class ArgumentAutoConfigurator.
*
* @author Nicolas Guilloux <[email protected]>
* @copyright 2014 - 2021 Rich ID (https://www.rich-id.fr)
*/
final class ArgumentAutoConfigurator implements ServiceAutoConfiguratorInterface
final class ArgumentAutoConfigurator extends AbstractServiceInjectionAutoConfigurator
{
public function autoconfigure(
Container $container,
ContainerBuilder $container,
Definition $definition,
ServiceConfiguration $configuration
): void {
Expand All @@ -29,25 +26,8 @@ public function autoconfigure(

$definition->setArgument(
$sanitizedArgument,
$this->getObject($container, $options)
$this->resolveObject($container, $options)
);
}
}

public function getObject(Container $container, array $options)
{
$type = $options['type'] ?? null;
$value = $options['value'] ?? null;

switch ($type) {
case Argument::SERVICE_TYPE:
return new Reference($value);

case Argument::PARAMETER_TYPE:
return $container->getParameter($value);

default:
throw new \UnexpectedValueException('The type used a service configuration is wrong.');
}
}
}
4 changes: 2 additions & 2 deletions src/Configurators/Partials/DecorationAutoConfigurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use RichId\AutoconfigureBundle\Configurators\Basics\ServiceAutoConfiguratorInterface;
use RichId\AutoconfigureBundle\Model\ServiceConfiguration;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

/**
Expand All @@ -18,7 +18,7 @@
final class DecorationAutoConfigurator implements ServiceAutoConfiguratorInterface
{
public function autoconfigure(
Container $container,
ContainerBuilder $container,
Definition $definition,
ServiceConfiguration $configuration
): void {
Expand Down
42 changes: 37 additions & 5 deletions src/Configurators/Partials/MethodCallAutoConfigurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

namespace RichId\AutoconfigureBundle\Configurators\Partials;

use RichId\AutoconfigureBundle\Configurators\Basics\ServiceAutoConfiguratorInterface;
use RichId\AutoconfigureBundle\Model\ServiceConfiguration;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

/**
Expand All @@ -15,15 +14,48 @@
* @author Nicolas Guilloux <[email protected]>
* @copyright 2014 - 2021 Rich ID (https://www.rich-id.fr)
*/
final class MethodCallAutoConfigurator implements ServiceAutoConfiguratorInterface
final class MethodCallAutoConfigurator extends AbstractServiceInjectionAutoConfigurator
{
public function autoconfigure(
Container $container,
ContainerBuilder $container,
Definition $definition,
ServiceConfiguration $configuration
): void {
foreach ($configuration->getMethodCalls() as $method => $arguments) {
$definition->addMethodCall($method, $arguments);
$resolvedArguments = $this->findMethodCallExistingArguments($definition, $method);

foreach ($arguments as $key => $value) {
$resolvedArguments[$key] = $this->resolveObject($container, $value);
}

$this->setMethodCall($definition, $method, $resolvedArguments);
}
}

private function findMethodCallExistingArguments(Definition $definition, string $method): array
{
foreach ($definition->getMethodCalls() as $data) {
if ($data[0] === $method) {
return $data[1] ?? [];
}
}

return [];
}

private function setMethodCall(Definition $definition, string $method, array $arguments): void
{
$methodCalls = $definition->getMethodCalls();

foreach ($methodCalls as $key => $data) {
if ($data[0] === $method) {
$methodCalls[$key] = [$method, $arguments];
$definition->setMethodCalls($methodCalls);

return;
}
}

$definition->addMethodCall($method, $arguments);
}
}
Loading

0 comments on commit 7ee7fb5

Please sign in to comment.