protected function fetch(string $playerName, string $cacheKey, array $fetchStrategies)
    {
        $playerCacheKey = strtolower($playerName);

        // Return immediately if the cache already contains the requested item
        if (isset($this->cache[$cacheKey][$playerCacheKey])) {
            return $this->cache[$cacheKey][$playerCacheKey];
        }

        // Try available strategies until the value is available
        $firstException = null;
        foreach($fetchStrategies as $url => $conversionMethod) {
            try {
                $url = sprintf($url, urlencode($playerName));

                $data = $this->fetchUrl($url);

                $result = call_user_func([$this->dataConverter, $conversionMethod], $data);

                // Cache resulting object(s) if they haven't been cached yet
                $targetObject = false;
                foreach($result as $resultCacheKey => $resultObject) {
                    if (!isset($this->cache[$resultCacheKey][$playerCacheKey])) {
                        $this->cache[$resultCacheKey][$playerCacheKey] = $resultObject;
                    }

                    if ($resultCacheKey === $cacheKey) {
                        $targetObject = $resultObject;
                    }
                }

                // Return if one of the objects contained our cacheKey
                if ($targetObject) {
                    return $targetObject;
                }
            } catch (Exception $exception) {
                if ($exception instanceof FetchFailedException || $exception instanceof DataConversionException) {
                    if (!$firstException) {
                        $firstException = $exception;
                    }
                } else {
                    /** @noinspection PhpUnhandledExceptionInspection */
                    throw $exception;
                }
            }
        }

        throw new FetchFailedException(sprintf("None of the fetch strategies (%s) succeeded in retrieving the player's %s.", implode(", ", $fetchStrategies), $cacheKey), 0, $firstException);
    }