function (&$base) {
    $getFromArray = function (&$base, $key) {
        return isset($base[$key])
            ? $base[$key]
            : null;
    };
    $getCallable = function (&$base, $key) use ($getFromArray) {
        if (is_callable(array($base, $key))) {
            return new JsPhpizeDotCarrier(array($base, $key));
        }
        if ($base instanceof \ArrayAccess) {
            return $getFromArray($base, $key);
        }
    };
    $getRegExp = function ($value) {
        return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp . $value->flags : null;
    };
    $fallbackDot = function (&$base, $key) use ($getCallable, $getRegExp) {
        if (is_string($base)) {
            if (preg_match('/^[-+]?\d+$/', strval($key))) {
                return substr($base, intval($key), 1);
            }
            if ($key === 'length') {
                return strlen($base);
            }
            if ($key === 'substr' || $key === 'slice') {
                return function ($start, $length = null) use ($base) {
                    return func_num_args() === 1 ? substr($base, $start) : substr($base, $start, $length);
                };
            }
            if ($key === 'charAt') {
                return function ($pos) use ($base) {
                    return substr($base, $pos, 1);
                };
            }
            if ($key === 'indexOf') {
                return function ($needle) use ($base) {
                    $pos = strpos($base, $needle);

                    return $pos === false ? -1 : $pos;
                };
            }
            if ($key === 'toUpperCase') {
                return function () use ($base) {
                    return strtoupper($base);
                };
            }
            if ($key === 'toLowerCase') {
                return function () use ($base) {
                    return strtolower($base);
                };
            }
            if ($key === 'match') {
                return function ($search) use ($base, $getRegExp) {
                    $regExp = $getRegExp($search);
                    $search = $regExp ? $regExp : (is_string($search) ? '/' . preg_quote($search, '/') . '/' : strval($search));

                    return preg_match($search, $base);
                };
            }
            if ($key === 'split') {
                return function ($delimiter) use ($base, $getRegExp) {
                    if ($regExp = $getRegExp($delimiter)) {
                        return preg_split($regExp, $base);
                    }

                    return explode($delimiter, $base);
                };
            }
            if ($key === 'replace') {
                return function ($from, $to) use ($base, $getRegExp) {
                    if ($regExp = $getRegExp($from)) {
                        return preg_replace($regExp, $to, $base);
                    }

                    return str_replace($from, $to, $base);
                };
            }
        }

        return $getCallable($base, $key);
    };
    $crawler = &$base;
    $result = $base;
    foreach (array_slice(func_get_args(), 1) as $key) {
        $result = is_array($crawler)
            ? $getFromArray($crawler, $key)
            : (is_object($crawler)
                ? (isset($crawler->$key)
                    ? $crawler->$key
                    : (method_exists($crawler, $method = "get" . ucfirst($key))
                        ? $crawler->$method()
                        : (method_exists($crawler, $key)
                            ? array($crawler, $key)
                            : $getCallable($crawler, $key)
                        )
                    )
                )
                : $fallbackDot($crawler, $key)
            );
        $crawler = &$result;
    }

    return $result;
};

if (!class_exists('JsPhpizeDotCarrier')) {
    class JsPhpizeDotCarrier extends ArrayObject
    {
        public function getValue()
        {
            if ($this->isArrayAccessible()) {
                return $this[0][$this[1]];
            }

            return $this[0]->{$this[1]} ?? null;
        }

        public function setValue($value)
        {
            if ($this->isArrayAccessible()) {
                $this[0][$this[1]] = $value;

                return;
            }

            $this[0]->{$this[1]} = $value;
        }

        public function getCallable()
        {
            return $this->getArrayCopy();
        }

        public function __isset($name)
        {
            $value = $this->getValue();

            if ((is_array($value) || $value instanceof ArrayAccess) && isset($value[$name])) {
                return true;
            }

            return is_object($value) && isset($value->$name);
        }

        public function __get($name)
        {
            return new self(array($this->getValue(), $name));
        }

        public function __set($name, $value)
        {
            $value = $this->getValue();

            if (is_array($value)) {
                $value[$name] = $value;
                $this->setValue($value);

                return;
            }

            $value->$name = $value;
        }

        public function __toString()
        {
            return (string) $this->getValue();
        }

        public function __toBoolean()
        {
            $value = $this->getValue();

            if (method_exists($value, '__toBoolean')) {
                return $value->__toBoolean();
            }

            return !!$value;
        }

        public function __invoke(...$arguments)
        {
            return call_user_func_array($this->getCallable(), $arguments);
        }

        private function isArrayAccessible()
        {
            return is_array($this[0]) || $this[0] instanceof ArrayAccess && !isset($this[0]->{$this[1]});
        }
    }
}
