On occasion we need to control a PHP script from a single script but allow it to execute several PHP instances.
PHP it’s self doesn’t have any native ability to multithread, which I remember as being a bit of an irritant when I started using the language, but you can use a couple of tricks to achieve a similar result.
Consider this scenario
Our SMS Gateway interface (or API) is accessed via an RESTful web service (HTTP POST) or our simple HTTP GET api, these services offer a simple way to integrate with us, however there are draw backs the main one being the speed at which you can send data to the interface in a serial fashion.
Imagine the following: Lets assume you want to send 10,000 text messages as fast as possible to the api, your method would do something like the following:
- construct data (your fully formed HTTP request)
- open port
- write data
- read response
- close port
- repeat 10,000 times
With PHP this process is carried out in a linear fashion in the context of a loop, now let’s look at the numbers:
Internet latency to the api is an assumed 10ms, which would put you somewhere in the UK
The ‘system’ that is the speed at which we can read and write back to your script is .5 ms
Total process time is 105000 ms or 105 seconds .. but wouldn’t it be nice to go quicker??
Now this is where in parallel or multi threading would come in handy, if we could kick say 5 of these processes off at once then we could send the same amount of texts in 105 / 5 which is 21 seconds and a vast improvement.
So how can this be done?
Say hello to the Curl Extension https://uk3.php.net/curl this isn’t necessarily included with your vanilla build of PHP so you’ll need to compile it in. The Curl extension offers many extras for the PHP developer one of which will will use to do our multithreading.
It is usually best to explain the idea by example, here ‘s a class called DoMultiThread that shows the basic idea
<?php
class DoMultiThread {
//// i’m not going to simulate a 10,000 volume send but you can get the idea!
// some dummy data an array of 5 names and numbers for constructing unique messages
private $names = array (array (“name” => “peter”, “number” => “077122345566” ),
array (“name” => “fred”, “number” => “077122345567” ),
array (“name” => “susan”, “number” => “077122345568” ),
array (“name” => “jim”, “number” => “077122345569” ),
array (“name” => “katie”, “number” => “077122345510” ) );
function __contruct() {
/// function sets up and sends 5 simultaneous threads
/// returns an array of the 5 outputs
$output = $this->execute ();
}
protected function execute() {
$mh = curl_multi_init ();
$handles = array (); /// will hold all our handle instances
/// create our 5 threads, i wouldn’t advise trying many more
foreach ( $names as $item ) {
// create a new single curl handle
$curl_handle = curl_init ();
// our unique transaction / message
$url = “https://api.textmarketer.co.uk/gateway/?username=user&password=pass&message=Hi ” . $item [‘name’] . “&orig=test&number=” . $item [‘number’];
// setting several options like url, timeout, returntransfer
curl_setopt ( $curl_handle, CURLOPT_URL, $url );
curl_setopt ( $curl_handle, CURLOPT_HEADER, 0 );
curl_setopt ( $curl_handle, CURLOPT_RETURNTRANSFER, true );
curl_setopt ( $curl_handle, CURLOPT_TIMEOUT, 30 );
// add to the curl multi handle, this allows us to do the multi threading
curl_multi_add_handle ( $mh, $curl_handle );
// we will need to access the indvidual handles later
$handles [] = $curl_handle;
}
// execute the multi handle
$running = null;
do {
curl_multi_exec ( $mh, $running );
// it’s a good idea to reduce load, here it’s 1 micro second
usleep ( 10000 );
} while ( $running > 0 );
// get the content of the transaction, this will contain (in our case) things like the transaction id, credits remaining etc
for($i = 0; $i < count ( $handles ); $i ++) {
// get the content of the handle
$output [] = curl_multi_getcontent ( $handles [$i] );
// remove the handle from the multi handle
curl_multi_remove_handle ( $mh, $handles [$i] );
}
// close the multi curl handle to free system resources
curl_multi_close ( $mh );
return $output;
}
}
?>