Skip to content
Closed
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
102 changes: 102 additions & 0 deletions benchmark/DefinitionBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Definitions\Benchmark;

use PhpBench\Attributes\BeforeMethods;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\Revs;
use Psr\Container\ContainerInterface;
use Yiisoft\Definitions\ArrayDefinition;
use Yiisoft\Definitions\CallableDefinition;
use Yiisoft\Definitions\Reference;
use Yiisoft\Definitions\ValueDefinition;
use Yiisoft\Definitions\DynamicReference;
use Yiisoft\Definitions\DefinitionStorage;

class TestClass
{
private string $property;

public function __construct(string $param1, string $param2)
{
$this->property = $param1 . $param2;
}
}

#[BeforeMethods('setUp')]
final class DefinitionBench
{
private DefinitionStorage $storage;
private ContainerInterface $container;

public function setUp(): void
{
$this->storage = new DefinitionStorage();
// Create a simple container implementation for testing
$this->container = new class () implements ContainerInterface {
private array $entries = [];

public function get(string $id): mixed
{
return $this->entries[$id] ?? null;
}

public function has(string $id): bool
{
return isset($this->entries[$id]);
}

public function set(string $id, mixed $value): void
{
$this->entries[$id] = $value;
}
};
}

#[Revs(1000)]
#[Iterations(10)]
public function benchArrayDefinitionResolve(): void
{
$definition = ArrayDefinition::fromConfig([
'class' => TestClass::class,
'property' => 'value',
'__construct()' => ['param1', 'param2'],
]);
$definition->resolve($this->container);
}

#[Revs(1000)]
#[Iterations(10)]
public function benchCallableDefinitionResolve(): void
{
$definition = new CallableDefinition(static fn () => new \stdClass());
$definition->resolve($this->container);
}

#[Revs(1000)]
#[Iterations(10)]
public function benchValueDefinitionResolve(): void
{
$definition = new ValueDefinition('test value');
$definition->resolve($this->container);
}

#[Revs(1000)]
#[Iterations(10)]
public function benchReferenceResolve(): void
{
$reference = Reference::to('dependency');
$this->container->set('dependency', 'test');
$reference->resolve($this->container);
}

#[Revs(1000)]
#[Iterations(10)]
public function benchDynamicReferenceResolve(): void
{
$reference = DynamicReference::to(static fn () => 'test');
$reference->resolve($this->container);
}
}
69 changes: 69 additions & 0 deletions benchmark/DefinitionStorageBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Definitions\Benchmark;

use PhpBench\Attributes\BeforeMethods;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\Revs;
use Yiisoft\Definitions\ArrayDefinition;
use Yiisoft\Definitions\ValueDefinition;
use Yiisoft\Definitions\DefinitionStorage;

#[BeforeMethods('setUp')]
final class DefinitionStorageBench
{
private DefinitionStorage $storage;

public function setUp(): void
{
$this->storage = new DefinitionStorage();
}

#[Revs(1000)]
#[Iterations(10)]
public function benchSetAndGet(): void
{
$this->storage->set('test', new ValueDefinition('value'));
$this->storage->get('test');
}

#[Revs(1000)]
#[Iterations(10)]
public function benchHas(): void
{
$this->storage->set('test', new ValueDefinition('value'));
$this->storage->has('test');
}

#[Revs(1000)]
#[Iterations(10)]
public function benchSetMultipleDefinitions(): void
{
$definitions = [
'test1' => new ValueDefinition('value1'),
'test2' => new ValueDefinition('value2'),
'test3' => new ValueDefinition('value3'),
];
$storage = new DefinitionStorage($definitions);
$storage->has('test1');
$storage->has('test2');
$storage->has('test3');
}

#[Revs(1000)]
#[Iterations(10)]
public function benchGetBuildStack(): void
{
$this->storage->set('test1', new ValueDefinition('value1'));
$this->storage->set('test2', ArrayDefinition::fromConfig([
'class' => 'TestClass',
'__construct()' => [
'test1',
],
]));
$this->storage->has('test2');
$this->storage->getBuildStack();
}
}
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
},
"require-dev": {
"maglnet/composer-require-checker": "^4.2",
"phpbench/phpbench": "^1.4",
"phpunit/phpunit": "^9.5",
"rector/rector": "^1.0.0",
"roave/infection-static-analysis-plugin": "^1.18",
Expand Down
17 changes: 17 additions & 0 deletions phpbench.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "./vendor/phpbench/phpbench/phpbench.schema.json",
"runner.bootstrap": "vendor/autoload.php",
"runner.path": "benchmark",
"runner.retry_threshold": 5,
"runner.iterations": 10,
"runner.revs": 1000,
"runner.warmup": 2,
"report.outputs": {
"my_output": {
"extends": "console",
"components": {
"console_table_style": "default"
}
}
}
}
25 changes: 9 additions & 16 deletions src/DefinitionStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public function setDelegateContainer(ContainerInterface $delegateContainer): voi
*/
public function has(string $id): bool
{
if (isset($this->definitions[$id])) {
return true;
}
$this->buildStack = [];
return $this->isResolvable($id, []);
}
Expand Down Expand Up @@ -154,7 +157,6 @@ private function isResolvable(string $id, array $building): bool
// Union type is used as type hint
if ($type instanceof ReflectionUnionType) {
$isUnionTypeResolvable = false;
$unionTypes = [];
foreach ($type->getTypes() as $unionType) {
/**
* @psalm-suppress DocblockTypeContradiction Need for PHP 8.0 and 8.1 only
Expand All @@ -172,27 +174,18 @@ private function isResolvable(string $id, array $building): bool
if ($typeName === 'self') {
continue;
}
$unionTypes[] = $typeName;
if ($this->isResolvable($typeName, $building)) {

if ($this->isResolvable($typeName, $building) ||
($this->delegateContainer !== null && $this->delegateContainer->has($typeName))) {
$isUnionTypeResolvable = true;
/** @infection-ignore-all Mutation don't change behaviour, but degrade performance. */
break;
}
}

if (!$isUnionTypeResolvable) {
Copy link
Member

Choose a reason for hiding this comment

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

????

Copy link
Member Author

@samdark samdark Feb 11, 2025

Choose a reason for hiding this comment

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

Yeah. "Optimization". Tests pass, so "why not" :) I'll revert it.

foreach ($unionTypes as $typeName) {
if ($this->delegateContainer !== null && $this->delegateContainer->has($typeName)) {
$isUnionTypeResolvable = true;
/** @infection-ignore-all Mutation don't change behaviour, but degrade performance. */
break;
}
}

$isResolvable = $isUnionTypeResolvable;
if (!$isResolvable) {
break;
}
$isResolvable = $isUnionTypeResolvable;
if (!$isResolvable) {
break;
}
continue;
}
Expand Down
Loading