Parallel cURL request using PHP multithreading - php

I have a function callFunctions() which basically calls various functions, say api1(), api2(), api3(), etc..
These functions make cURL requests to various APIs which return data which I then parse and merge into a globally declared array, say $finalData
Earlier i was calling these functions in linear order and each function took their own time to fetch results, parse and merge them into $finalData.
global $finalData = array();
callFunctions(){
api1();
api2();
..
..
api10();
}
But it takes a lot of time to get the final data due to large number of requests.
So, I tried to call these functions parallely using Multi-threading.
class ApiParallelQueries extends Thread {
public function __construct($i){
$this->i=$i;
}
public function run(){
selectApis($this->i);
}
}
function selectApis($i){
switch($i){
case 1: api1();break;
case 2: api2();break;
..
..
case 10: api10();break;
}
}
callFunctions(){
for($i = 1; $i <= 10; $i++){
$workers[$i] = new ApiParallelQueries($i);
$workers[$i]->start();
}
for($i = 1; $i <= 10; $i++){
$workers[$i]->join();
}
}
But the problem is, I'm not getting the result I was getting from the sequential function calls.
I'm not sure if it has something to do with global variables or not but i'm guessing thats the problem,
could it be some other issue? can we not make cURL requests with multithreading?
Note: I also noticed a strange thing while i was trying this, i tried to print timestamps and the time was different when the functions were called using multi-threading.

Related

Better "Randomization" with Simplexml and PHP

I'm working on a 'thought' function for a game i'm working on -- it pulls random strings from an XML file, combines them and makes them 'funny'. However, i'm running into a small issue in that the same couple of items keep getting selected each time.
The two functions I am using are
function randRoller($number)
{
mt_srand((microtime()*time())/3.145);
$x = [];
for($i = 0; $i < 100; $i++)
{
#$x = mt_rand(0,$number);
}
return mt_rand(0,$number);
}
/* RETRIEVE ALL RELEVANT DATA FROM THE XML FILE */
function retrieveFromXML($node)
{
$node = strtolower($node);
$output = [];
$n = substr($node,0,4);
#echo $node;
foreach($this->xml->$node->$n as $data)
{
$output[] = $data->attributes();
}
$count = count($output)-1;
$number = $this->randRoller($count);
return $output[$number];
}
Granted, the "randRoller" function is sorta defunct now because the orginal version I had (Which 'rolled' ten numbers from the count, and then selected the one which got the most number of dice) didn't work as planned.
I've tried everything i can think of to get better results && have googled my brains out to fix it. but still am getting the same repetitive results.
Don't use mt_srand() unless you know what you are doing, since it is called automatically. See the note on http://php.net/manual/en/function.mt-srand.php:
Note: There is no need to seed the random number generator with srand() or mt_srand() as this is done automatically.
Remove (all) the mt_srand() call(s).

Possible to overide a foreach variable parameter within itself?

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;
}

Can I retry file_get_contents() until it opens a stream?

I am using PHP to get the contents of an API. The problem is, sometimes that API just sends back a 502 Bad Gateway error and the PHP code can’t parse the JSON and set the variables correctly. Is there some way I can keep trying until it works?
This is not an easy question because PHP is a synchronous language by default.
You could do this:
$a = false;
$i = 0;
while($a == false && $i < 10)
{
$a = file_get_contents($path);
$i++;
usleep(10);
}
$result = json_decode($a);
Adding usleep(10) allows your server not to get on his knees each time the API will be unavailable. And your function will give up after 10 attempts, which prevents it to freeze completely in case of long unavailability.
Since you didn't provide any code it's kind of hard to help you. But here is one way to do it.
$data = null;
while(!$data) {
$json = file_get_contents($url);
$data = json_decode($json); // Will return false if not valid JSON
}
// While loop won't stop until JSON was valid and $data contains an object
var_dump($data);
I suggest you throw some sort of increment variable in there to stop attempting after X scripts.
Based on your comment, here is what I would do:
You have a PHP script that makes the API call and, if successful, records the price and when that price was acquired
You put that script in a cronjob/scheduled task that runs every 10 minutes.
Your PHP view pulls the most recent price from the database and uses that for whatever display/calculations it needs. If pertinent, also show the date/time that price was captured
The other answers suggest doing a loop. A combo approach probably works best here: in your script, put in a few loops just in case the interface is down for a short blip. If it's not up after say a minute, use the old value until your next try.
A loop can solve this problem, but so can a recursive function like this one:
function file_get_contents_retry($url, $attemptsRemaining=3) {
$content = file_get_contents($url);
$attemptsRemaining--;
if( empty($content) && $attemptsRemaining > 0 ) {
return file_get_contents_retry($url, $attemptsRemaining);
}
return $content;
}
// Usage:
$retryAttempts = 6; // Default is 3.
echo file_get_contents_retry("http://google.com", $retryAttempts);

How do I implement this random number generator above every comment in WordPress?

