guibranco / pancake

🧰 🛠️ Pancake project - toolkit for PHP projects
https://guibranco.github.io/pancake/
MIT License
3 stars 1 forks source link

[FEATURE] Add Auto-Registration and Dependency Resolution to DI Container in Pancake #237

Open guibranco opened 3 days ago

guibranco commented 3 days ago

Description: I would like to enhance the DI Container in the Pancake project to support auto-registration for services that are not explicitly registered in the container. This auto-registration feature should be enabled by default but configurable via a flag. Additionally, the container should be able to automatically resolve all dependencies, including constructor dependencies, for services that are auto-registered.

Related to #235

Below is an updated code example for the DIContainer class illustrating the requested functionality:

Updated DIContainer Example

<?php

namespace GuiBranco\Pancake;

class DIContainer
{
    private array $services = [];
    private array $sharedInstances = [];
    private bool $autoRegisterEnabled = true;

    public function __construct(bool $autoRegisterEnabled = true)
    {
        $this->autoRegisterEnabled = $autoRegisterEnabled;
    }

    public function register(string $name, callable $resolver, bool $shared = false)
    {
        $this->services[$name] = [
            'resolver' => $resolver,
            'shared' => $shared,
        ];
    }

    public function resolve(string $name)
    {
        if (isset($this->services[$name])) {
            if ($this->services[$name]['shared']) {
                if (!isset($this->sharedInstances[$name])) {
                    $this->sharedInstances[$name] = $this->services[$name]['resolver']($this);
                }
                return $this->sharedInstances[$name];
            }
            return $this->services[$name]['resolver']($this);
        }

        if ($this->autoRegisterEnabled) {
            return $this->autoRegister($name);
        }

        throw new \Exception("Service '{$name}' not registered.");
    }

    private function autoRegister(string $name)
    {
        if (!class_exists($name)) {
            throw new \Exception("Class '{$name}' does not exist.");
        }

        $reflectionClass = new \ReflectionClass($name);
        $constructor = $reflectionClass->getConstructor();

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

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

        foreach ($parameters as $parameter) {
            $parameterClass = $parameter->getType() ? $parameter->getType()->getName() : null;

            if ($parameterClass && class_exists($parameterClass)) {
                $dependencies[] = $this->resolve($parameterClass);
            } else {
                throw new \Exception("Cannot resolve parameter '{$parameter->getName()}' for class '{$name}'.");
            }
        }

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

    public function registerSingleton(string $name, callable $resolver)
    {
        $this->register($name, $resolver, true);
    }

    public function registerTransient(string $name, callable $resolver)
    {
        $this->register($name, $resolver, false);
    }

    public function setAutoRegisterEnabled(bool $enabled)
    {
        $this->autoRegisterEnabled = $enabled;
    }
}

New Features:

  1. Auto-Registration of Services:

    • If a service is not found in the registration array, the DI container will attempt to auto-register it by resolving its constructor dependencies.
    • This auto-registration feature is enabled by default, but can be controlled using the setAutoRegisterEnabled method.
  2. Dependency Resolution:

    • The DI container will automatically resolve constructor dependencies for both explicitly registered and auto-registered services.
    • It will recursively resolve dependencies for services that require other services as constructor arguments.

Example of Auto-Registration:

<?php

// Assume a service with dependencies
class ServiceA {
    public function __construct(ServiceB $serviceB) { }
}

class ServiceB {
    public function __construct(ServiceC $serviceC) { }
}

class ServiceC {
    public function __construct() { }
}

$container = new \GuiBranco\PocMvc\Src\Core\DIContainer();

// Automatically registers and resolves ServiceA, along with its dependencies (ServiceB, ServiceC)
$serviceA = $container->resolve(ServiceA::class);

Task Requirements:

Additional Requirements:

Acceptance Criteria:

gitauto-ai[bot] commented 3 days ago

Click the checkbox below to generate a PR!

@guibranco, You have 5 requests left in this cycle which refreshes on 2024-11-21 10:07:38+00:00. If you have any questions or concerns, please contact us at info@gitauto.ai.