phpDiContainer = new Container(); $this->container = new InflectableContainer($this->phpDiContainer); } public function testCanGetAndSetServices(): void { $this->container->set('test.value', 'hello world'); self::assertTrue($this->container->has('test.value')); self::assertSame('hello world', $this->container->get('test.value')); } public function testCanMakeInstancesWithParameters(): void { $instance = $this->container->make(TestClass::class, ['value' => 'test']); self::assertInstanceOf(TestClass::class, $instance); self::assertSame('test', $instance->getValue()); } public function testCanRegisterClassWithoutDependencies(): void { $instance = $this->container->register(TestClass::class); self::assertInstanceOf(TestClass::class, $instance); self::assertTrue($this->container->has(TestClass::class)); } public function testCanRegisterClassWithDependencies(): void { $this->container->set('dependency', new TestDependency('injected')); $instance = $this->container->register(TestClassWithDependency::class, ['dependency']); self::assertInstanceOf(TestClassWithDependency::class, $instance); self::assertSame('injected', $instance->getDependencyValue()); } public function testCanBindInterfaceToImplementation(): void { $instance = $this->container->bind(TestInterface::class, TestImplementation::class); self::assertInstanceOf(TestImplementation::class, $instance); self::assertTrue($this->container->has(TestInterface::class)); $retrieved = $this->container->get(TestInterface::class); self::assertInstanceOf(TestImplementation::class, $retrieved); } public function testCanBindInterfaceToImplementationWithDependencies(): void { $this->container->set('dependency', new TestDependency('bound')); $instance = $this->container->bind( TestInterface::class, TestImplementationWithDependency::class, ['dependency'], ); self::assertInstanceOf(TestImplementationWithDependency::class, $instance); self::assertSame('bound', $instance->getDependencyValue()); } public function testInflectionSystemAppliesDirectClassInflections(): void { $this->container->registerInflection( TestInflectionTarget::class, 'setInjectedValue', ['injected_value'], ); $instance = $this->container->make(TestInflectionTarget::class); self::assertSame('injected_value', $instance->getInjectedValue()); } public function testInflectionSystemAppliesInterfaceInflections(): void { $this->container->registerInflection( TestInflectionInterface::class, 'setInjectedValue', ['interface_value'], ); $instance = $this->container->make(TestInflectionImplementation::class); self::assertSame('interface_value', $instance->getInjectedValue()); } public function testInflectionSystemResolvesParametersFromContainer(): void { $dependency = new TestDependency('resolved'); $this->container->set(TestDependency::class, $dependency); $this->container->registerInflection( TestInflectionTarget::class, 'setDependency', [TestDependency::class], ); $instance = $this->container->make(TestInflectionTarget::class); self::assertSame($dependency, $instance->getDependency()); } public function testAttributeProcessorsAreCalledDuringRegistration(): void { $processor = $this->createMock(AttributeProcessorInterface::class); $processor->expects(self::once())->method('canProcess')->with(TestClass::class)->willReturn(true); $processor->expects(self::once())->method('process')->with(TestClass::class); $this->container->addAttributeProcessor($processor); $this->container->register(TestClass::class); } public function testReturnsNonObjectsDirectly(): void { $this->container->set('array.value', ['test' => 'array']); $this->container->set('string.value', 'test string'); self::assertSame(['test' => 'array'], $this->container->get('array.value')); self::assertSame('test string', $this->container->get('string.value')); } } class TestClass { public function __construct(private readonly string $value = 'default') {} public function getValue(): string { return $this->value; } } class TestDependency { public function __construct(private readonly string $value) {} public function getValue(): string { return $this->value; } } class TestClassWithDependency { public function __construct(private readonly TestDependency $dependency) {} public function getDependencyValue(): string { return $this->dependency->getValue(); } } interface TestInterface { } class TestImplementation implements TestInterface { } class TestImplementationWithDependency implements TestInterface { public function __construct(private readonly TestDependency $dependency) {} public function getDependencyValue(): string { return $this->dependency->getValue(); } } interface TestInflectionInterface { public function setInjectedValue(string $value): void; } class TestInflectionTarget { private ?string $injectedValue = null; private ?TestDependency $dependency = null; public function setInjectedValue(string $value): void { $this->injectedValue = $value; } public function getInjectedValue(): ?string { return $this->injectedValue; } public function setDependency(TestDependency $dependency): void { $this->dependency = $dependency; } public function getDependency(): ?TestDependency { return $this->dependency; } } class TestInflectionImplementation implements TestInflectionInterface { private ?string $injectedValue = null; public function setInjectedValue(string $value): void { $this->injectedValue = $value; } public function getInjectedValue(): ?string { return $this->injectedValue; } }