Dice\Dice::getClosure PHP Method

getClosure() private method

Returns a closure for creating object $name based on $rule, caching the reflection object for later use
private getClosure ( string $name, array $rule ) : callable
$name string the Name of the class to get the closure for
$rule array The container can be fully configured using rules provided by associative arrays. See {@link https://r.je/dice.html#example3} for a description of the rules.
return callable A closure
    private function getClosure($name, array $rule)
    {
        // Reflect the class and constructor, this should only ever be done once per class and get cached
        $class = new \ReflectionClass(isset($rule['instanceOf']) ? $rule['instanceOf'] : $name);
        $constructor = $class->getConstructor();
        // Create parameter generating function in order to cache reflection on the parameters. This way $reflect->getParameters() only ever gets called once
        $params = $constructor ? $this->getParams($constructor, $rule) : null;
        // Get a closure based on the type of object being created: Shared, normal or constructorless
        if (!empty($rule['shared'])) {
            $closure = function (array $args, array $share) use($class, $name, $constructor, $params) {
                // Shared instance: create the class without calling the constructor (and write to \$name and $name, see issue #68)
                $this->instances[$name] = $this->instances[ltrim($name, '\\')] = $class->newInstanceWithoutConstructor();
                // Now call this constructor after constructing all the dependencies. This avoids problems with cyclic references (issue #7)
                if ($constructor) {
                    $constructor->invokeArgs($this->instances[$name], $params($args, $share));
                }
                return $this->instances[$name];
            };
        } else {
            if ($params) {
                $closure = function (array $args, array $share) use($class, $params) {
                    // This class has depenencies, call the $params closure to generate them based on $args and $share
                    return new $class->name(...$params($args, $share));
                };
            } else {
                $closure = function () use($class) {
                    // No constructor arguments, just instantiate the class
                    return new $class->name();
                };
            }
        }
        // If there are shared instances, create them and merge them with shared instances higher up the object graph
        if (isset($rule['shareInstances'])) {
            $closure = function (array $args, array $share) use($closure, $rule) {
                return $closure($args, array_merge($args, $share, array_map([$this, 'create'], $rule['shareInstances'])));
            };
        }
        // When $rule['call'] is set, wrap the closure in another closure which will call the required methods after constructing the object
        // By putting this in a closure, the loop is never executed unless call is actually set
        return isset($rule['call']) ? function (array $args, array $share) use($closure, $class, $rule) {
            // Construct the object using the original closure
            $object = $closure($args, $share);
            foreach ($rule['call'] as $call) {
                // Generate the method arguments using getParams() and call the returned closure (in php7 will be ()() rather than __invoke)
                $params = $this->getParams($class->getMethod($call[0]), ['shareInstances' => isset($rule['shareInstances']) ? $rule['shareInstances'] : []])->__invoke($this->expand(isset($call[1]) ? $call[1] : []));
                $object->{$call[0]}(...$params);
            }
            return $object;
        } : $closure;
    }