1
\$\begingroup\$

I wrote a simple Dependency Resolver in PHP that I'm hoping to get some input on.

The main problem that gave me some trouble was when it had to resolve typed arguments ( like type hinting a constructor argument with an interface, I don't remember exactly what they call it ). The only work around was to let the DependencyResolver know about such dependencies that are not directly resolvable (instantiable).

Note: The class name of this Dependency Resolver is ObjectManager

// dependencies.php
// this is where I define all my dependencies in case if they are not directly instantiable
$deps = array(
  'DependentClass' => array(
     'depclass' => 'DependencyClass'
    )
);

ObjectManager::getInstance()->setDependencies($deps);

ObjectManager uses singleton pattern so I can use the same object throughout the flow

// mainfile.php
use DependencyClassInterface;
use AnotherClass;
Class DependentClass {
  protected $depClass;
  public function __construct(DependencyClassInterface $depclass, AnotherClass $another) {
     $this->depClass = $depClass;
  }
  public function doSomething() { echo "Something"; }
}
ObjectManager::getInstance()
     ->get('DependentClass')
     ->doSomething();

And finally the implementation of Dependency Resolver.

<?php
class ObjectManager
{
protected $dependencies;

protected static $omInstance;

public static function getInstance()
{
    if (!self::$omInstance) {
        self::$omInstance = new ObjectManager();
    }
    return self::$omInstance;
}

public function setDependencies(array $dependencies)
{
    $this->dependencies = $dependencies;
}

public function getDependencies()
{
    return $this->dependencies;
}

public function get($class)
{
    $finalizedParams = array();
    $deps = array();

    // check if deps are defined
    if (array_key_exists($class, $this->dependencies)) {
        $deps = $this->dependencies[$class];
    }

    $reflector = new ReflectionClass($class);

    if (!$reflector->isInstantiable()) {
        throw new Exception("Class {$class} is not instantiable");
    }

    $constructor = $reflector->getConstructor();
    if ($constructor == null) {
        return $reflector->newInstance();
    }

    $parameters = $constructor->getParameters();

    foreach ($parameters as $key => $parameter) {
        $parameterType = $this->getParameterType($parameter);
        $parameterName = $parameter->getName();

        if ($parameterType == null) {
            if ($parameter->isDefaultValueAvailable()) {
                $finalizedParams[$key] = $parameter->getDefaultValue();
            } else {
                throw new Exception("
                Failed resolving argument (dependency) \"$parameterName\" for class $class.
                Not directly instantiatable.
                No default value specified.");
            }
            continue;
        }

        $classReflection = new ReflectionClass($parameterType);

        // if dep is directly instantiable; save and continue
        if ($classReflection->isInstantiable()) {
            $finalizedParams[$key] = $classReflection->newInstance();
            continue;
        }

        // if defined in di.php
        if (array_key_exists($parameterName, $deps)) {
            $argName = $deps[$parameterName];
            $finalizedParams[$key] = ObjectManager::getInstance()->get($argName);
            continue;
        }
    }

    return $reflector->newInstanceArgs($finalizedParams);
}

protected function getParameterType(ReflectionParameter $parameter)
{
    $export = ReflectionParameter::export(
        array(
            $parameter->getDeclaringClass()->name,
            $parameter->getDeclaringFunction()->name,
        ),
        $parameter->name,
        true
    );
    return preg_match('/[>] ([A-z]+) /', $export, $matches)
    ? $matches[1] : null;
}
}

Hopefully, you got the idea. It might be an overkill to resolve dependencies this way, but I like the option of an Object Manager being able to do that.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.