Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve containers types to use iterable objects #299

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Yii Dependency Injection Change Log

## 1.2.2 under development
## 2.0.0 under development

- no changes in this release.
- Chg #299: Improve types to use iterable objects (@xepozz)

## 1.2.1 December 23, 2022

Expand Down
48 changes: 30 additions & 18 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
use Traversable;
use Yiisoft\Definitions\ArrayDefinition;
use Yiisoft\Definitions\DefinitionStorage;
use Yiisoft\Definitions\Exception\CircularReferenceException;
Expand Down Expand Up @@ -59,7 +60,7 @@

/**
* @var array Tagged service IDs. The structure is `['tagID' => ['service1', 'service2']]`.
* @psalm-var array<string, list<string>>
* @psalm-var array<string, iterable<string>>
*/
private array $tags;

Expand Down Expand Up @@ -223,13 +224,14 @@
/**
* Sets multiple definitions at once.
*
* @param array $config Definitions indexed by their IDs.
* @param iterable $config Definitions indexed by their IDs.
*
* @throws InvalidConfigException
*/
private function addDefinitions(array $config): void
private function addDefinitions(iterable $config): void
vjik marked this conversation as resolved.
Show resolved Hide resolved
{
/** @var mixed $definition */
/** @psalm-suppress MixedAssignment */
foreach ($config as $id => $definition) {
if ($this->validate && !is_string($id)) {
throw new InvalidConfigException(
Expand All @@ -239,8 +241,8 @@
)
);
}
/** @var string $id */

$id = (string) $id;
vjik marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not need type-casting. Above check that $id is string.

$this->addDefinition($id, $definition);
}
}
Expand All @@ -251,11 +253,11 @@
* Each delegate must is a callable in format "function (ContainerInterface $container): ContainerInterface".
* The container instance returned is used in case a service can not be found in primary container.
*
* @param array $delegates
* @param iterable $delegates
*
* @throws InvalidConfigException
*/
private function setDelegates(array $delegates): void
private function setDelegates(iterable $delegates): void
vjik marked this conversation as resolved.
Show resolved Hide resolved
{
$this->delegates = new CompositeContainer();
$container = $this->get(ContainerInterface::class);
Expand All @@ -278,7 +280,7 @@

$this->delegates->attach($delegate);
}
$this->definitions->setDelegateContainer($this->delegates);

Check warning on line 283 in src/Container.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ } $this->delegates->attach($delegate); } - $this->definitions->setDelegateContainer($this->delegates); + } /** * @param mixed $definition Definition to validate.
}

/**
Expand All @@ -305,7 +307,7 @@

$definition = array_merge(
$class === null ? [] : [ArrayDefinition::CLASS_NAME => $class],
[ArrayDefinition::CONSTRUCTOR => $constructorArguments],

Check warning on line 310 in src/Container.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "ArrayItemRemoval": --- Original +++ New @@ @@ $methodsAndProperties = $definition['methodsAndProperties']; $definition = array_merge( $class === null ? [] : [ArrayDefinition::CLASS_NAME => $class], - [ArrayDefinition::CONSTRUCTOR => $constructorArguments], + [], // extract only value from parsed definition method array_map(fn(array $data): mixed => $data[2], $methodsAndProperties) );
// extract only value from parsed definition method
array_map(fn (array $data): mixed => $data[2], $methodsAndProperties),
);
Expand All @@ -323,10 +325,12 @@
/**
* @throws InvalidConfigException
*/
private function validateMeta(array $meta): void
private function validateMeta(iterable $meta): void
vjik marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateMeta always receive array. Not need iterable.

