I have this function in order to retrieve the count of Facebook comments to blog posts:
function comment_count($url) {
$json = json_decode(file_get_contents('https://graph.facebook.com/?ids=' . $url));
return ($json->$url->comments) ? $json->$url->comments : 0;
}
However if I insert it in a loop fetching the results of a query in order to retrieve five posts on a page, this function is seriously affecting the speed of the website (the page takes up to 6-7 seconds to load).
Is there a way to avoid this? Why is it so slow?
Thanks
Pass in a comma separated list of URLs to the ids parameter to get all the counts at once, or alternatively, cache them on the server side and use those values.
Example: https://graph.facebook.com/?ids=http://www.google.com,http://www.bing.com,http://www.yahoo.com
This is specified in Facebook's Graph API Reference under the section "selection"
An example implementation follows:
<?php
function comment_count($urls) {
$json = json_decode(file_get_contents('https://graph.facebook.com/?ids=' . implode(',', array_map("rawurlencode", $urls))));
$output = Array();
foreach($json as $url=>$data)
{
$output[$url] = isset($data->comments) ? $data->comments : 0;
}
return $output;
}
var_dump(comment_count(Array('http://www.facebook.com/', 'http://www.google.com')));
I hope this helps!
Related
I have just discovered you can get pagination results through the api by passing in the page parameter like so:
$projects = $client->get('projects/147/time-records?page=3')->getJson();
Is there a way of knowing how many time records a project has so I know how many times I need to paginate?
Alternatively, how would I go about retrieving several pages worth of data - i'm struggling with the code!
I have created an issue on Github - will await a response.
For now, I do the following:
// Get all the projects
// Set the page number
$page = 1;
// Create an empty array
$project_records = array();
// Get the first page of results
$project_records_results = $client->get('projects?page=' . $page)->getJson();
// Merge the results with base array
$project_records = array_merge($project_records, $project_records_results);
// Get the next page of results,
// if it returns something merge with the base array and continue
while ($project_records_results = $client->get('projects?page=' . ++$page)->getJson()) {
$project_records = array_merge($project_records, $project_records_results);
}
Sure. All paginated results will include following headers:
X-Angie-PaginationCurrentPage - indicates current page
X-Angie-PaginationItemsPerPage - indicates number of items per page
X-Angie-PaginationTotalItems - indicates number of items in the entire data set.
When you get header values, simple:
$total_pages = ceil($total_items_header_value / $items_per_page_header_value);
will give you number of pages that are in the collection.
Alternative: You can iterate through pages (by starting with page GET parameter set to 1, and incrementing it) until you get an empty result (page with no records). Page that returns no records is the last page.
Please note, that the headers are now all lowercase (v1)!
So the answer above should be corrected.
To get them call:
$headers = $client->get($path)->getHeaders();
Working code example from /api/v1/:
$paginationCurrentPage = isset($headers['x-angie-paginationcurrentpage'][0]) ? $headers['x-angie-paginationcurrentpage'][0] : NULL;
$paginationItemsPerPage = isset($headers['x-angie-paginationitemsperpage'][0]) ? $headers['x-angie-paginationitemsperpage'][0] : NULL;
$paginationTotalItems = isset($headers['x-angie-paginationtotalitems'][0]) ? $headers['x-angie-paginationtotalitems'][0] : NULL;
I am creating a PHP class that use a 3rd party API. The API has a method with this request URL structure:
https://api.domain.com/path/sales?page=x
Where "x" is the page number.
Each page return 50 sales and I need to return an undefined number of pages for each user (depending on the user sales) and store some data from each sale.
I have already created some methods that get the data from the URL, decode and create a new array with the desired data, but only with the first page request.
Now I want to create a method that check if is there another page, and if there is, get it and make the check again
How can I check if there is another page? And how to create a loop that get another page if there is one?
I have already this code, but it create an infinite loop.
require('classes/class.example_api.php');
$my_class = new Example_API;
$page = 1;
$sales_url = $my_class->sales_url( $page );
$url = $my_class->get_data($sales_url);
while ( !empty($url) ) {
$page++;
$sales_url = $my_class->sales_url( $page );
$url = $my_class->get_data($sales_url);
}
I don't use CURL, I use file_get_content. When I request a page out of range, I get this result:
string(2) "[]"
And this other after json_decode:
array(0) { }
From your input, in the while loop, you change the $url (which actually holds the data return by the API call) and this is checked for emptiness, if I'm correct.
$url = $my_class->get_data($sales_url);
If the above is just the original response (so in case of page out of range a string "[]"), it will never get empty("[]") to true. So my guess is that the return value from get_data is this string, while it should be the actual array/json even if the result is empty (ie I suspect that you perform the json_decode once you have collected the data e.g. outside the loop).
If this is the case, my suggestion would be to either check for "[]" in the loop (e.g. while ($url !== "[]")) or within the loop decode the response data ($url = json_decode($url)).
From my experience with several API's, the response returns the number of rows found, and x number per page starting with page 1.
In your case, if the response has the number of rows then just divide it by the x number page and loop through the results as page numbers.
$results = 1000;
$perPage = 50;
$pages = ceil($results/$perPage);
for (i=1; $i <= $pages; $i++){
// execute your api call and store the results
}
Hope this help.
From the responses you've shown, you get an empty array if there are no results. In that case, you could use the empty method in a loop to determine if there's anything to report:
// Craft the initial request URL
$page = 1;
$url = 'https://api.domain.com/path/sales?page=' . $page;
// Now start looping
while (!empty(file_get_contents($url)) {
// There's data here, do something with it
// And set the new URL for the next page
$url = 'https://api.domain.com/path/sales?page=' . ++$page;
}
That way it will keep looping over all the pages, until there is no more data.
Check http response headers for total number of items in set
I'm connecting to the trakt.tv api, I want to create a little app for myself that displays movies posters with ratings etc.
This is what I'm currently using to retrieve their .json file containing all the info I need.
$json = file_get_contents('http://api.trakt.tv/movies/trending.json/2998fbac88fd207cc762b1cfad8e34e6');
$movies = json_decode($json, true);
$movies = array_slice($movies, 0, 20);
foreach($movies as $movie) {
echo $movie['images']['fanart'];
}
Because the .json file is huge it is loading pretty slow. I only need a couple of attributes from the file, like title,rating and the poster link. Besides that I only need the first 20 or so. How can I make sure to load only a part of the .json file to load it faster?
Besides that I'm not experienced with php in combination with .json so if my code is garbage and you have suggestions I would love to hear them.
Unless the API provides a limit parameter or similar, I don't think you can limit the query at your side. On a quick look it doesn't seem to provide this. It also doesn't look like it really returns that much data (under 100KB), so I guess it is just slow.
Given the slow API I'd cache the data you receive and only update it once per hour or so. You could save it to a file on your server using file_put_contents and record the time it was saved too. When you need to use the data, if the saved data is over an hour old, refresh it.
This quick sketch of an idea works:
function get_trending_movies() {
if(! file_exists('trending-cache.php')) {
return cache_trending_movies();
}
include('trending-cache.php');
if(time() - $movies['retreived-timestamp'] > 60 * 60) { // 60*60 = 1 hour
return cache_trending_movies();
} else {
unset($movies['retreived-timestamp']);
return $movies;
}
}
function cache_trending_movies() {
$json = file_get_contents('http://api.trakt.tv/movies/trending.json/2998fbac88fd207cc762b1cfad8e34e6');
$movies = json_decode($json, true);
$movies = array_slice($movies, 0, 20);
$movies_with_date = $movies;
$movies_with_date['retreived-timestamp'] = time();
file_put_contents('trending-cache.php', '<?php $movies = ' . var_export($movies_with_date, true) . ';');
return $movies;
}
print_r(get_trending_movies());
I have made a small script which uses the Twitch API. The API only allows a maximum of 100 results per query. I would like to have this query carry on until there are no more results.
My theory behind this, is to run a foreach or while loop and increment the offset by 1 each time.
My problem however, is that I cannot change the foreach parameters within itself.
Is there anyway of executing this efficiently without causing an infinite loop?
Here is my current code:
<?php
$newcurrentFollower = 0;
$offset=0;
$i = 100;
$json = json_decode(file_get_contents("https://api.twitch.tv/kraken/channels/greatbritishbg/follows?limit=25&offset=".$offset));
foreach ($json->follows as $follow)
{
echo $follow->user->name . ' (' . $newcurrentFollower . ')' . "<br>";
$newcurrentFollower++;
$offset++;
$json = json_decode(file_get_contents("https://api.twitch.tv/kraken/channels/greatbritishbg/follows?limit=25&offset=".$offset));
}
?>
Using a While loop:
while($i < $total)
{
$json = json_decode(file_get_contents("https://api.twitch.tv/kraken/channels/greatbritishbg/follows?limit=25&offset=".$offset));
echo $json->follows->user->name . ' (' . $newcurrentFollower . ')' . "<br>";
$newcurrentFollower++;
$offset++;
$i++;
}
Ends up echoing this (No names are successfully being grabbed):
Here is the API part for $json->follows:
https://github.com/justintv/Twitch-API/blob/master/v2_resources/channels.md#get-channelschannelfollows
You can use this:
$offset = 0;
$count = 1;
do {
$response = json_decode(file_get_contents(
'https://api.twitch.tv/kraken/channels/greatbritishbg/follows?limit=100&offset=' . $offset
));
foreach($response->follows as $follow) {
echo $follow->user->name . ' (' . ($count++) . ')' . "</br>";
}
$offset+=25;
} while (!empty($response->follows));
You want to use a while loop here, not just a foreach. Basically:
while (the HTTP request returns results)
{
foreach ($json->follows as $follow)
{
do stuff
}
increment offset so the next request returns the next one not already processed
}
The trickiest part is going to be getting the while condition right so that it returns false when the request gets no more results, and will depend on what the API actually returns if there are no more results.
Also important, the cleanest way would be to have the HTTP request occur as part of the while condition, but if you need to do some complicated computation of the JSON return to check the condition, you can put an initial HTTP request before the loop, and then do another request at the end of each while loop iteration.
The problem is you're only capturing the key not the value. Place it into a datastructure to access the information.
Honestly I find a recursive function much more effective than a iterative/loop approach then just update a datatable or list before the next call. It's simple, uses cursors, lightweight and does the job. Reusable if you use generics on it too.
This code will be in c#, however I know with minor changes you'll be able to get it working in php with ease.
query = //follower object get request//
private void doProcessFollowers(string query)
{
HTTPParse followerData = new HTTPParse(); //custom json wrapper. using the basic is fine. Careful with your cons though
var newRoot = followerData.createFollowersRoot(query); // generates a class populated by json
if (newRoot[0]._cursor != null)
{
populateUserDataTable(newRoot); //update dataset
doProcessFollowers(newRoot[0]._links.next); //recurse
}
}
Anyway - This just allows you to roll through the cursors without needing to worry about indexes - unless you specifically want them for whatever reason. If you're working with generics you can just reuse this code without issue. Find a generic example below. All you need to do to make it reuseable is pass the correct class within the <> of the method call. Can work for any custom class that you use to parse json data with. Which is basically what the 'createfollowerroot()' is in the above code, except that's hard typed.
Also I know it's in c# and the topic is php, with a few minor changes to syntax you'll get it working easily.
Anyway Hope this helped somebody
Generic example:
public static List<T> rootSerialize<T>(JsonTextReader reader)
{
List<T> outputData = new List<T>();
while (reader.Read())
{
JsonSerializer serializer = new JsonSerializer();
var tempData = serializer.Deserialize<T>(reader);
outputData.Add(tempData);
}
return outputData;
}
I'm working on a small and simple code which basically does some tweets filtering. The problem is that I'm hitting the request limit of Twitter API and I would like to know if there is a workaround or if what I want to do just cannot be done.
First, I type a twitter username to retrieve the ID's of people this user follows.
$user_id = $_GET["username"];
$url_post = "http://api.twitter.com/1/friends/ids.json?cursor=-1&screen_name=" . urlencode($user_id);
$following = file_get_contents($url_post, true);
$json = json_decode($following);
$ids = $json->ids;
Twitter API responds with a list of ID's.
Here comes the problem. The next step is to make a request to find out username, profile picture and description for each one of those ID's.
$following = array();
foreach ($ids as $value)
{
$build_url = 'http://api.twitter.com/1/users/lookup.json?user_id=' . $value . '';
$following[] = $build_url;
}
foreach ($following as $url)
{
$data_names = file_get_contents($url, true); //getting the file content
$json_names = json_decode($data_names);
foreach ($json_names as $tweet) {
$name = $tweet->name;
$description = $tweet->description;
echo '<p>';
echo $name . '<br>';
echo $description;
echo '</p>';
}
}
If the user follows 50 people it works. But if he follows, let's say, 600 hundred, that would be 600 hundred request (for username, description and profile pic) to Twitter API which exceeds the limit.
Is there any way to workaround this o it just cannot be done?
Thank you!
You can and should request users/lookup API endPoint with 100 userIds at a time, instead of doing one request per twitter ID. cf. https://dev.twitter.com/docs/api/1.1/get/users/lookup
You have to replace your forEach loop (foreach ($following as $url)) by a recursive function.
At the end of the function, check the number of hits remaining before calling it again (cf. this link to see how to know the time remining until you get rate limited).
If there is no hit left, sleep 15 minutes before calling the function again, otherwise do the call again.
There is plenty of information on how to do this, use Google and search existing stackOverflow questions.