<commit_before><?php

declare(strict_types=1);

namespace Lit\Air;

use Lit\Air\Injection\InjectorInterface;
use Lit\Air\Psr\CircularDependencyException;
use Lit\Air\Psr\Container;
use Lit\Air\Psr\ContainerException;
use Psr\Container\ContainerInterface;

class Factory
{
    /**
     * @var Container
     */
    protected $container;
    /**
     * @var array
     */
    protected $circularStore = [];

    public function __construct(ContainerInterface $container = null)
    {
        if ($container instanceof Container) {
            $this->container = $container;
        } elseif (is_null($container)) {
            $this->container = new Container();
        } else {
            $this->container = Container::wrap($container);
        }
        $this->container->set(Container::KEY_FACTORY, $this);
    }

    public static function of(ContainerInterface $container): self
    {
        if (!$container->has(Container::KEY_FACTORY)) {
            return new self($container);
        }

        return $container->get(Container::KEY_FACTORY);
    }

    /**
     * @param string $className
     * @param array $extraParameters
     * @return object
     */
    public function instantiate(string $className, array $extraParameters = [])
    {
        $class = new \ReflectionClass($className);
        $constructor = $class->getConstructor();

        $constructParams = $constructor
            ? $this->resolveParams($constructor->getParameters(), $className, $extraParameters)
            : [];

        $instance = $class->newInstanceArgs($constructParams);
        $this->inject($instance, $extraParameters);

        return $instance;
    }

    /**
     * @param callable $callback
     * @param array $extra
     * @return mixed
     */
    public function invoke(callable $callback, array $extra = [])
    {
        if (is_string($callback) || $callback instanceof \Closure) {
            $method = new \ReflectionFunction($callback);
            $params = $method->getParameters();
        } else {
            if (is_object($callback)) {
                $callback = [$callback, '__invoke'];
            }
            $method = (new \ReflectionClass($callback[0]))->getMethod($callback[1]);
            $params = $method->getParameters();
        }

        if ($method->isClosure()) {
            $name = sprintf('Closure@%s:%d', $method->getFileName(), $method->getStartLine());
        } elseif ($method instanceof \ReflectionMethod) {
            $name = sprintf('Method@%s::%s', $method->getDeclaringClass()->name, $method->name);
        } else {
            $name = (string) $method;
            $name = substr($name, 0, strpos($name, "{\n"));
        }

        return call_user_func_array($callback, $this->resolveParams($params, '!' . $name, $extra));
    }

    /**
     * @param string $className
     * @param array $extraParameters
     * @return object of $className«
     * @throws \Psr\Container\ContainerExceptionInterface
     */
    public function produce($className, array $extraParameters = [])
    {
        if ($this->container->hasCacheEntry($className)) {
            return $this->container->get($className);
        }

        if (!class_exists($className)) {
            throw new \RuntimeException("$className not found");
        }

        $instance = $this->instantiate($className, $extraParameters);

        $this->container->set($className, $instance);

        return $instance;
    }

    /**
     * @param $obj
     * @param array $extra
     * @throws \Psr\Container\ContainerExceptionInterface
     */<commit_msg>add getOrProduce<commit_after><?php

declare(strict_types=1);

namespace Lit\Air;

use Lit\Air\Injection\InjectorInterface;
use Lit\Air\Psr\CircularDependencyException;
use Lit\Air\Psr\Container;
use Lit\Air\Psr\ContainerException;
use Psr\Container\ContainerInterface;

class Factory
{
    /**
     * @var Container
     */
    protected $container;
    /**
     * @var array
     */
    protected $circularStore = [];

    public function __construct(ContainerInterface $container = null)
    {
        if ($container instanceof Container) {
            $this->container = $container;
        } elseif (is_null($container)) {
            $this->container = new Container();
        } else {
            $this->container = Container::wrap($container);
        }
        $this->container->set(Container::KEY_FACTORY, $this);
    }

    public static function of(ContainerInterface $container): self
    {
        if (!$container->has(Container::KEY_FACTORY)) {
            return new self($container);
        }

        return $container->get(Container::KEY_FACTORY);
    }

    /**
     * @param string $className
     * @param array $extraParameters
     * @return object
     */
    public function instantiate(string $className, array $extraParameters = [])
    {
        $class = new \ReflectionClass($className);
        $constructor = $class->getConstructor();

        $constructParams = $constructor
            ? $this->resolveParams($constructor->getParameters(), $className, $extraParameters)
            : [];

        $instance = $class->newInstanceArgs($constructParams);
        $this->inject($instance, $extraParameters);

        return $instance;
    }

    /**
     * @param callable $callback
     * @param array $extra
     * @return mixed
     */
    public function invoke(callable $callback, array $extra = [])
    {
        if (is_string($callback) || $callback instanceof \Closure) {
            $method = new \ReflectionFunction($callback);
            $params = $method->getParameters();
        } else {
            if (is_object($callback)) {
                $callback = [$callback, '__invoke'];
            }
            $method = (new \ReflectionClass($callback[0]))->getMethod($callback[1]);
            $params = $method->getParameters();
        }

        if ($method->isClosure()) {
            $name = sprintf('Closure@%s:%d', $method->getFileName(), $method->getStartLine());
        } elseif ($method instanceof \ReflectionMethod) {
            $name = sprintf('Method@%s::%s', $method->getDeclaringClass()->name, $method->name);
        } else {
            $name = (string)$method;
            $name = substr($name, 0, strpos($name, "{\n"));
        }

        return call_user_func_array($callback, $this->resolveParams($params, '!' . $name, $extra));
    }

    /**
     * @param string $className
     * @param array $extraParameters
     * @return object of $className«
     * @throws \Psr\Container\ContainerExceptionInterface
     */
    public function produce(string $className, array $extraParameters = [])
    {
        if ($this->container->hasCacheEntry($className)) {
            return $this->container->get($className);
        }

        if (!class_exists($className)) {
            throw new \RuntimeException("$className not found");
        }

        $instance = $this->instantiate($className, $extraParameters);

        $this->container->set($className, $instance);

        return $instance;
    }

    /**
     * @param string $className
     * @return object of $className
     * @throws \Psr\Container\ContainerExceptionInterface
     */
    public function getOrProduce(string $className)
    {
        $recipe = $this->container->getRecipe($className);
        if ($recipe) {
            return $this->container->get($className);
        }
        return $this->produce($className);
    }

    /**
     * @param $obj
     * @param array $extra
     * @throws \Psr\Container\ContainerExceptionInterface
     */
{'fraction_non_alphanumeric': 0.14489876935291782, 'fraction_numerical': 0.0010586211459573905, 'mean_word_length': 2.438580527752502, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 66, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6388117', 'n_tokens_mistral': 2062, 'n_tokens_neox': 2026, 'n_words': 410}