reinfi/zf-optimized-servicemanager

View on GitHub
src/Service/Handler/AutowireHandler.php

Summary

Maintainability
A
25 mins
Test Coverage
<?php

namespace Reinfi\OptimizedServiceManager\Service\Handler;

use Reinfi\DependencyInjection\Injection\AutoWiring;
use Reinfi\DependencyInjection\Injection\AutoWiringPluginManager;
use Reinfi\DependencyInjection\Injection\InjectionInterface;
use Reinfi\DependencyInjection\Service\AutoWiring\ResolverService;
use Reinfi\OptimizedServiceManager\Model\InstantiationMethod;
use Reinfi\OptimizedServiceManager\Types\TypeInterface;
use Zend\ServiceManager\ServiceManager;

/**
 * @package Reinfi\OptimizedServiceManager\Service\Handler
 */
class AutowireHandler extends AbstractHandler
{
    /**
     * @var ResolverService
     */
    protected $resolverService;

    /**
     * @var ServiceManager
     */
    protected $container;

    /**
     * @param ResolverService $resolverService
     * @param ServiceManager $container
     */
    public function __construct(
        ResolverService $resolverService,
        ServiceManager $container
    ) {
        $this->resolverService = $resolverService;
        $this->container = $container;
    }

    /**
     * @inheritDoc
     */
    protected function buildBody(
        InstantiationMethod $method,
        TypeInterface $type
    ) {
        $injections = $this->resolverService->resolve($type->getService());

        $injectionCalls = [];
        foreach ($injections as $injection) {
            switch (true) {
                case $injection instanceof AutoWiring:
                    $injectionCalls[] = $this->handleAutoWiring($injection);
                    break;
                case $injection instanceof AutoWiringPluginManager:
                    $injectionCalls[] = $this->handleAutoWiringPluginManager($injection);
                    break;
                default:
                    $injectionCalls[] = $this->handleOther($injection);
            }
        }

        if (count($injectionCalls) === 0) {
            $method->addBodyPart(sprintf('return new \\%s();', $type->getService()));

            return;
        }

        $method->addBodyPart(sprintf('return new \\%s(', $type->getService()));
        $lastInjectionCall = array_pop($injectionCalls);
        foreach ($injectionCalls as $injectionCall) {
            $method->addBodyPart($injectionCall . ',');
        }
        $method->addBodyPart($lastInjectionCall);
        $method->addBodyPart(');');
    }

    /**
     * @param AutoWiring $injection
     *
     * @return string
     */
    protected function handleAutoWiring(AutoWiring $injection): string
    {
        $serviceNameProperty = new \ReflectionProperty(
            get_class($injection),
            'serviceName'
        );

        $serviceNameProperty->setAccessible(true);
        $serviceName = $serviceNameProperty->getValue($injection);

        // If service is created via abstract factory we need to get it from underlying container.
        if ($this->container->canCreateFromAbstractFactory($serviceName, $serviceName)) {
            return sprintf(
                '$this->instances[\'%s\'] ?? parent::get(\'%s\')',
                $serviceName,
                $serviceName
            );
        }

        return sprintf(
            '$this->instances[\'%s\'] ?? $this->get(\'%s\')',
            $serviceName,
            $serviceName
        );
    }

    /**
     * @param AutoWiringPluginManager $injection
     *
     * @return string
     */
    protected function handleAutoWiringPluginManager(AutoWiringPluginManager $injection): string
    {
        $serviceNameProperty = new \ReflectionProperty(
            get_class($injection),
            'serviceName'
        );

        $serviceNameProperty->setAccessible(true);
        $serviceName = $serviceNameProperty->getValue($injection);

        $pluginManagerProperty = new \ReflectionProperty(
            get_class($injection),
            'pluginManager'
        );

        $pluginManagerProperty->setAccessible(true);
        $pluginManager = $pluginManagerProperty->getValue($injection);

        return sprintf(
            'parent::get(\'%s\')->get(\'%s\')', $pluginManager, $serviceName
        );
    }

    /**
     * @param InjectionInterface $injection
     *
     * @return string
     */
    protected function handleOther(InjectionInterface $injection): string
    {
        $instance = $injection($this->container);

        return sprintf(
            '$this->instances[\'%s\'] ?? parent::get(\'%s\')',
            get_class($instance),
            get_class($instance)
        );
    }
}