? T : mixed) */ public function get(string $id): mixed { $instance = $this->phpDiContainer->get($id); // Only apply inflections to objects, not arrays or primitives if (is_object($instance)) { return $this->applyInflections($instance); } return $instance; } public function has(string $id): bool { return $this->phpDiContainer->has($id); } public function set(string $name, mixed $value): void { $this->phpDiContainer->set($name, $value); } /** * @template T of object * @param class-string $name * @param array $parameters * @return T */ public function make(string $name, array $parameters = []): object { $instance = $this->phpDiContainer->make($name, $parameters); return $this->applyInflections($instance); } public function inflect(string $class): InflectionHelper { return new InflectionHelper($this, $class); } public function register(string $abstract, array $dependencies = []): object { if ($dependencies === []) { // Simple registration with no dependencies $this->phpDiContainer->set($abstract, create($abstract)); } else { // Registration with explicit dependencies $this->phpDiContainer->set( $abstract, function (InflectableContainer $container) use ($abstract, $dependencies): object { $resolvedDependencies = []; foreach ($dependencies as $dependency) { $resolvedDependencies[] = $container->get($dependency); } return new $abstract(...$resolvedDependencies); }, ); } // Process attributes for the registered class $this->processAttributes($abstract); return $this->get($abstract); } public function bind(string $abstract, string $concrete, array $dependencies = []): object { if ($dependencies === []) { // Simple interface-to-implementation binding $this->phpDiContainer->set($abstract, create($concrete)); } else { // Interface-to-implementation binding with explicit dependencies $this->phpDiContainer->set( $abstract, function (InflectableContainer $container) use ($concrete, $dependencies): object { $resolvedDependencies = []; foreach ($dependencies as $dependency) { $resolvedDependencies[] = $container->get($dependency); } return new $concrete(...$resolvedDependencies); }, ); } return $this->get($abstract); } public function addAttributeProcessor(AttributeProcessorInterface $processor): void { $this->attributeProcessors[] = $processor; } public function registerInflection(string $targetClass, string $method, array $params): void { $this->inflections[$targetClass][] = [ 'method' => $method, 'params' => $params, ]; } private function applyInflections(object $instance): object { $instanceClass = $instance::class; // Apply direct class inflections $this->applyDirectInflections($instance, $instanceClass); // Apply interface/parent class inflections $this->applyInheritanceInflections($instance); return $instance; } private function applyDirectInflections(object $instance, string $instanceClass): void { if (isset($this->inflections[$instanceClass])) { foreach ($this->inflections[$instanceClass] as $inflection) { $resolvedParams = $this->resolveParameters($inflection['params']); $methodName = $inflection['method']; // @phpstan-ignore-next-line Dynamic method call is intentional for inflection $instance->$methodName(...$resolvedParams); } } } private function applyInheritanceInflections(object $instance): void { foreach ($this->inflections as $targetClass => $inflections) { if ($instance instanceof $targetClass) { foreach ($inflections as $inflection) { $resolvedParams = $this->resolveParameters($inflection['params']); $methodName = $inflection['method']; // @phpstan-ignore-next-line Dynamic method call is intentional for inflection $instance->$methodName(...$resolvedParams); } } } } private function resolveParameters(array $params): array { $resolved = []; foreach ($params as $param) { if (is_string($param) && $this->isResolvableClass($param)) { $resolved[] = $this->phpDiContainer->get($param); } else { $resolved[] = $param; } } return $resolved; } private function processAttributes(string $className): void { foreach ($this->attributeProcessors as $processor) { if ($processor->canProcess($className)) { $processor->process($className); } } } private function isResolvableClass(string $param): bool { // Check if it's a class name (contains backslash or ends with ::class pattern) return class_exists($param) || interface_exists($param) || $this->phpDiContainer->has($param); } }