Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Container.php
<?php

namespace XF;

use function
array_key_exists, is_object;

/**
 * Dependency injection container and object type factory.
 */
class Container implements \ArrayAccess
{
    protected
$data = [];
    protected
$cache = [];
    protected
$cacheable = [];

    protected
$factory = [];
    protected
$factoryObjects = [];

   
#[\ReturnTypeWillChange]
   
public function offsetGet($key)
    {
        if (
array_key_exists($key, $this->cache))
        {
            return
$this->cache[$key];
        }

        if (
array_key_exists($key, $this->data))
        {
           
$value = $this->data[$key];

           
$output = ($this->isInvokable($value) ? $value($this) : $value);
            if (!empty(
$this->cacheable[$key]))
            {
               
$this->cache[$key] = $output;
            }

            return
$output;
        }

       
$namespace = $this->getNamespaceObject($key, $nsKey);
        if (
$namespace)
        {
            return
$namespace->offsetGet($nsKey);
        }

        throw new \
InvalidArgumentException("Container key '$key' was not found");
    }

    public function
isCached($key)
    {
        return
array_key_exists($key, $this->cache);
    }

    public function
decache($key)
    {
        unset(
$this->cache[$key]);
    }

   
#[\ReturnTypeWillChange]
   
public function offsetSet($key, $value)
    {
       
$this->set($key, $value);
    }

   
#[\ReturnTypeWillChange]
   
public function offsetExists($key)
    {
        if (
array_key_exists($key, $this->data))
        {
            return
true;
        }

       
$namespace = $this->getNamespaceObject($key, $nsKey);
        if (
$namespace)
        {
            return
$namespace->offsetExists($nsKey);
        }

        return
false;
    }

   
#[\ReturnTypeWillChange]
   
public function offsetUnset($key)
    {
        unset(
$this->data[$key], $this->cache[$key], $this->cacheable[$key]);
    }

    public function
__get($key)
    {
        return
$this->offsetGet($key);
    }

    public function
__set($key, $value)
    {
       
$this->set($key, $value);
    }

    public function
__isset($key)
    {
        return
$this->offsetExists($key);
    }

    public function
set($key, $value, $cache = null)
    {
       
$this->data[$key] = $value;

        if (
$cache === null)
        {
           
$cache = $this->isInvokable($value);
        }
       
$this->cacheable[$key] = (bool)$cache;
        unset(
$this->cache[$key]);
    }

   
/**
     * @param string $value
     * @param string $subKey Value of the key without the namespace
     *
     * @return \XF\SubContainer\AbstractSubContainer|null
     */
   
protected function getNamespaceObject($value, &$subKey = null)
    {
       
$parts = explode('.', $value, 2);
        if (isset(
$parts[1]))
        {
           
$namespace = $parts[0];
            if (
$this->offsetExists($namespace))
            {
               
$namespaceEntry = $this->offsetGet($namespace);
                if (
$namespaceEntry instanceof \XF\SubContainer\AbstractSubContainer)
                {
                   
$subKey = $parts[1];
                    return
$namespaceEntry;
                }
            }
        }

        return
null;
    }

    public function
wrap($callable)
    {
        return function() use (
$callable)
        {
            return
$callable;
        };
    }

    public function
extend($key, $callable)
    {
        if (!
$this->isInvokable($callable))
        {
            throw new \
InvalidArgumentException("Extension must be invokable");
        }

        if (!
array_key_exists($key, $this->data))
        {
            throw new \
InvalidArgumentException("Container key '$key' was not found");
        }

       
$value = $this->data[$key];
       
$this->data[$key] = function(Container $container) use ($value, $callable)
        {
           
$output = ($container->isInvokable($value) ? $value($container) : $value);
            return
$callable($output, $container);
        };

        if (
array_key_exists($key, $this->cache))
        {
           
// this has already been cached, so we need to run this now so that the
            // next call picks this up
           
$this->cache[$key] = $callable($this->cache[$key], $this);
        }
    }

    public function
getOriginal($key)
    {
        if (
array_key_exists($key, $this->data))
        {
            return
$this->data[$key];
        }

        throw new \
InvalidArgumentException("Container key '$key' was not found");
    }

    public function
factory($type, $callable, $cacheable = true)
    {
        if (!
$this->isInvokable($callable))
        {
            throw new \
InvalidArgumentException("Factory must be invokable");
        }

       
$this->factory[$type] = [$callable, $cacheable];
    }

    public function
extendFactory($type, $callable)
    {
        if (!
$this->isInvokable($callable))
        {
            throw new \
InvalidArgumentException("Extension must be invokable");
        }

        if (!isset(
$this->factory[$type]))
        {
            throw new \
InvalidArgumentException("Factory type '$type' was not found");
        }

       
$original = $this->factory[$type][0];
       
$this->factory[$type][0] = function($class, array $params, Container $container) use ($original, $callable)
        {
            return
$callable($class, $params, $container, $original);
        };
    }

    public function
removeFactory($type)
    {
        unset(
$this->factory[$type], $this->factoryObjects[$type]);
    }

    public function
create($type, $key, array $params = [])
    {
        if (!isset(
$this->factory[$type]))
        {
            throw new \
InvalidArgumentException("Factory type '$type' was not found");
        }

        list(
$callable, $cacheable) = $this->factory[$type];

        if (
$cacheable && isset($this->factoryObjects[$type][$key]))
        {
            return
$this->factoryObjects[$type][$key];
        }

       
$object = $callable($key, $params, $this);
        if (
$cacheable)
        {
           
$this->factoryObjects[$type][$key] = $object;
        }

        return
$object;
    }

    public function
getInvokableFactory($type)
    {
        return function(
$class, array $params = []) use ($type)
        {
            return
$this->create($type, $class, $params);
        };
    }

    public function
decacheFactory($type, $class = null)
    {
        if (
$class)
        {
            unset(
$this->factoryObjects[$type][$class]);
        }
        else
        {
            unset(
$this->factoryObjects[$type]);
        }
    }

    public function
createObject($class, array $params = [], $failSilently = false)
    {
       
$params = array_values($params);

        if (!
class_exists($class))
        {
            if (
$failSilently)
            {
                return
null;
            }
            throw new \
LogicException("Class $class does not exist");
        }

        return new
$class(...$params);
    }

    protected function
isInvokable($value)
    {
        return
is_object($value) && method_exists($value, '__invoke');
    }

    public function
__sleep()
    {
        throw new \
LogicException('Instances of ' . __CLASS__ . ' cannot be serialized or unserialized');
    }

    public function
__wakeup()
    {
        throw new \
LogicException('Instances of ' . __CLASS__ . ' cannot be serialized or unserialized');
    }
}