{
/** @var mixed $value */
/** @psalm-suppress MixedAssignment */
foreach ($meta as $key => $value) {
$key = (string)$key;
arogachev marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$key = (string)$key;

Not need.

if (!in_array($key, self::ALLOWED_META, true)) {
throw new InvalidConfigException(
sprintf(
Expand All @@ -353,10 +357,10 @@
*/
private function validateDefinitionTags(mixed $tags): void
{
if (!is_array($tags)) {
if (!is_iterable($tags)) {
throw new InvalidConfigException(
sprintf(
'Invalid definition: tags should be array of strings, %s given.',
'Invalid definition: tags should be either iterable or array of strings, %s given.',
get_debug_type($tags)
)
);
Expand Down Expand Up @@ -387,22 +391,22 @@
/**
* @throws InvalidConfigException
*/
private function setTags(array $tags): void
private function setTags(iterable $tags): void
vjik marked this conversation as resolved.
Show resolved Hide resolved
{
if ($this->validate) {
foreach ($tags as $tag => $services) {
if (!is_string($tag)) {
throw new InvalidConfigException(
sprintf(
'Invalid tags configuration: tag should be string, %s given.',
$tag
get_debug_type($services)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
get_debug_type($services)
get_debug_type($tag)

)
);
}
if (!is_array($services)) {
if (!is_iterable($services)) {
throw new InvalidConfigException(
sprintf(
'Invalid tags configuration: tag should contain array of service IDs, %s given.',
'Invalid tags configuration: tag should be either iterable or array of service IDs, %s given.',
get_debug_type($services)
)
);
Expand All @@ -420,18 +424,26 @@
}
}
}
/** @psalm-var array<string, list<string>> $tags */
/** @psalm-var iterable<string, iterable<string>> $tags */

$this->tags = $tags;
$this->tags = $tags instanceof Traversable ? iterator_to_array($tags, true) : $tags ;
}

/**
* @psalm-param string[] $tags
*/
private function setDefinitionTags(string $id, array $tags): void
private function setDefinitionTags(string $id, iterable $tags): void
{
foreach ($tags as $tag) {
if (!isset($this->tags[$tag]) || !in_array($id, $this->tags[$tag], true)) {
if (!isset($this->tags[$tag])) {
$this->tags[$tag] = [$id];
continue;
}

$tags = $this->tags[$tag];
$tags = $tags instanceof Traversable ? iterator_to_array($tags, true) : $tags;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better convert iterable to array before foreach

if (!in_array($id, $tags, true)) {
/** @psalm-suppress PossiblyInvalidArrayAssignment */
$this->tags[$tag][] = $id;
}
}
Expand Down Expand Up @@ -491,7 +503,7 @@
);
}

$this->building[$id] = 1;

Check warning on line 506 in src/Container.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ } throw new CircularReferenceException(sprintf('Circular reference to "%s" detected while building: %s.', $id, implode(', ', array_keys($this->building)))); } - $this->building[$id] = 1; + $this->building[$id] = 2; try { /** @var mixed $object */ $object = $this->buildInternal($id);
try {
/** @var mixed $object */
$object = $this->buildInternal($id);
Expand Down Expand Up @@ -537,7 +549,7 @@
* @throws CircularReferenceException
* @throws InvalidConfigException
*/
private function addProviders(array $providers): void
private function addProviders(iterable $providers): void
vjik marked this conversation as resolved.
Show resolved Hide resolved
{
$extensions = [];
/** @var mixed $provider */
Expand Down
32 changes: 16 additions & 16 deletions src/ContainerConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
*/
final class ContainerConfig implements ContainerConfigInterface
{
private array $definitions = [];
private array $providers = [];
private array $tags = [];
private iterable $definitions = [];
private iterable $providers = [];
private iterable $tags = [];
private bool $validate = true;
private array $delegates = [];
private iterable $delegates = [];
private bool $useStrictMode = false;

private function __construct()
Expand All @@ -26,46 +26,46 @@
}

/**
* @param array $definitions Definitions to put into container.
* @param iterable $definitions Definitions to put into container.
*/
public function withDefinitions(array $definitions): self
public function withDefinitions(iterable $definitions): self
{
$new = clone $this;

Check warning on line 33 in src/ContainerConfig.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "CloneRemoval": --- Original +++ New @@ @@ */ public function withDefinitions(iterable $definitions) : self { - $new = clone $this; + $new = $this; $new->definitions = $definitions; return $new; }
$new->definitions = $definitions;
return $new;
}

public function getDefinitions(): array
public function getDefinitions(): iterable
{
return $this->definitions;
}

/**
* @param array $providers Service providers to get definitions from.
* @param iterable $providers Service providers to get definitions from.
*/
public function withProviders(array $providers): self
public function withProviders(iterable $providers): self
{
$new = clone $this;

Check warning on line 48 in src/ContainerConfig.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "CloneRemoval": --- Original +++ New @@ @@ */ public function withProviders(iterable $providers) : self { - $new = clone $this; + $new = $this; $new->providers = $providers; return $new; }
$new->providers = $providers;
return $new;
}

public function getProviders(): array
public function getProviders(): iterable
{
return $this->providers;
}

/**
* @param array $tags Tagged service IDs. The structure is `['tagID' => ['service1', 'service2']]`.
* @param iterable $tags Tagged service IDs. The structure is `['tagID' => ['service1', 'service2']]`.
*/
public function withTags(array $tags): self
public function withTags(iterable $tags): self
{
$new = clone $this;

Check warning on line 63 in src/ContainerConfig.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "CloneRemoval": --- Original +++ New @@ @@ */ public function withTags(iterable $tags) : self { - $new = clone $this; + $new = $this; $new->tags = $tags; return $new; }
$new->tags = $tags;
return $new;
}

public function getTags(): array
public function getTags(): iterable
{
return $this->tags;
}
Expand All @@ -75,7 +75,7 @@
*/
public function withValidate(bool $validate): self
{
$new = clone $this;

Check warning on line 78 in src/ContainerConfig.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "CloneRemoval": --- Original +++ New @@ @@ */ public function withValidate(bool $validate) : self { - $new = clone $this; + $new = $this; $new->validate = $validate; return $new; }
$new->validate = $validate;
return $new;
}
Expand All @@ -86,18 +86,18 @@
}

/**
* @param array $delegates Container delegates. Each delegate is a callable in format
* @param iterable $delegates Container delegates. Each delegate is a callable in format
* `function (ContainerInterface $container): ContainerInterface`. The container instance returned is used
* in case a service can not be found in primary container.
*/
public function withDelegates(array $delegates): self
public function withDelegates(iterable $delegates): self
{
$new = clone $this;

Check warning on line 95 in src/ContainerConfig.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "CloneRemoval": --- Original +++ New @@ @@ */ public function withDelegates(iterable $delegates) : self { - $new = clone $this; + $new = $this; $new->delegates = $delegates; return $new; }
$new->delegates = $delegates;
return $new;
}

public function getDelegates(): array
public function getDelegates(): iterable
{
return $this->delegates;
}
Expand Down
16 changes: 8 additions & 8 deletions src/ContainerConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@
interface ContainerConfigInterface
{
/**
* @return array Definitions to put into container.
* @return iterable Definitions to put into container.
*/
public function getDefinitions(): array;
public function getDefinitions(): iterable;

/**
* @return array Service providers to get definitions from.
* @return iterable Service providers to get definitions from.
*/
public function getProviders(): array;
public function getProviders(): iterable;

/**
* @return array Tagged service IDs. The structure is `['tagID' => ['service1', 'service2']]`.
* @return iterable Tagged service IDs. The structure is `['tagID' => ['service1', 'service2']]`.
*/
public function getTags(): array;
public function getTags(): iterable;

/**
* @return bool Whether definitions should be validated immediately.
*/
public function shouldValidate(): bool;

/**
* @return array Container delegates. Each delegate is a callable in format
* @return iterable Container delegates. Each delegate is a callable in format
* `function (ContainerInterface $container): ContainerInterface`. The container instance returned is used
* in case a service can not be found in primary container.
*/
public function getDelegates(): array;
public function getDelegates(): iterable;

/**
* @return bool If the automatic addition of definition when class exists and can be resolved is disabled.
Expand Down
4 changes: 2 additions & 2 deletions src/ServiceProviderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ interface ServiceProviderInterface
* @return array Definitions for the container. Each array key is the name of the service (usually it is
* an interface name), and a corresponding value is a service definition.
*/
public function getDefinitions(): array;
public function getDefinitions(): iterable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need fix phpdoc.


/**
* Returns an array of service extensions.
Expand All @@ -58,5 +58,5 @@ public function getDefinitions(): array;
* @return array Extensions for the container services. Each array key is the name of the service to be modified
* and a corresponding value is callable doing the job.
*/
public function getExtensions(): array;
public function getExtensions(): iterable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need fix phpdoc.

}
4 changes: 2 additions & 2 deletions tests/Support/CarExtensionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@

final class CarExtensionProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
public function getDefinitions(): iterable
{
return [];
}

public function getExtensions(): array
public function getExtensions(): iterable
{
return [
Car::class => static function (ContainerInterface $container, Car $car) {
Expand Down
4 changes: 2 additions & 2 deletions tests/Support/CarProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

final class CarProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
public function getDefinitions(): iterable
{
return [
'car' => Car::class,
EngineInterface::class => EngineMarkOne::class,
];
}

public function getExtensions(): array
public function getExtensions(): iterable
{
return [
Car::class => static function (ContainerInterface $container, Car $car) {
Expand Down
4 changes: 2 additions & 2 deletions tests/Support/ContainerInterfaceExtensionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@

final class ContainerInterfaceExtensionProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
public function getDefinitions(): iterable
{
return [];
}

public function getExtensions(): array
public function getExtensions(): iterable
{
return [
ContainerInterface::class => static fn (ContainerInterface $container, ContainerInterface $extended) => $container,
Expand Down
4 changes: 2 additions & 2 deletions tests/Support/NullCarExtensionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@

final class NullCarExtensionProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
public function getDefinitions(): iterable
{
return [
];
}

public function getExtensions(): array
public function getExtensions(): iterable
{
return [
Car::class => static fn (ContainerInterface $container, Car $car) => null,
Expand Down
Loading
Loading