Backup #819

ID819
Dateipfad/var/www/dev.campus.systemische-tools.de/src/Framework/Container.php
Version3
Typ modified
Größe5.2 KB
Hash4a13d84a6fbed981d501bd4c515d5e25366eebbb65d43a431e87e116e18ee2ba
Datum2025-12-23 08:09:01
Geändert vonclaude-code-hook
GrundClaude Code Pre-Hook Backup vor Edit-Operation
Datei existiert Ja

Dateiinhalt

<?php

namespace Framework;

use ReflectionClass;
use ReflectionNamedType;
use ReflectionParameter;

/**
 * Dependency Injection Container with autowiring support.
 *
 * Features:
 * - Factory registration for lazy instantiation
 * - Autowiring for classes with type-hinted constructors
 * - Singleton caching of resolved services
 */
final class Container
{
    /** @var array<string, object> Cached singleton instances */
    private array $instances = [];

    /** @var array<string, callable> Factory functions */
    private array $factories = [];

    /**
     * Register a factory for a service.
     *
     * @param string $id Service identifier (usually class name)
     * @param callable(Container): object $factory Factory function
     */
    public function set(string $id, callable $factory): void
    {
        $this->factories[$id] = $factory;
        // Clear cached instance if re-registering
        unset($this->instances[$id]);
    }

    /**
     * Register an existing instance as a singleton.
     *
     * @param string $id Service identifier
     * @param object $instance The instance to register
     */
    public function instance(string $id, object $instance): void
    {
        $this->instances[$id] = $instance;
    }

    /**
     * Check if a service is registered.
     */
    public function has(string $id): bool
    {
        return isset($this->instances[$id]) || isset($this->factories[$id]) || class_exists($id);
    }

    /**
     * Get a service by ID.
     *
     * Resolution order:
     * 1. Check cached instances
     * 2. Check registered factories
     * 3. Try autowiring if ID is a class name
     *
     * @param string $id Service identifier (usually class name)
     * @return object The resolved service instance
     * @throws ContainerException If service cannot be resolved
     */
    public function get(string $id): object
    {
        // Return cached instance if available
        if (isset($this->instances[$id])) {
            return $this->instances[$id];
        }

        // Use factory if registered
        if (isset($this->factories[$id])) {
            $this->instances[$id] = ($this->factories[$id])($this);

            return $this->instances[$id];
        }

        // Try autowiring for class names
        if (class_exists($id)) {
            $this->instances[$id] = $this->autowire($id);

            return $this->instances[$id];
        }

        throw new ContainerException("Service not found: {$id}");
    }

    /**
     * Autowire a class by resolving constructor dependencies.
     *
     * @param class-string $class
     * @throws ContainerException
     */
    private function autowire(string $class): object
    {
        $reflection = new ReflectionClass($class);

        if (!$reflection->isInstantiable()) {
            throw new ContainerException("Cannot instantiate {$class}");
        }

        $constructor = $reflection->getConstructor();

        // No constructor = no dependencies
        if ($constructor === null) {
            return new $class();
        }

        $parameters = $constructor->getParameters();
        $dependencies = [];

        foreach ($parameters as $param) {
            $dependencies[] = $this->resolveParameter($param, $class);
        }

        return $reflection->newInstanceArgs($dependencies);
    }

    /**
     * Resolve a single constructor parameter.
     *
     * @throws ContainerException
     */
    private function resolveParameter(ReflectionParameter $param, string $class): mixed
    {
        $type = $param->getType();

        // Handle nullable/optional parameters with default values
        if ($param->isDefaultValueAvailable()) {
            // Try to resolve the type, but fall back to default if it fails
            if ($type instanceof ReflectionNamedType && !$type->isBuiltin()) {
                $typeName = $type->getName();
                // Only try to resolve if explicitly registered (not just class_exists)
                if (isset($this->instances[$typeName]) || isset($this->factories[$typeName])) {
                    try {
                        return $this->get($typeName);
                    } catch (ContainerException) {
                        // Fall through to default value
                    }
                }
            }

            return $param->getDefaultValue();
        }

        // No type hint
        if ($type === null) {
            throw new ContainerException(
                "Cannot resolve parameter \${$param->getName()} in {$class}: no type hint"
            );
        }

        // Union types not supported
        if (!$type instanceof ReflectionNamedType) {
            throw new ContainerException(
                "Cannot resolve parameter \${$param->getName()} in {$class}: union types not supported"
            );
        }

        // Built-in types (string, int, etc.) cannot be autowired
        if ($type->isBuiltin()) {
            if ($type->allowsNull()) {
                return null;
            }

            throw new ContainerException(
                "Cannot resolve parameter \${$param->getName()} in {$class}: built-in type {$type->getName()}"
            );
        }

        // Resolve the dependency
        return $this->get($type->getName());
    }
}

Vollständig herunterladen

Aktionen

Herunterladen

Andere Versionen dieser Datei

ID Version Typ Größe Datum
819 3 modified 5.2 KB 2025-12-23 08:09
495 2 modified 5.1 KB 2025-12-22 15:32
493 1 modified 4.8 KB 2025-12-22 15:31

← Zurück zur Übersicht