public function render(string $library, $params = null, int $ttl = 0, string $cacheName = null): string
	{
		list($class, $method) = $this->determineClass($library);

		// Is it cached?
		$cacheName = ! empty($cacheName) ? $cacheName : $class . $method . md5(serialize($params));

		if (! empty($this->cache) && $output = $this->cache->get($cacheName))
		{
			return $output;
		}

		// Not cached - so grab it...
		$instance = new $class();

		if (! method_exists($instance, $method))
		{
			throw ViewException::forInvalidCellMethod($class, $method);
		}

		// Try to match up the parameter list we were provided
		// with the parameter name in the callback method.
		$paramArray = $this->prepareParams($params);
		$refMethod  = new ReflectionMethod($instance, $method);
		$paramCount = $refMethod->getNumberOfParameters();
		$refParams  = $refMethod->getParameters();

		if ($paramCount === 0)
		{
			if (! empty($paramArray))
			{
				throw ViewException::forMissingCellParameters($class, $method);
			}

			$output = $instance->{$method}();
		}
		elseif (($paramCount === 1) && (
				( ! array_key_exists($refParams[0]->name, $paramArray)) ||
				(array_key_exists($refParams[0]->name, $paramArray) && count($paramArray) !== 1) )
		)
		{
			$output = $instance->{$method}($paramArray);
		}
		else
		{
			$fireArgs      = [];
			$method_params = [];

			foreach ($refParams as $arg)
			{
				$method_params[$arg->name] = true;
				if (array_key_exists($arg->name, $paramArray))
				{
					$fireArgs[$arg->name] = $paramArray[$arg->name];
				}
			}

			foreach ($paramArray as $key => $val)
			{
				if (! isset($method_params[$key]))
				{
					throw ViewException::forInvalidCellParameter($key);
				}
			}

			$output = $instance->$method(...array_values($fireArgs));
		}
		// Can we cache it?
		if (! empty($this->cache) && $ttl !== 0)
		{
			$this->cache->save($cacheName, $output, $ttl);
		}
		return $output;
	}