Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 1.4.2 under development

- Enh #400: Improve container performance and make `has()` cache limit configurable (@samdark)
- Enh #397: Explicitly import functions in "use" section (@mspirkov)

## 1.4.1 December 01, 2025
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,20 @@ $config = ContainerConfig::create()
$container = new Container($config);
```

The container caches `has()` results to speed up repeated lookups. The cache is limited to 1024 entries by default.
If your application checks many dynamic service IDs in a long-running process, you can adjust the limit or disable the
cache:

```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;

$config = ContainerConfig::create()
->withHasCacheLimit(0); // Disable `has()` cache.

$container = new Container($config);
```

## Strict mode

Container may work in a strict mode, that's when you should define everything in the container explicitly.
Expand Down
36 changes: 34 additions & 2 deletions src/CompositeContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use RuntimeException;
use Throwable;
use Yiisoft\Di\Reference\TagReference;
Expand All @@ -25,6 +26,13 @@
*/
private array $containers = [];

/**
* Index of a container where a service ID was previously found.
*
* @psalm-var array<string, int>
*/
private array $lookupCache = [];

/**
* @psalm-template T
* @psalm-param string|class-string<T> $id
Expand Down Expand Up @@ -59,7 +67,7 @@
$tags = [];
foreach ($this->containers as $container) {
if (!$container instanceof Container) {
continue;

Check warning on line 70 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "Continue_": @@ @@ $tags = []; foreach ($this->containers as $container) { if (!$container instanceof Container) { - continue; + break; } if ($container->has($id)) { /** @psalm-suppress MixedArgument `Container::get()` always return array for tag */
}
if ($container->has($id)) {
/** @psalm-suppress MixedArgument `Container::get()` always return array for tag */
Expand All @@ -71,8 +79,22 @@
return array_merge(...$tags);
}

foreach ($this->containers as $container) {
if (isset($this->lookupCache[$id], $this->containers[$this->lookupCache[$id]])) {
$index = $this->lookupCache[$id];
$container = $this->containers[$index];
if ($container->has($id)) {

Check warning on line 85 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "IfNegation": @@ @@ if (isset($this->lookupCache[$id], $this->containers[$this->lookupCache[$id]])) { $index = $this->lookupCache[$id]; $container = $this->containers[$index]; - if ($container->has($id)) { + if (!$container->has($id)) { try { /** @psalm-suppress MixedReturnStatement */ return $container->get($id);
try {
/** @psalm-suppress MixedReturnStatement */
return $container->get($id);

Check warning on line 88 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ReturnRemoval": @@ @@ $container = $this->containers[$index]; if ($container->has($id)) { try { - /** @psalm-suppress MixedReturnStatement */ - return $container->get($id); + } catch (NotFoundExceptionInterface) { } }
} catch (NotFoundExceptionInterface) {
}
}
unset($this->lookupCache[$id]);
}
Comment thread
samdark marked this conversation as resolved.

foreach ($this->containers as $index => $container) {
if ($container->has($id)) {
$this->lookupCache[$id] = (int) $index;

Check warning on line 97 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "CastInt": @@ @@ } foreach ($this->containers as $index => $container) { if ($container->has($id)) { - $this->lookupCache[$id] = (int) $index; + $this->lookupCache[$id] = $index; /** @psalm-suppress MixedReturnStatement */ return $container->get($id); }
/** @psalm-suppress MixedReturnStatement */
return $container->get($id);
}
Expand All @@ -82,7 +104,7 @@
$exceptions = [];
foreach ($this->containers as $container) {
$hasException = false;
try {

Check warning on line 107 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "UnwrapFinally": @@ @@ } catch (Throwable $t) { $hasException = true; $exceptions[] = [$t, $container]; - } finally { - if (!$hasException) { - $exceptions[] = [new RuntimeException('Container "has()" returned false, but no exception was thrown from "get()".'), $container]; - } + } + if (!$hasException) { + $exceptions[] = [new RuntimeException('Container "has()" returned false, but no exception was thrown from "get()".'), $container]; } } throw new CompositeNotFoundException($exceptions);
$container->get($id);
} catch (Throwable $t) {
$hasException = true;
Expand Down Expand Up @@ -115,23 +137,32 @@
}

if ($id === StateResetter::class) {
return true;

Check warning on line 140 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ReturnRemoval": @@ @@ throw new InvalidArgumentException(sprintf('ID must be a string, %s given.', get_debug_type($id))); } if ($id === StateResetter::class) { - return true; + } if (TagReference::isTagAlias($id)) { foreach ($this->containers as $container) {
}

if (TagReference::isTagAlias($id)) {
foreach ($this->containers as $container) {
if (!$container instanceof Container) {
continue;

Check warning on line 146 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "Continue_": @@ @@ if (TagReference::isTagAlias($id)) { foreach ($this->containers as $container) { if (!$container instanceof Container) { - continue; + break; } if ($container->has($id)) { return true;
}
if ($container->has($id)) {
return true;
}
}
return false;

Check warning on line 152 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ReturnRemoval": @@ @@ return true; } } - return false; + } if (isset($this->lookupCache[$id], $this->containers[$this->lookupCache[$id]])) { $index = $this->lookupCache[$id];
}

foreach ($this->containers as $container) {
if (isset($this->lookupCache[$id], $this->containers[$this->lookupCache[$id]])) {
$index = $this->lookupCache[$id];
if ($this->containers[$index]->has($id)) {
return true;

Check warning on line 158 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ReturnRemoval": @@ @@ if (isset($this->lookupCache[$id], $this->containers[$this->lookupCache[$id]])) { $index = $this->lookupCache[$id]; if ($this->containers[$index]->has($id)) { - return true; + } unset($this->lookupCache[$id]); }
}
unset($this->lookupCache[$id]);
}

foreach ($this->containers as $index => $container) {
if ($container->has($id)) {
$this->lookupCache[$id] = (int) $index;

Check warning on line 165 in src/CompositeContainer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "CastInt": @@ @@ } foreach ($this->containers as $index => $container) { if ($container->has($id)) { - $this->lookupCache[$id] = (int) $index; + $this->lookupCache[$id] = $index; return true; } }
return true;
}
}
Expand All @@ -155,6 +186,7 @@
foreach ($this->containers as $i => $c) {
if ($container === $c) {
unset($this->containers[$i]);
$this->lookupCache = [];
}
}
}
Expand Down
Loading
Loading