protected function curl($url, array $params = array())
    {
        // Not using array_merge here because that function reindexes numeric keys
        $options = $params + $this->config['curl'] + array(
            CURLOPT_USERAGENT => $this->userAgent,
            CURLOPT_ENCODING => '',
            CURLOPT_FOLLOWLOCATION => true,
        );

        $options[CURLOPT_URL] = $url;
        $options[CURLOPT_HEADER] = true;
        $options[CURLOPT_RETURNTRANSFER] = 1;

         // CURLOPT_FOLLOWLOCATION doesnt play well with open_basedir/safe_mode
        if (ini_get('safe_mode') || ini_get('open_basedir')) {
            $options[CURLOPT_FOLLOWLOCATION] = false;
            $options[CURLOPT_TIMEOUT] = 15;
            $this->config['force_redirects'] = true;
        }

        $handler = curl_init();
        curl_setopt_array($handler, $options);
        $response = curl_exec($handler);

        $status = curl_getinfo($handler, CURLINFO_HTTP_CODE);
        $headerSize = curl_getinfo($handler, CURLINFO_HEADER_SIZE);

        $header = substr($response, 0, $headerSize);
        $body = substr($response, $headerSize);
        curl_close($handler);

        if ($this->config['force_redirects'] && in_array($status, array('301', '302'))) {

            if (preg_match('~(?:location|uri): ?([^\n]+)~i', $header, $matches)) {

                $url = trim($matches['1']);

                // Relative redirections
                if (substr($url, 0, 1) == '/') {
                    $parsed = parse_url($options[CURLOPT_URL]);
                    $url = $parsed['scheme'] . '://' . rtrim($parsed['host'], '/') . $url;
                }

                return $this->curl($url, $options);
            }
        }

        if (empty($body) || !in_array($status, array('200'))) {
            throw new \Exception($status . ': Invalid response for ' . $url);
        }

        return $body;
    }