| Current Path : /var/www/html/vendor/codeception/codeception/src/Codeception/Lib/ |
| Current File : /var/www/html/vendor/codeception/codeception/src/Codeception/Lib/Di.php |
<?php
namespace Codeception\Lib;
use Codeception\Exception\InjectionException;
use Codeception\Util\ReflectionHelper;
class Di
{
const DEFAULT_INJECT_METHOD_NAME = '_inject';
protected $container = [];
/**
* @var Di
*/
protected $fallback;
public function __construct($fallback = null)
{
$this->fallback = $fallback;
}
public function get($className)
{
// normalize namespace
$className = ltrim($className, '\\');
return isset($this->container[$className]) ? $this->container[$className] : null;
}
public function set($class)
{
$this->container[get_class($class)] = $class;
}
/**
* @param string $className
* @param array $constructorArgs
* @param string $injectMethodName Method which will be invoked after object creation;
* Resolved dependencies will be passed to it as arguments
* @throws InjectionException
* @return null|object
*/
public function instantiate(
$className,
$constructorArgs = null,
$injectMethodName = self::DEFAULT_INJECT_METHOD_NAME
) {
// normalize namespace
$className = ltrim($className, '\\');
// get class from container
if (isset($this->container[$className])) {
if ($this->container[$className] instanceof $className) {
return $this->container[$className];
}
throw new InjectionException("Failed to resolve cyclic dependencies for class '$className'");
}
// get class from parent container
if ($this->fallback) {
if ($class = $this->fallback->get($className)) {
return $class;
}
}
$this->container[$className] = false; // flag that object is being instantiated
$reflectedClass = new \ReflectionClass($className);
if (!$reflectedClass->isInstantiable()) {
return null;
}
$reflectedConstructor = $reflectedClass->getConstructor();
if (is_null($reflectedConstructor)) {
$object = new $className;
} else {
try {
if (!$constructorArgs) {
$constructorArgs = $this->prepareArgs($reflectedConstructor);
}
} catch (\Exception $e) {
throw new InjectionException("Failed to create instance of '$className'. " . $e->getMessage());
}
$object = $reflectedClass->newInstanceArgs($constructorArgs);
}
if ($injectMethodName) {
$this->injectDependencies($object, $injectMethodName);
}
$this->container[$className] = $object;
return $object;
}
/**
* @param $object
* @param string $injectMethodName Method which will be invoked with resolved dependencies as its arguments
* @throws InjectionException
*/
public function injectDependencies($object, $injectMethodName = self::DEFAULT_INJECT_METHOD_NAME, $defaults = [])
{
if (!is_object($object)) {
return;
}
$reflectedObject = new \ReflectionObject($object);
if (!$reflectedObject->hasMethod($injectMethodName)) {
return;
}
$reflectedMethod = $reflectedObject->getMethod($injectMethodName);
try {
$args = $this->prepareArgs($reflectedMethod, $defaults);
} catch (\Exception $e) {
$msg = $e->getMessage();
if ($e->getPrevious()) { // injection failed because PHP code is invalid. See #3869
$msg .= '; '. $e->getPrevious();
}
throw new InjectionException(
"Failed to inject dependencies in instance of '{$reflectedObject->name}'. $msg"
);
}
if (!$reflectedMethod->isPublic()) {
$reflectedMethod->setAccessible(true);
}
$reflectedMethod->invokeArgs($object, $args);
}
/**
* @param \ReflectionMethod $method
* @param $defaults
* @throws InjectionException
* @return array
*/
protected function prepareArgs(\ReflectionMethod $method, $defaults = [])
{
$args = [];
$parameters = $method->getParameters();
foreach ($parameters as $k => $parameter) {
$dependency = ReflectionHelper::getClassFromParameter($parameter);
if (is_null($dependency)) {
if (!$parameter->isOptional()) {
if (!isset($defaults[$k])) {
throw new InjectionException("Parameter '$parameter->name' must have default value.");
}
$args[] = $defaults[$k];
continue;
}
$args[] = $parameter->getDefaultValue();
} else {
$arg = $this->instantiate($dependency);
if (is_null($arg)) {
throw new InjectionException("Failed to resolve dependency '$dependency'.");
}
$args[] = $arg;
}
}
return $args;
}
}