Assuming you want to use HTTP requests, you have a few options, set a timeout, each time less:
function doTaskWithEnd($uri, $end, $ctx = null) {
if (!$ctx) { $ctx = stream_context_create(); }
stream_context_set_option($ctx, "http", "timeout", $end - time());
$ret = file_get_contents($uri, false, $ctx));
if ($ret === false) {
throw new \Exception("Request failed or timed out!");
}
return $ret;
}
$end = time() + 100;
$fetched = doTaskWithEnd("http://example.com/some/module/fetch", $end);
$ctx = stream_context_create(["http" => ["method" => "POST", "content" => $fetched]]);
$parsed = doTaskWithEnd("http://example.com/some/module/parsed", $end, $ctx);
$ctx = stream_context_create(["http" => ["method" => "PUT", "content" => $parsed]]);
doTaskWithEnd("http://example.com/some/module/save", $end, $ctx);
Or alternatively, with an non-blocking solution (let's use amphp/amp + amphp/artax for this):
function doTaskWithTimeout($requestPromise, $timeout) {
$ret = yield \Amp\first($requestPromise, $timeout);
if ($ret === null) {
throw new \Exception("Timed out!");
}
return $ret;
}
\Amp\execute(function() {
$end = new \Amp\Pause(100000); /* timeout in ms */
$client = new \Amp\Artax\Client;
$fetched = yield from doTaskWithTimeout($client->request("http://example.com/some/module/fetch"));
$req = (new \Amp\Artax\Request)
->setUri("http://example.com/some/module/parsed")
->setMethod("POST")
->setBody($fetched)
;
$parsed = yield from doTaskWithTimeout($client->request($req), $end);
$req = (new \Amp\Artax\Request)
->setUri("http://example.com/some/module/save")
->setMethod("PUT")
->setBody($parsed)
;
yield from doTaskWithTimeout($client->request($req), $end);
});
Now, I ask, do you really want to offload to separate requests? Can't we just assume there are now functions fetch(), parse($fetched) and save($parsed)?
In this case it's easy and we just may set up an alarm:
declare(ticks=10); // this declare() line must happen before the first include/require
pcntl_signal(\SIGALRM, function() {
throw new \Exception("Timed out!");
});
pcntl_alarm(100);
$fetched = fetch();
$parsed = parse($fetched);
save($parsed);
pcntl_alarm(0); // we're done, reset the alarm
Alternatively, the non-blocking solution works too (assuming fetch(), parse($fetched) and save($parsed) properly return Promises and are designed non-blockingly):
\Amp\execute(function() {
$end = new \Amp\Pause(100000); /* timeout in ms */
$fetched = yield from doTaskWithTimeout(fetch(), $end);
$parsed = yield from doTaskWithTimeout(parse($fetched), $end);
yield from doTaskWithTimeout(save($parsed), $end);
});
If you just want to have a global timeout for different sequential tasks, I'd preferably go with doing it just all in one script with a pcntl_alarm(), alternatively go with the stream context timeout option.
The non-blocking solutions are mainly applicable if you happen to need to do other things at the same time. E.g. if you want to do that fetch+parse+save cycle multiple times, independently from each other cycle.