I have a random number generator here, but I can't get it working above all WordPress comments. I get:
Fatal error: Cannot redeclare class Random in [mysite]/functions.php on line 148
Here is the function for the random number generator:
/**
* Generate random images for the forum games function
*/
function random_forum_games() {
class Random {
// random seed
private static $RSeed = 0;
// set seed
public static function seed($s = 0) {
self::$RSeed = abs(intval($s)) % 9999999 + 1;
self::num();
}
// generate random number
public static function num($min = 0, $max = 9999999) {
if (self::$RSeed == 0) self::seed(mt_rand());
self::$RSeed = (self::$RSeed * 125) % 2796203;
return self::$RSeed % ($max - $min + 1) + $min;
}
}
// set seed
Random::seed(42);
// echo 10 numbers between 1 and 10
for ($i = 0; $i < 1; $i++) {
echo Random::num(1, 10) . '<br />';
}
}
add_action( 'forum_games', 'random_forum_games' );
In order to call the function above every comment, in my comments template, I have:
<?php do_action ( 'forum_games' ); ?>
I know why what I am doing is wrong, but I am not really a "programmer". I came here for a little bit of simple help, whether it be a single line of code or a link to something that can tell me what to do without trying to teach me PHP. I do not have time to learn PHP, so please be kind. If there is not a simple solution, then you don't have to answer.
I don't mean to sound defensive, but I have been turned away by programmers before. Not everyone who needs to get something running has the time or skill to learn every single detail. You don't expect every person who prepares a meal for their family to learn how to hunt or to make pesticides, so please do not turn me away just because I cannot do PHP. If you want, just link me to something that isn't trying to teach me to code from scratch. I am doing as much of the work myself as I can, and I'm asking only for tidbits of support, so please don't be rude. Thank you.
You do not define a class inside a function. It should be defined OUTSIDE of the function, and then you can (if need be) INSTANTIATE it inside the function, e.g.
class rand {
...
}
function get_rand() {
$r = new rand();
}
or better yet, you would simply have a get_rand() method inside the class:
class rand() {
function get_rand() {
...
}
}
$r = new rand();
echo $r->get_rand();
it seems like you declared the function more than once. Maybe it is included more than once or you are calling the class more than once. Try moving the class out of the function and if it is in an include file, include it only once with include_once rather than include.
As for people not being friendly... some are, so what. Most are friendly, helpful... just make sure you give details about your problem (code + way to make it repeatable) and show you made an effort, you'll be fine :)

Is it possible to get the statements within a method in PHP?

function mainFunction() {
functionA(5, "blah");
functionB("ok", "whatever");
}
How to write a function GetFunctions that returns the functions within mainFunction?
How to call them with the parameters given in mainFunction?
How to call them as follows?
foreach (GetFunctions(mainFunction) as $function) {
print "Calling function $function: ";
call($functionA); // called with parameters(5, "blah")
}
Working in PHP 5.2.8
EDIT: OK, here's a more complete explanation. I tried to keep it simple to make it easy to understand, but apparently that wasn't a good idea.
The goal is to call each assertion within a given static method. I am writing a testing framework. Each assertion returns true or false.
I am calling the methods as follows.
$methods = get_class_methods('LibraryTests');
foreach ($methods as $method) {
if ( StartsWith($method, 'Test') ) {
print "calling: " . $method . ": ";
call_user_func('LibraryTests::' . $method);
}
}
The above code calls each method within the class, but I want to call each assertion individually and track the result (true/false). CallAssertion is supposed to call each assertion (such as TestUnit::AssertEqual(GetFormattedHour(5), "5 PM");). This is the method that I am asking about.
Here is the class:
class LibraryTests extends TestUnit {
static $success = 0;
static $failure = 0;
static $total = 0;
static function CallAssertion($assertion) {
self::$total += 1;
if ($assertion) { self::$success += 1; }
else { self::$failure += 1; }
}
static function TestGetFormattedHour() {
TestUnit::AssertEqual(GetFormattedHour(5), "5 PM");
TestUnit::AssertEqual(GetFormattedHour(16), "4 PM");
}
So, the question is, how to write CallAssertion?
You can't.
Instead, create a class and use reflection to get its methods.
Regardless, you'll want to figure out why this is necessary and see if there is an entirely different approach you can use.
(If this is for debugging purposes, you can use debug_backtrace to inspect but its purpose is not for calling functions as you have described in your question.)
Hmm, what problem are you actually trying to solve. To me it sounds like you're trying to inspect the call stack at runtime. If so, I'd suggest just using debug_backtrace() (src).
I wouldn't suggest using that function in production as much though, as it's a rather heavy hit on your code.
One possibility would be to do a file_get_contents on the PHP file that contains main_function, then go through it to parse out main_function and the functions it calls. Of course, I don't know your situation so that might not work.
You can do this with:
http://php.net/manual/en/function.token-get-all.php
Probably a bad idea, but good luck!

Categories