/** * Wrapper to fetch all elements besides some from podio imposed maxresult. * * Examples: * * $result = PodioFetchAll::iterateApiCall('PodioFile::get_for_app', "YOUR_APP_ID", array('attached_to' => 'item')); * $result = PodioFetchAll::iterateApiCall('PodioItem::filter', "YOUR_APP_ID", array(), "items"); * * @param type $function e.g. 'PodioFile::get_for_app' * @param type $id first parameter of function * @param type $params * @param int $limit no of elements fetched on each call * @param String $resulttype if set, the result of the call is suspected to be in $result[$resulttype] * @return type array of all fetched elements */ static function iterateApiCall($function, $id, $params = array(), $limit = 100, $resulttype = null) { $completed = false; $iteration = 0; $result = array(); while (!$completed) { #$tmp_result = $function($id, array_merge($params, array("limit" => $limit, 'offset' => $limit * $iteration))); $tmp_result = call_user_func($function, $id, array_merge($params, array('limit' => $limit, 'offset' => $limit * $iteration))); RateLimitChecker::preventTimeOut(); #echo "done iteration $iteration\n"; $iteration++; if ($tmp_result instanceof PodioCollection) { $result = array_merge($result, $tmp_result->_get_items()); if ($tmp_result instanceof PodioItemCollection) { echo "filtered: {$tmp_result->filtered}, total: {$tmp_result->total} (limit: {$limit})\n"; if (sizeof($tmp_result->_get_items()) < $limit) { $completed = true; } } else { echo "WARNING unexpected collection: " . get_class($tmp_result); } } else { if (isset($resulttype)) { if (is_array($tmp_result) && isset($tmp_result[$resulttype]) && is_array($tmp_result[$resulttype])) { $result = array_merge($result, $tmp_result[$resulttype]); if (sizeof($tmp_result[$resulttype]) < $limit) { $completed = true; } } else { $completed = true; } } else { if (is_array($tmp_result)) { $result = array_merge($result, $tmp_result); if (sizeof($tmp_result) < $limit) { $completed = true; } } else { $completed = true; } } } unset($tmp_result); } return $result; }
/** * If $file represents a file hosted by podio, it is downloaded to $folder. * In any case a link to the file is returned (relative to $folder). * $folder is assumed to be without trailing '/'. * (The problem with files not hosted by podio is that mostly you need a login, * i.e. you wont get the file but an html login page.) * * Uses the file $config['backupTo'].'/filestore.php' to assure no file is downloaded twice. * (over incremental backups). Creates a (sym)link to the original file in case. * * @param type $folder * @param type $file * @return String link relative to $folder or weblink */ function downloadFileIfHostedAtPodio($folder, $file) { global $config; $link = $file->link; if ($file->hosted_by == "podio") { //$filestore: Stores fileid->path_to_file_relative to backupTo-folder. //this is loaded on every call to assure files from interrupted runs are preserved. $filestore = array(); $filenameFilestore = $config['backupTo'] . '/filestore.php'; if (file_exists($filenameFilestore)) { $filestore = unserialize(file_get_contents($filenameFilestore)); } $filename = fixDirName($file->name); while (file_exists($folder . '/' . $filename)) { $filename = 'Z' . $filename; } if (array_key_exists($file->file_id, $filestore)) { echo "DEBUG: Detected duplicate download for file: {$file->file_id}\n"; $existing_file = realpath($config['backupTo'] . '/' . $filestore[$file->file_id]); $link = RelativePaths::getRelativePath($folder, $existing_file); link($existing_file, $folder . '/' . $filename); #symlink($existing_file, $folder.'/'.$filename); } else { try { file_put_contents($folder . '/' . $filename, $file->get_raw()); RateLimitChecker::preventTimeOut(); $link = $filename; $filestore[$file->file_id] = RelativePaths::getRelativePath($config['backupTo'], $folder . '/' . $filename); file_put_contents($filenameFilestore, serialize($filestore)); } catch (PodioBadRequestError $e) { echo $e->body; # Parsed JSON response from the API echo $e->status; # Status code of the response echo $e->url; # URI of the API request // You normally want this one, a human readable error description echo $e->body['error_description']; } } } else { #echo "Warning: Not downloading file hosted by ".$file->hosted_by."\n"; } unset($filestore); return $link; }
/** * If getting close to hitting the rate limit, this method blocks until the * rate-limit count is resetted. * * One might want to call this method before/after every call to the podio api. * * This is a devensive approach as it supspects a rate limited call every time * the header "x-rate-limit-remaining" is not set. E.g. after PodioFile->get_raw(). * * @global boolean $verbose */ public static function preventTimeOut() { global $verbose; if (!isset(self::$start)) { echo "init RateLimitChecker..\n"; self::$start = time(); } //Note: after PodioFile->get_raw() Podio::rate_limit_remaining() leads to errors, since the header is not set. if (array_key_exists('x-rate-limit-remaining', Podio::$last_response->headers)) { $remaining = Podio::$last_response->headers['x-rate-limit-remaining']; $limit = Podio::$last_response->headers['x-rate-limit-limit']; if ($limit == 250) { self::$no_of_estimations = 0; } } else { $remaining = --self::$rate_limit_lower; self::$no_of_estimations++; $limit = 250; } if ($limit == 250) { self::$rate_limit_lower = $remaining; } else { self::$rate_limit = $remaining; } if (isset($verbose) && $verbose) { echo "DEBUG: rate_limit={$limit} remaining={$remaining} " . (array_key_exists('x-rate-limit-remaining', Podio::$last_response->headers) ? "" : "(estimate)") . "\n"; } if ($remaining < self::$stop_limit) { echo "running since " . date('H:i', self::$start) . "\n"; $minutes_till_reset = 60 - (time() - self::$start) / 60 + 1; echo "sleeping {$minutes_till_reset} minutes to prevent rate limit violation.\n"; sleep(60 * $minutes_till_reset); self::$rate_limit_lower = 250; self::$rate_limit = 5000; self::$start = time(); } else { if (self::dummyCallNeccessary($remaining)) { return; } } }