public function api($url = NULL, $method = 'GET', $params = array())
 {
     $__ = func_get_args();
     // don't cache if it is a complex url structure.
     if (!isset($url) || is_array($url)) {
         return call_user_func_array(array($this->facebook, 'api'), $__);
     }
     if (is_array($method) && empty($params)) {
         $params = $method;
         $method = 'GET';
     }
     if ($method != 'GET') {
         return call_user_func_array(array($this->facebook, 'api'), $__);
     }
     // use prefix cache object
     $cache = $this->cache;
     $is_cached = FALSE;
     // copy the params so we can modify it slightly to get a more reusable cache key
     $_cacheparams = $params;
     // we need the fb id or access token in the cache key checksum ...
     // if you want generic caching for objects that are not user specific,
     // check out the example of the NoAuthFacebook:
     // @see https://github.com/gaiaops/gaia_core_php/blob/master/tests/cache/facebook.t
     $uid = $this->facebook->getUser();
     $_cacheparams['access_token'] = $uid ? $uid : $this->facebook->getAccessToken();
     // if it is the /me url, lower the soft timeout so we can be more accurate.
     $soft_timeout = $url == '/me' ? self::$SHORT_CACHE_TTL : self::$LONG_CACHE_TTL;
     // create a cache key based off of the url and params
     $cacheKey = 'graph/' . self::$VERSION . '/' . sha1($url . serialize($_cacheparams));
     // grab the data from the cache
     $data = $cache->get($cacheKey);
     // did we get back a valid response?
     if (is_array($data) && is_array($data['response'])) {
         // mark a flag for later that let's us know we have valid data
         $is_cached = TRUE;
         // check to see if the soft timeout has expired.
         // if not, return the data.
         if ($data['touch'] + $soft_timeout > self::time()) {
             return $data['response'];
         }
         // make sure no one else is trying to refresh this data too.
         if (!$cache->add($cacheKey . '.lock', 1, 30) && $cache->get($cacheKey . '.lock')) {
             return $data['response'];
         }
     }
     // placeholder for exception.
     $e = NULL;
     // keep track of when we started. don't want to try forever.
     $start = time();
     // override facebook curl opts temporarily, but hold on to what they were
     // so we can restore them when we are done.
     $orig_opts = \BaseFacebook::$CURL_OPTS;
     \BaseFacebook::$CURL_OPTS[CURLOPT_CONNECTTIMEOUT] = $is_cached ? self::$API_CONN_RETRY_TIMEOUT : self::$API_CONN_TIMEOUT;
     \BaseFacebook::$CURL_OPTS[CURLOPT_TIMEOUT] = $is_cached ? self::$API_RETRY_TIMEOUT : self::$API_TIMEOUT;
     $tries = $is_cached ? 1 : self::$RETRIES;
     // try to get data from facebook. loop a few times.
     for ($i = 0; $i < $tries; $i++) {
         try {
             // ask facebook for my friends
             // need to limit how long we try.
             $data = array('response' => $this->facebook->api($url, $params), 'touch' => self::time());
             // did we get a response back? if not, keep trying!
             if (!is_array($data['response'])) {
                 continue;
             }
             // yay! we got data back.
             // restore facebook curl opts to what they were.
             \BaseFacebook::$CURL_OPTS = $orig_opts;
             $cache->set($cacheKey, $data, 86400 * 7);
             // set the data into the cache for a week.
             // return the data from the array.
             return $data['response'];
         } catch (Exception $e) {
             if ($e instanceof \FacebookApiException && $e->getType() != 'CurlException') {
                 $cache->delete($cacheKey);
                 $is_cached = FALSE;
                 break;
             }
             // don't do anything yet. do some retry looping.
         }
         // did we run out of time?
         // if so, bail.
         if (time() - $start > self::$API_RETRY_TIMEOUT) {
             if (!$e instanceof Exception) {
                 $e = new Exception('took too long to fetch ' . $url . ' from Facebook.');
             }
             break;
         }
         // still trying ... loop again!.
     }
     // restore facebook curl opts to what they were.
     \BaseFacebook::$CURL_OPTS = $orig_opts;
     // do we have a slightly stale version from the cache? if so use that instead.
     if ($is_cached) {
         // update the touch in the cache so we don't keep trying to refresh.
         // try again in 5 minutes.
         $data['touch'] += 300;
         $cache->set($cacheKey, $data, 86400 * 7);
         // return the data;
         return $data['response'];
     }
     // looks like we looped and looped and got nothing back.
     // might be because of an exception from earlier. if so, throw that.
     if ($e instanceof Exception) {
         throw $e;
     }
     // no exception thrown, but no data either. DOH!
     throw new Exception('retries exceeded');
 }