Skip to content

Instantly share code, notes, and snippets.

@jacmkno
Last active February 25, 2023 08:42
Show Gist options
  • Save jacmkno/dc13c089c3d641a2c3d48f7cde5c377b to your computer and use it in GitHub Desktop.
Save jacmkno/dc13c089c3d641a2c3d48f7cde5c377b to your computer and use it in GitHub Desktop.
Promises in PHP with Asyncronous Bash Execution
<?php
namespace Promises;
class BashPromise extends Promise {
public $outputTmpFile;
public $inputTmpFile;
function __construct($process, $inputData=NULL, $inputAsStdIn = FALSE){
$inputExtra = '';
if($inputData){
$this->inputTmpFile = tempnam("/tmp", "PHPBashPromiseIn");
file_put_contents($this->inputTmpFile, $inputData);
if($inputAsStdIn){
$inputExtra = " <<< \"$(cat $this->inputTmpFile)\"";
}else{
$inputExtra = " $this->inputTmpFile";
}
}
$this->outputTmpFile = tempnam("/tmp", "PHPBashPromiseOut");
unlink($this->outputTmpFile);
shell_exec('/bin/bash -c \'a=$('.$process.$inputExtra.'); echo "${a}" > '.$this->outputTmpFile.'\' > /dev/null 2>&1 &');
}
function next(){
if($this->outputTmpFile){
if(file_exists($this->outputTmpFile)){
$this->result = file_get_contents($this->outputTmpFile);
unlink($this->outputTmpFile);
$this->outputTmpFile = NULL;
if($this->inputTmpFile) unlink($this->inputTmpFile);
}else{
return [$this];
}
}
return parent::next();
}
}
<?php
namespace Promises;
class Promise {
public $callbacks = [];
public $result = NULL;
public $queue = [];
public $promiseGroupCnt = 0;
public $promiseGroup = NULL;
function __construct($process /* function || Array<Promise> */){
if(is_array($process)){
$this->result = [];
$this->promiseGroup = $process;
$this->promiseGroupCnt = count($process);
array_map(function($promise){
$idx = count($this->result);
$this->result[] = NULL;
$promise->then(function($result) use ($idx) {
$this->result[$idx] = $result;
$this->promiseGroupCnt -= 1;
});
}, $process);
}else{
$this->callbacks[] = $process;
}
}
public function then($callback){
$this->callbacks[] = $callback;
return $this;
}
public static function all($promises){
return new Promise($promises);
}
public function next(){
$nxts = [];
if(is_a($this->result, Promise::class)) {
$nxt = $this->result->next();
if(count($nxt)){
$nxts = array_merge($nxts, $nxt);
}else{
$this->result = $this->result->result;
}
}else{
if($this->promiseGroup !== NULL && $this->promiseGroupCnt){
$nxts = array_merge($nxts, $this->promiseGroup);
$this->promiseGroup = [];
}else{
$callback = array_shift($this->callbacks);
if (is_callable($callback)) {
$result = $callback($this->result);
if($result !== NULL){
$this->result = $result;
}
}
}
}
if($this->pending()){
$nxts[] = $this;
}
return $nxts;
}
public function pending(){
return $this->promiseGroupCnt || count($this->callbacks) || is_a($this->result, Promise::class) || count($this->queue);
}
public function await(){
$this->queue = [$this];
while($p = array_shift($this->queue)){
$nxts = $p->next();
$this->queue = array_merge($this->queue, $nxts);
usleep(10*1000000/1000);
}
return $this->result;
}
function toString($direct=FALSE){
if($direct){
return json_encode($this, JSON_PRETTY_PRINT)."\n\n";
}
$data = [
'callbacks' => count($this->callbacks),
'result' => json_encode($this->result),
'queue' => count($this->queue),
'promiseGroupCnt' => $this->promiseGroupCnt,
'promiseGroup' => gettype($this->promiseGroup),
];
return json_encode($data, JSON_PRETTY_PRINT)."\n\n";
}
}
<?php
namespace Example;
use Promises\Promise;
use Promises\BashPromise;
$a = (new BashPromise('sleep 3; head -n 2', json_encode(['xxx'=>[1,2,34]], JSON_PRETTY_PRINT), TRUE))->then(function($out){
return [strlen($out), '"'.$out.'"'];
});
$b = new BashPromise('sleep 4; echo 2');
$c = new BashPromise('sleep 4; echo 3');
$d = new Promise(function(){
return Promise::all([
Promise::all([]),
new Promise(function(){return 4;}),
(new BashPromise('sleep 4; ls -la'))->then(function($out){
return [strlen($out), substr($out, 0, 20)];
}),
new BashPromise('sleep 4; echo 6')
]);
});
$e = Promise::all([$a, $b, $c, $d]);
$f = $e->await();
print_r($f);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment