.gitignore000064400000000036144761607200006542 0ustar00/vendor/ /tests/ composer.lockREADME.md000064400000004333144761607200006035 0ustar00## Table of contents * [General info](#general-info) * [Installing](#installing) * [Utilities Included](#utilities-included) * * [dhGlobal](#dhglobal) * * [dhOut](#dhout) * * [dhLogger](#dhlogger) ## General info dhUtils is a collection of basic utilities to make development easier. ## Installing In composer.json, add the following: ``` { "require": { "boru/dhutils": "dev-master" }, "repositories": [ { "type": "composer", "url": "https://satis.boruapps.com" } ] } ``` then run ``` composer install ``` ## Utilities Included This example sets up an instance of dhOut it then adds it to dhGlobals. Then it uses the global ``` //standard composer incldue: require_once "vendor/autoload.php"; //use statements to shortcut class names -- not required but is handy. //instead, you could use the full qualified class name //eg, instead of dhGlobal it would be \boru\dhutils\dhGlobal (or dhOut, or dhLogger, etc) use boru\dhutils\{dhGlobal,dhOut,dhLogger}; //create an instance of dhOut $dhOut = new dhOut(); //add it to dhGlobal dhGlobal::add("out",$dhOut); //add another variable dhGlobal::add("foo","bar"); //in some other piece of code somewhere: dhGlobal::add("logger",new dhLogger("debug",false,$dhOut)); //And in another class/function: dhGlobal::get("out")->add("testing dhOut from another class"); dhGlobal::get("out")->add("global var 'foo'=" . dhGlobal::get("bar")); dhGlobal::get("logger")->debug("debug notice here"); ``` ### dhGlobal Easily transport objects and variables across functions/classes without having to inject or initiate in each location Methods: * setup(bool stdOut, bool prefix, bool outFile, bool logFile) * * Shortcut to set up dhGlobal::get("out") and dhGlobal::get("logger") * add(string KEY, mixed ITEM); * get(string KEY, mixed DEFAULT); * isset(string KEY); * remove(string KEY); ### dhOut ### dhLogger ### deleted tags 1.0.1 1.0.10 1.0.11 1.0.12 1.0.14 1.0.15 1.0.16 1.0.2 1.0.3 1.0.4 1.0.5 1.0.9 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.4.0 1.4.1 2.0.0 2.0.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.7.2 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.2.1 2.2.1.1 2.2.1.2 2.2.1.3composer.json000064400000001605144761607200007277 0ustar00{ "name": "boru/dhutils", "type": "library", "autoload": { "psr-4": { "boru\\dhutils\\": "src/" } }, "extra" : { "branch-alias" : {"dev-master" : "1.0.x-dev"} }, "authors": [ { "name": "Daniel Hayes", "email": "dhayes@boruapps.com" } ], "require": { "boru/dhcache": "*", "guzzlehttp/guzzle": "^6.5", "clue/mq-react": "^1.5", "react/http": "^1.8", "react/async": "^2.0", "react/child-process": "^0.6.5" }, "suggest": { "boru/dhdb": "dhDB library for database", "boru/dhcli": "CLI toolkit/library for making CLI apps", "boru/dhapi": "Toolkit/library for creating API apps" }, "repositories": [ { "type": "composer", "url": "https://satis.boruapps.com" } ] } examples/dhThreads/exec.php000064400000001655144761607200011743 0ustar00uniqId(),"rand"=>["start"=>$randStart,"end"=>$randEnd,"rand"=>$rand]]);examples/dhThreads_class.php000064400000005717144761607200012207 0ustar00true, "jsonObject"=>false, "callback"=>function($thread) { $simpleOut = dhGlobal::get("simpleOut"); $simpleOut->done($thread->id()); $simpleOut->display(); //dhGlobal::outLine($thread->id(),"Completed"); //dhGlobal::outLine($thread->id(),$thread->meta()); //dhGlobal::outLine($thread->id(),$thread->output()); } ]; /** * The script to execute.. note that this is just a sample script that will sleep for a random time * in order to show it is multi-threaded */ $script = new File(["path"=>"dhThreads/exec.php"]); //push $threads commands through our threadpool.. for($i=0;$i<$threads;$i++) { $randStart = rand(1,3); $randEnd = $randStart+3; $threadPool->startScript($script,["randStart"=>$randStart,"randEnd"=>$randEnd],$options); } echo "Now waiting..\n"; //wait for all threads to complete $threadPool->wait(); class simpleOutput { public $threads = []; public $done = 0; public $total = 0; public function __construct($threads=10) { $this->total = $threads; $this->done = 0; for($i=0;$i<$threads;$i++) { $this->threads[$i] = "."; } } public function display() { $percent = $this->done / $this->total; $percent *= 100; dhGlobal::outLine(implode(" ",$this->threads)." ".dhGlobal::pad(number_format($percent,2),7," ",STR_PAD_LEFT)); } public function done($i) { $this->done++; $this->threads[$i] = 'x'; } } examples/dhThreads_global.php000064400000005457144761607200012343 0ustar00true, "jsonObject"=>false, "callback"=>function($thread) { $simpleOut = dhGlobal::get("simpleOut"); $simpleOut->done($thread->id()); $simpleOut->display(); //dhGlobal::outLine($thread->id(),$thread->command()); //dhGlobal::outLine($thread->id(),$thread->meta()); //dhGlobal::outLine($thread->id(),$thread->output()); }, "collectType"=>dhThreads::COLLECT_FULL, ]; /** * Build an array of commands to execute */ $commands = []; for($i=0;$i<$threads;$i++) { $randStart = rand(1,3); $randEnd = $randStart+3; $commands[] = [ "exec"=>"php -f ".__DIR__."/dhThreads/exec.php inline $randStart $randEnd", "meta"=>["randomData"=>"toInclude","someval"=>$i,"randStart"=>$randStart,"randEnd"=>$randEnd], ]; } $output = dhGlobal::threadMultiExec($commands,$options,$maxThreads,$throttleDelay); /** * global methods to wrap all of dhThreads_class example using just dhGlobal */ //Using threadMultiExec //dhGlobal::threadMultiExec($commands=[],$options=[],$numThreads=4,$throttleDelay=0.1) class simpleOutput { public $threads = []; public function __construct($threads=10) { for($i=0;$i<$threads;$i++) { $this->threads[$i] = "."; } } public function display() { dhGlobal::outLine(implode(" ",$this->threads)); } public function done($i) { $this->threads[$i] = 'x'; } } examples/general.php000064400000000422144761607200010515 0ustar00encoding = $encoding; } public function isValid() { return !empty($this->data); } public function isError() { return !empty($this->error); } public function toDisplay($len=150) { $packet = $this->toPacket(); if(strlen($packet)>$len) { $packet = substr($packet,0,$len-4)." ..."; } return $packet; } public function toPacket() { if(empty($this->data) && empty($this->error)) { return false; } $packet = json_encode(["workId"=>$this->workId,"message"=>$this->data,"error"=>$this->error]); $packetData = static::encode($packet,$this->encoding); $packet = static::$startDelimiter; $packet .= $this->encoding; $packet .= static::$typeDelimiter; $packet .= $packetData; $packet .= static::$endDelimiter; return $packet; } public static function fromPacket($rawPacket) { $instance = new self(); $delimStart = static::$startDelimiter; $delimEnd = static::$endDelimiter; $delimType = static::$typeDelimiter; if(substr($rawPacket,0,strlen($delimStart)) == $delimStart && substr($rawPacket,-strlen($delimEnd)) == $delimEnd) { $rawPacket = substr($rawPacket,1); $rawPacket = substr($rawPacket,0,-1); $parts = explode($delimType,$rawPacket,2); if(count($parts) !== 2) { return false; } $instance->setEncoding($parts[0]); $instance->setRawPacket($parts[1]); $json = json_decode($parts[1],true); if(is_array($json)) { $instance->setWorkId(dhGlobal::getVal($json,"workId",null)); $instance->setData(dhGlobal::getVal($json,"message",null)); $instance->setError(dhGlobal::getVal($json,"error",null)); } return $instance; } return false; } public static function encode($packet,$type=Message::ENCODING_BASE64) { if($type == Message::ENCODING_BASE64) { return base64_encode($packet); } return $packet; } public static function decode($packet,$type=Message::ENCODING_BASE64) { if($type == Message::ENCODING_BASE64) { return base64_decode($packet); } return $packet; } /** * Get the value of encoding */ public function getEncoding() { return $this->encoding; } /** * Set the value of encoding * * @return self */ public function setEncoding($encoding) { $this->encoding = $encoding; return $this; } /** * Get the value of workId */ public function getWorkId() { return $this->workId; } /** * Set the value of workId * * @return self */ public function setWorkId($workId) { $this->workId = $workId; return $this; } /** * Get the value of rawPacket */ public function getRawPacket() { return $this->rawPacket; } /** * Set the value of rawPacket * * @return self */ public function setRawPacket($rawPacket) { $this->rawPacket = $rawPacket; return $this; } /** * Get the value of data */ public function getData() { return $this->data; } /** * Set the value of data * * @return self */ public function setData($data) { $this->data = $data; return $this; } /** * Get the value of error * * @return mixed */ public function getError() { return $this->error; } /** * Set the value of error * * @param mixed $error * @return self */ public function setError($error) { $this->error = $error; return $this; } public function data($dotKey=null,$default=null) { if(is_null($dotKey)) { return $this->getData(); } if(is_null($this->data) || empty($this->data)) { return $default; } return dhGlobal::getVal($this->data,$dotKey,$default); } public function error($dotKey=null,$default=null) { if(is_null($dotKey)) { return $this->getError(); } if(is_null($this->error) || empty($this->error)) { return $default; } return dhGlobal::getVal($this->error,$dotKey,$default); } /** * Get a dhObject representation of the data result * @return dhObject|false */ public function getDataObject() { if(!is_null($this->data)) { return new Result($this->data,$this->workId,false); } return false; } /** * Get a dhObject representation of the error result * @return dhObject|false */ public function getErrorObject() { if(!is_null($this->error)) { return new Result($this->error,$this->workId,true); } return false; } }src/async/Queue.php000064400000054676144761607200010276 0ustar00max = !is_null($max) ? $max : dhGlobal::get("asyncQueue.maxPerQueue",3); $workerBootstrap = !is_null($workerBootstrap) ? $workerBootstrap : dhGlobal::get("asyncQueue.defaultBootstrap",null); $this->retriesOnError = !is_null($retriesOnError) ? $retriesOnError : dhGlobal::get("asyncQueue.retriesOnError",0); $this->visualize = !is_null($visualize) ? $visualize : dhGlobal::get("asyncQueue.visualize",false); $this->log = !is_null($log) ? $log : dhGlobal::get("asyncQueue.log",false); $this->deferred = new Deferred(); if($this->visualize) { $this->progress = new QueueStatus("Progress"); $this->promise()->then(function() { $this->updateProgress(); $this->progress->stop(); }); StdOut::progressBar($this->progress); } $this->workerManager = new WorkerManager($this,$this->max); $this->workerManager->setWorkerBootstrap($workerBootstrap); $this->workerManager->setLog($this->log); } public function __destruct() { if(!is_null($this->progress)) { $this->progress->stop(); } } public function updateInterval($interval=0.5) { if($this->visualize && !is_null($this->progress)) { $this->progress->updateInterval($interval); } return $this; } public function enableExtendedBar($inline=false) { $this->progress->showExec(true); $this->progress->setThreads($this->max); return $this; } /** * Add work to the Queue * @param Work $work * @return Work * @throws UnexpectedValueException * @throws RuntimeException * @throws Exception */ public function queue(Work $work,$groupIdentifier=null) { if(is_null($this->deferred)) { $this->deferred = new Deferred(); } $this->work[$work->getId()] = $work; if(!is_null($groupIdentifier)) { $this->addToWorkGroup($work,$groupIdentifier); } $this->moveToQueued($work); $this->workerManager()->checkWorkers(true); $this->checkMaxQueued(); return $work; } /** * Creates a new workGroup and returns a promise. Once all work within the workGroup is completed, the promise will resolve with the groupIdentifier * * The workGroup Identifier can be used in Queue->getWorkGroup($groupIdentifier) * @param mixed $groupIdentifier * @return WorkGroup */ public function createWorkGroup($groupIdentifier) { $this->workGroups[$groupIdentifier] = new WorkGroup($groupIdentifier); return $this->workGroups[$groupIdentifier]->promise(); } public function workGroupPromise($groupIdentifier) { return $this->workGroups[$groupIdentifier]->promise(); } private function addToWorkGroup(Work &$work,$groupIdentifier) { if(!isset($this->workGroups[$groupIdentifier])) { $this->createWorkGroup($groupIdentifier); } $this->workGroups[$groupIdentifier]->addWork($work); } public function reset() { $this->deferred=null; $this->queued=[]; $this->active=[]; $this->done=[]; $this->work=[]; return $this; } /** * Get the next queued item, or false if nothing is queued * @return Work|false */ public function next() { if(!empty($this->queued)) { $work = array_shift($this->queued); $this->moveToActive($work); return $work; } return false; } public function promise() { if(is_null($this->deferred)) { $this->deferred = new Deferred(); $this->deferred->resolve([]); } return $this->deferred->promise(); } public function wait() { $this->log("wait called"); $this->isWaiting=true; $this->isDone=false; $this->promise()->then(function() use (&$isDone) { $this->isDone=true; }); $progressTimer = Loop::addPeriodicTimer(0.5,function($t) { $this->updateProgress(); }); Loop::addPeriodicTimer(0.1,function($timer) use ($progressTimer) { if($this->isDone) { Loop::cancelTimer($timer); if(!is_null($progressTimer)) { Loop::cancelTimer($progressTimer); } Loop::stop(); } else { $this->workerManager()->checkWorkers(); } }); while(!$this->isDone) { Loop::run(); } //\React\Async\await($this->promise()); return $this; } public function collect($groupIdentifier=null) { $this->wait(); if(!is_null($groupIdentifier)) { return isset($this->workGroups[$groupIdentifier]) ? $this->workGroups[$groupIdentifier] : false; } return $this->work; } public function getWorkGroup($groupIdentifier=null) { return isset($this->workGroups[$groupIdentifier]) ? $this->workGroups[$groupIdentifier] : false; } private function checkMaxQueued() { if($this->maxQueued !== false && $this->maxQueued>0 && count($this->queued) >= $this->maxQueued) { Loop::addPeriodicTimer(0.1,function($timer) { if($this->maxQueued !== false && $this->maxQueued>0 && count($this->queued) >= $this->maxQueued) { } else { Loop::stop(); } }); Loop::run(); } } public function workerManager() { return $this->workerManager; } public function setWorkerLogDir($logDir) { $this->workerManager->setLogDir($logDir); } /** @return bool true if queued array is not empty, false if empty */ public function hasQueued(){ return !empty($this->queued); } /** @return bool true if the active array is not empty, false if empty */ public function hasActive(){ return !empty($this->active); } /** @return bool true if the done array is not empty, false if empty */ public function hasDone(){ return !empty($this->done); } /** * Retrieve the work record by ID * @param string $workId * @return Work|false */ public function getWork($workId) { return isset($this->work[$workId]) ? $this->work[$workId] : false; } /** * Retrieve full work array * @return Work[]|false */ public function getAllWork() { return !empty($this->work) ? $this->work : false; } /** @return bool true if $work is in the active state, false otherwise */ public function workIsActive(Work $work) { return $this->getActive($work->getId()) !== false ? true : false; } /** @return bool true if $work is in the queued state, false otherwise */ public function workIsQueued(Work $work) { return $this->getActive($work->getId()) !== false ? true : false; } /** @return bool true if $work is in the done state, false otherwise */ public function workIsDone(Work $work) { return $this->getDone($work->getId()) !== false ? true : false; } /** * Get the ueue, or get a Work from the queue by ID * @param string|null $workId * @return Work[]|false|Work */ public function getQueued($workId=null) { if(!is_null($workId)) { return dhGlobal::getVal($this->queued,$workId,false); } return $this->queued; } /** * Get the active queue, or get a Work from the queue by ID * @param string|null $workId * @return Work[]|false|Work */ public function getActive($workId=null) { if(!is_null($workId)) { return dhGlobal::getVal($this->active,$workId,false); } return $this->active; } /** * Get the done queue, or get a Work from the queue by ID * @param string|null $workId * @return Work[]|false|Work */ public function getDone($workId=null) { if(!is_null($workId)) { return dhGlobal::getVal($this->done,$workId,false); } return $this->active; } /** * Set the value of visualize * @param mixed $visualize * @return self */ public function setVisualize($visualize) { $this->visualize = $visualize; return $this; } /** * Get the value of workerErrorLimit * @return mixed */ public function getWorkerErrorLimit() { return $this->workerErrorLimit; } /** * Set the value of workerErrorLimit * @param mixed $workerErrorLimit * @return self */ public function setWorkerErrorLimit($workerErrorLimit) { $this->workerErrorLimit = $workerErrorLimit; $this->workerManager->setWorkerErrorLimit($this->workerErrorLimit); return $this; } /** * Get the value of retriesOnError * @return mixed */ public function getRetriesOnError() { return $this->retriesOnError; } /** * Set the value of retriesOnError * @param mixed $retriesOnError * @return self */ public function setRetriesOnError($retriesOnError) { $this->retriesOnError = $retriesOnError; return $this; } /** * Get the value of maxQueued * @return mixed */ public function getMaxQueued() { return $this->maxQueued; } /** * Set the value of maxQueued * @param mixed $maxQueued * @return self */ public function setMaxQueued($maxQueued) { $this->maxQueued = $maxQueued; return $this; } /** * Get the value of expected * @return int */ public function getExpected() { return $this->expected; } /** * Set the value of expected * @param int $expected * @return self */ public function setExpected($expected) { $this->expected = $expected; return $this; } /** * Get the value of maxWorkerWork * @return int */ public function getMaxWorkPerWorker() { return $this->workerManager->getMaxWorkPerWorker(); } /** * Set the value of maxWorkerWork * @param int $maxWorkerWork * @return self */ public function setMaxWorkPerWorker($maxWorkerWork) { $this->workerManager->setMaxWorkPerWorker($maxWorkerWork); return $this; } /** * Get the value of killOnError * @return mixed */ public function getKillOnError() { return $this->workerManager->getKillOnError(); } /** * Set the value of killOnError * @param mixed $killOnError * @return self */ public function setKillOnError($killOnError) { $this->workerManager->setKillOnError($killOnError); return $this; } /** * Get after this number of seconds, the worker will be closed, 0 to disable * @return int */ public function getWorkerTimeout() { return $this->workerManager->getWorkerTimeout(); } /** * Set after this number of seconds, the worker will be closed, 0 to disable * @param int $workerTimeout after this number of seconds, the worker will be closed, 0 to disable * @return self */ public function setWorkerTimeout($workerTimeout) { $this->workerManager->setWorkerTimeout($workerTimeout); return $this; } /** * Set the value of bootstrapAsCallable * @param mixed $bootstrapAsCallable * @return self */ public function setBootstrapAsCallable($bootstrapAsCallable) { $this->workerManager->setBootstrapAsCallable($bootstrapAsCallable); return $this; } /** * Get the value of bootstrapAsCallable * @return bool */ public function getBootstrapAsCallable() { return $this->workerManager->getBootstrapAsCallable(); } /** * * @param false|string $log * @return self */ public function setLog($log) { $this->log = $log; $this->workerManager->setLog($log); return $this; } public function updateProgress() { if(!$this->visualize || $this->workerManager->getProcessCount()<=0) { return; } $this->progress->update(count($this->done),count($this->active),count($this->queued),$this->expected); if(!is_null($this->lastActivity) && $this->maxIdleTime>0 && microtime(true)-$this->lastActivity>=$this->maxIdleTime) { $time = round(microtime(true)-$this->lastActivity); $dtF = new \DateTime('@0'); $dtT = new \DateTime("@$time"); $str = dhGlobal::dateIntervalToElapsed($dtF->diff($dtT),true,false,2,""); dhGlobal::outLine("it has been",$str,"since anything happened.. closing"); if($this->isWaiting) { $this->isDone=true; } else { exit(); } } } public function updateWorkerTime($pid) { if(!$this->visualize || $this->workerManager->getProcessCount()<=0) { return; } if(($k = $this->workerManager()->getWorkerMap($pid)) !== false) { $this->progress->updateThread($k,"time",microtime(true)); } } public function updateWorkerStatus($pid,$status) { if(!$this->visualize || $this->workerManager->getProcessCount()<=0) { return; } if(($k = $this->workerManager()->getWorkerMap($pid)) !== false) { $this->progress->updateThread($k,"display",$status); $this->progress->updateThread($k,"time",microtime(true)); } } public function moveToQueued(Work $work) { $work->onWorkQueued(); return $this->moveTo($work,$this->queued); } public function moveToActive(Work $work) { $work->onWorkStart(); return $this->moveTo($work,$this->active); } public function moveToDone(Work $work) { return $this->moveTo($work,$this->done); } public function workOnDone(Work $work,$message=null) { $this->logWork($work,"workOnDone"); $this->moveToDone($work); if(!is_null($message)) { $work->done($message->getDataObject()); } else { $work->done(new Result([],$work->getId(),false)); } } public function workOnError(Work $work,$message=null,$workerClosed=false) { if($workerClosed) { $this->logWork($work,"workOnError","worker closed..."); if($this->retriesOnError>0) { dhGlobal::debug("Work stopped due to worker close, handling with retry process"); $this->handleWorkOnError($work,$message); } else { $this->moveToQueued($work); dhGlobal::debug("Work re-queued due to worker close"); } } else { $this->handleWorkOnError($work,$message); } } private function handleWorkOnError(Work $work,$message=null) { $this->logWork($work,"handleWorkOnError",!is_null($message) ? $message : ""); $retryCount = isset($this->retryCounts[$work->getId()]) ? $this->retryCounts[$work->getId()] : 0; $retryCount++; if($this->retriesOnError>0 && $retryCount<=$this->retriesOnError) { dhGlobal::trace("[retry]","retry count for",$work->getId(),"is",$retryCount); $this->logWork($work,"RETRY #".$retryCount); $this->retryCounts[$work->getId()] = $retryCount; $this->moveToQueued($work); } else { $this->moveToDone($work); if(!is_null($message)) { $work->error($message->getErrorObject()); } else { $work->error(new Result([],$work->getId(),true)); } } } private function moveTo(Work $work,&$destinationArray) { $this->lastActivity = microtime(true); if(!isset($this->work[$work->getId()])) { throw new \Exception("Work not found in work array"); } unset($this->queued[$work->getId()]); unset($this->active[$work->getId()]); unset($this->done[$work->getId()]); $destinationArray[$work->getId()] = &$this->work[$work->getId()]; if(!$this->hasActive() && !$this->hasQueued()) { if(!is_null($this->deferred)) { //we need to resolve the QueuePromise after the final 'work' promise, otherwise we will resolve the QueuePromise before the WorkPromise is processed. $work->then(function() { $this->deferred->resolve($this->done); },function() { $this->deferred->resolve($this->done); }); } } $this->updateProgress(); return $work; } private function log(...$args) { array_unshift($args,"[QUEUE ]"); $this->_log(...$args); } private function logWorker(WorkerProcess $process,...$args) { array_unshift($args,"[WORKER]",":".$process->getId().":"); $this->_log(...$args); } private function logWork(Work $work,...$args) { array_unshift($args,"[ WORK ]",":".$work->getId().":"); $this->_log(...$args); } private function _log(...$args) { if($this->log !== false) { if(is_null($this->logger)) { $this->logger = new dhOut(false,$this->log); } $this->logger->line(...$args); } } private function logDisplay($var,$len=150) { if(is_array($var)) { $var = json_encode($var); } if(!is_array($var) && !is_object($var)) { if(strlen($var)>$len) { $packet = substr($var,0,$len-4)." ..."; } } return $var; } public static function callableWork($callable,...$args) { $work = new Work($callable); $work->args($args); $work->asJson(false); return $work; } public static function basicDisplay(Work $work,$identifier,...$args) { if(empty($args)) { $args = $work->args(); } if(empty($args)) { $args = []; } else { if(!is_array($args)) { $args = [$args]; } } $work->onStart(function() use ($identifier,$args) { Queue::display("start",$identifier,...$args); }); $work->then(function(Result $result) use ($identifier) { Queue::display("complete",$identifier,$result->result()); },function(Result $result) use ($identifier) { $resData = $result->result(); $errData = $result->error(); if(!empty($errData)) { Queue::display("error",$identifier,$errData); } else { if(empty($resData)) { $resData = "-empty-"; } Queue::display("error",$identifier,$resData); } }); return $work; } public static function basicDisplayOnDone(Work $work,$identifier,...$args) { if(empty($args)) { $args = $work->args(); } if(empty($args)) { $args = []; } else { if(!is_array($args)) { $args = [$args]; } } $work->then(function(Result $result) use ($identifier) { Queue::display("complete",$identifier,$result->result()); },function(Result $result) use ($identifier) { $resData = $result->result(); $errData = $result->error(); if(!empty($errData)) { Queue::display("error",$identifier,$errData); } else { if(empty($resData)) { $resData = "-empty-"; } Queue::display("error",$identifier,$resData); } }); return $work; } public static function display($action,$identifier,...$args) { $displayLen = stdOut::getCols(60); if($displayLen<20) { $displayLen=20; } $prefix = $suffix = ""; if($action == "start") { $prefix = "> --- started :"; } elseif($action == "error") { $prefix = "> !!! failure :"; } elseif($action == "complete") { $prefix = "> *** complete :"; } $argString = ""; if(!empty($args)) { foreach($args as $k=>$arg) { $args[$k] = StdOut::argToString($arg,true); } $argString = implode(" ",$args); if(strlen($argString) >= $displayLen) { $argString=substr($argString,0,$displayLen-4)." ..."; } } dhGlobal::outLine($prefix,$identifier,$argString); } /** * Get the value of maxIdleTime * @return mixed */ public function getMaxIdleTime() { return $this->maxIdleTime; } /** * Set the value of maxIdleTime * @param mixed $maxIdleTime * @return self */ public function setMaxIdleTime($maxIdleTime) { $this->maxIdleTime = $maxIdleTime; return $this; } }src/async/Result.php000064400000002510144761607200010444 0ustar00workId = $workId; $this->isError = $isError; } public function isError() { return $this->isError; } public function id() { return $this->workId; } public function result($dotKey=null,$default=null) { if(is_null($dotKey)) { return $this->get("result",$default); } $newKey = "result.".$dotKey; return $this->get($newKey,$default); } public function error($dotKey=null,$default=null) { if(is_null($dotKey)) { return $this->get("error",$default); } $newKey = "error.".$dotKey; return $this->get($newKey,$default); } /** * Get the value of workId * @return mixed */ public function getWorkId() { return $this->workId; } /** * Set the value of workId * @param mixed $workId * @return self */ public function setWorkId($workId) { $this->workId = $workId; return $this; } }src/async/Work.php000064400000026105144761607200010116 0ustar00id = uniqid(); $this->callable = $serializeableCallable; $this->args = dhGlobal::getVal($options, "args", null); $this->asJson = dhGlobal::getVal($options, "asJson", true); $this->onData = dhGlobal::getVal($options, "onData", null); $this->onError = dhGlobal::getVal($options, "onError", null); $this->onStart = dhGlobal::getVal($options, "onStart", null); $this->onDone = dhGlobal::getVal($options, "onDone", null); $this->bootstrap = dhGlobal::getVal($options, "bootstrap",null); $this->queue = dhGlobal::getVal($options, "queue", null); $this->deferred = dhGlobal::getVal($options, "deferred", null); $this->metaData = dhGlobal::getVal($options, "metaData", null); if(is_null($this->deferred)) { $this->deferred = new Deferred(); } } public function start() { return $this->run(); } public function getId() { return $this->id; } public function getPriority() { return $this->priority; } public function getResult() { return $this->result; } public function getError() { return $this->error; } public function isDone() { return $this->done; } public function isRunning() { return $this->running; } public function isReady() { return $this->ready; } public function hasSuccess() { return $this->success; } public function isSuccess() { return $this->success; } /** * @param mixed|null $args * @return mixed */ public function args($args=null) { if(is_null($args)) { return $this->args; } $this->args = $args; return $this; } /** * @param bool|null $asJson * @return bool */ public function asJson($asJson=null) { if(is_null($asJson)) { return $this->asJson; } if($asJson) { $asJson = true; } else { $asJson = false; } $this->asJson = $asJson; return $this; } public function done($result) { $this->success=true; $this->onWorkDone($result); } public function error($data=null) { $this->onWorkError($data); } public function terminate() { } public function getWork() { $packet = []; $packet["id"] = $this->id; $packet["callable"] = $this->callable; $packet["args"] = $this->args; $packet["asJson"] = $this->asJson; return $packet; } /** * Wrapper for \React\Promise\Promise::then() * @param callable|null $onFulfilled * @param callable|null $onRejected * @return \React\Promise\PromiseInterface */ public function then(callable $onFulfilled=null,callable $onRejected=null) { return $this->Promise()->then($onFulfilled,$onRejected); } /** * Making it easy to run work without having to queue.. creates a new queue for each run() call. * * Options * * bootstrap - the bootstrap definition to use with the newly created queue.. ommitted if using existing queue * * queue - optional instance of \boru\dhutils\async\Queue, instead of creating a new queue, re-uses an existing one. * @param array $options * @return $this */ public function run($options=[]) { $this->bootstrap = dhGlobal::getVal($options,"bootstrap",$this->bootstrap); $queue = dhGlobal::getVal($options,"queue",$this->queue); if(is_null($queue)) { $queue = dhGlobal::getAsyncQueue($this->bootstrap); } $queue->queue($this); return $this; } /** Handler Methods */ public function onWorkDone($result=null) { $this->result = $result; $this->done = true; $this->running = false; $this->ready = false; $this->deferred->resolve($this->result); if(!is_null($this->onDone)) { $handler = $this->onDone; $handler($this,$this->result); } } public function onWorkError($chunk=null) { if(!is_null($this->onError)) { $handler = $this->onError; $handler($this,$chunk); } $this->error = $chunk; $this->deferred->reject($chunk); } public function onWorkData($chunk=null) { if(!is_null($this->onData)) { $handler = $this->onData; $handler($this,$chunk); } } public function onWorkStart() { $this->running = true; $this->ready = false; if(!is_null($this->onStart)) { $handler = $this->onStart; $handler($this); } } public function onWorkQueued() { $this->running = true; $this->ready = false; if(!is_null($this->onQueued)) { $handler = $this->onQueued; $handler($this); } } /** * Get the value of running * @return bool */ public function getRunning() { return $this->running; } /** * Set the value of running * @param bool $running * @return self */ public function setRunning($running) { $this->running = $running; return $this; } /** * Get the value of done * @return bool */ public function getDone() { return $this->done; } /** Set the value of done * @param bool $done * @return self */ public function setDone($done) { $this->done = $done; return $this; } /** * Set the Deferred that will be resolved on completion * @param Deferred $deferred * @return $this */ public function setDeferred(Deferred $deferred) { $this->deferred = $deferred; return $this; } /** * Get the value of deferred * * @return mixed */ public function getDeferred() { return $this->deferred; } /** @return \React\Promise\Promise */ public function Promise() { return $this->deferred->promise(); } /** @return \boru\dhutils\multithread\process\Process */ public function Process() { return $this->process; } /** * Get the value of queue * @return mixed */ public function getQueue() { return $this->queue; } /** * Set the value of queue * @param mixed $queue * @return self */ public function setQueue($queue) { $this->queue = $queue; return $this; } /** * Get the value of bootstrap * @return mixed */ public function getBootstrap() { return $this->bootstrap; } /** * Set the value of bootstrap * @param mixed $bootstrap * @return self */ public function setBootstrap($bootstrap) { $this->bootstrap = $bootstrap; return $this; } public function onStart(callable $callback) { $this->onStart = $callback; return $this; } public function onDone(callable $callback) { $this->onDone = $callback; return $this; } public function onData(callable $callback) { $this->onData = $callback; return $this; } public function onError(callable $callback) { $this->onError = $callback; return $this; } public function onQueued(callable $callback) { $this->onQueued = $callback; return $this; } public function setMetaData($key,$val="",$append=false) { if(strpos($key,".") !== false) { if($append) { $check = dhGlobal::getDot($this->metaData,$key); if(!is_null($check)) { if(is_array($check)) { $check[] = $val; $val = $check; } else { $narr = []; $narr[] = $check; $narr[] = $val; $val = $narr; } } } dhGlobal::dotAssign($this->metaData,$key,$val); } else { if(isset($this->metaData[$key]) && $append) { if(is_array($this->metaData[$key])) { $this->metaData[$key][] = $val; } else { $temp = $this->metaData[$key]; $array[$key] = []; $array[$key][] = $temp; $array[$key][] = $val; } } else { $this->metaData[$key] = $val; } } return $this; } public function getMetaData($key=null,$default=null,$exists=false) { if(is_null($key)) { if($exists) { return !empty($this->metaData) ? true : false; } else { return !empty($this->metaData) ? $this->metaData : $default; } } if(strpos($key,".") !== false) { $uniqueid = uniqid("getArray",true); if(($check = dhGlobal::getDot($this->metaData,$key,$uniqueid)) !== $uniqueid) { return $exists ? true : $check; }; } if($exists) { return isset($this->metaData[$key]); } else { return isset($this->metaData[$key]) ? $this->metaData[$key] : $default; } } public function metaDataExists($key) { return $this->getMetaData($key,null,true); } }src/async/WorkGroup.php000064400000002657144761607200011141 0ustar00name = $name; $this->deferred = new Deferred(); } public function deferred() { return $this->deferred; } public function promise() { return $this->deferred->promise(); } public function addWork(Work &$work) { $workId = $work->getId(); $this->work[$workId] = &$work; $this->pending[$workId] = null; $work->then(function() use ($workId) { $this->workDone($workId); },function() use ($workId) { $this->workDone($workId); }); } public function workDone($workId) { unset($this->pending[$workId]); if(empty($this->pending)) { $this->deferred->resolve($this->name); } } public function getWork() { return $this->work; } public function getPending() { return $this->pending; } public function getName() { return $this->name; } public function then($success=null,$error=null) { $this->promise()->then($success,$error); return $this; } }src/async/WorkTemplate.php000064400000000105144761607200011602 0ustar00queue = $queue; $this->numWorkers = $numWorkers; } public function __destruct() { if(!empty($this->processes)) { foreach($this->processes as $process) { $process->stop(); } } } public function checkWorkers($launchIfNeeded=true) { foreach($this->processes as $pid=>$process) { if(!$process->isRunning() || $process->isDone()) { $this->removeWorker($pid); } else { $this->checkWorkerTimeout($process); } } if($launchIfNeeded) { $tempMax = min($this->numWorkers,count($this->queue->getQueued())); while(count($this->processes) < $tempMax) { $this->spawnWorker(); } } if(empty($this->processes) && !empty($this->queued)) { throw new \Exception("Workers all died and were not replaced\n"); } $this->queue->updateProgress(); } public function spawnWorker() { $workerOptions = []; if(!is_null($this->workerBootstrap) && $this->bootstrapAsCallable===false) { if(is_array($this->workerBootstrap)) { $bsString = implode(" ",$this->workerBootstrap); } else { $bsString = $this->workerBootstrap; } $workerOptions[]=$bsString; } if(!is_null($this->logDir)) { $workerOptions[] = "log:".$this->logDir; } $workerString = empty($workerOptions) ? "" : implode(" ",$workerOptions); $process = new WorkerProcess("php -f ".__DIR__."/worker/Worker.php $workerString",[],$this->getProcessMeta()); $process->setMetaData("workerTimeout",$this->workerTimeout); $this->processes[$process->getId()] = $process; $process->setMetaData("lastTime",microtime(true)); $process->start(); $this->addWorker($process); } /** * * @param WorkerProcess $process * @return mixed */ private function checkWorkerTimeout($process) { if(($lastTime = $process->getMetaData("lastTime",false)) !== false) { $pid = $process->getId(); $workerTimeoutLimit = $this->workerTimeout; if(isset($this->processWork[$pid]) && isset($this->work[$this->processWork[$pid]]) && ($work = $this->queue->getWork($this->processWork[$pid])) !== false) { $workerTimeoutLimit = $work->getMetaData("timeout",$this->workerTimeout); } $elapsed = microtime(true)-$lastTime; if(!is_null($workerTimeoutLimit) && $workerTimeoutLimit > 0 && $elapsed>=$workerTimeoutLimit) { $this->logWorker($process,"worker timed out after",number_format($elapsed,3),"seconds"); $this->processes[$pid]->stop(); return true; } } return false; } private function removeWorker($pid) { unset($this->processes[$pid]); if(($k = $this->getWorkerMap($pid)) !== false) { unset($this->processMap[$k]); } } private function addWorker($process) { for($i=0;$i<$this->numWorkers;$i++) { if(!isset($this->processMap[$i])) { $this->processMap[$i] = $process->getId(); return true; } } return false; } public function getWorkerMap($pid) { return array_search($pid,$this->processMap); } private function getProcessWorkCount(WorkerProcess $process,$inc=false) { if(!isset($this->processWorkCounts[$process->getId()])) { $this->processWorkCounts[$process->getId()]=0; } if($inc) { $this->processWorkCounts[$process->getId()]++; } return $this->processWorkCounts[$process->getId()]; } private function checkWorkerWorkCount(WorkerProcess $process,$inc=false) { if($this->maxWorkerWork>0 && $this->getProcessWorkCount($process)>=$this->maxWorkerWork) { return false; } return true; } private function getProcessMeta() { return [ "isMessageFrames"=>true, "useBuffer"=>false, "onStart"=>function(WorkerProcess $process) { dhGlobal::trace("[worker]",$process->getId(),":started:"); $data = $process->Buffer()->read(true,false); $this->processOnStart($process); }, "onDone"=>function(WorkerProcess $process,$exitCode=null,$termSignal=null) { dhGlobal::trace("[worker]",$process->getId(),":done:"); $this->processOnDone($process,$exitCode,$termSignal); }, "onData"=>function(WorkerProcess $process,$chunk=null) { $lines = explode("\n",$chunk); if(!empty($lines)) { foreach($lines as $line) { dhGlobal::trace("[worker]",$process->getId(),":data:",$line); $this->processOnData($process,$line); } } }, "onError"=>function(WorkerProcess $process,$chunk=null) { $lines = explode("\n",$chunk); if(!empty($lines)) { foreach($lines as $line) { dhGlobal::trace("[worker-error]",$process->getId(),":error:",$line); $this->processOnError($process,$line); } } else { dhGlobal::trace("[worker-error]",$process->getId(),":error:"); $this->processOnError($process); } }, ]; } private function processOnError(WorkerProcess $process,$chunk=null) { $this->logWorker($process,"<-- error:",$this->logDisplay($chunk)." "); if(!is_null($chunk)) { dhGlobal::error("[worker]",$process->getId(),$chunk); } else { dhGlobal::error("[worker]",$process->getId()); } } private function processOnStreamError(WorkerProcess $process,$stream="",$chunk=null) { } private function processOnStart(WorkerProcess $process) { $this->logWorker($process,"onStart"); $process->setMetaData("lastTime",microtime(true)); $this->queue->updateWorkerStatus($process->getId(),""); if(!is_null($this->workerBootstrap) && $this->bootstrapAsCallable===true) { $this->sendBootstrapPacket($process); } dhGlobal::trace("[worker]",$process->getId(),"worker process started"); } private function processOnDone(WorkerProcess $process,$exitCode=null,$termSignal=null) { $this->logWorker($process,"onDone"); $process->setMetaData("lastTime",microtime(true)); $this->queue->updateWorkerStatus($process->getId(),""); dhGlobal::trace("[worker]",$process->getId(),"worker process exited"); $this->removeWorker($process->getId()); if(isset($this->processWork[$process->getId()])) { $workId = $this->processWork[$process->getId()]; if(($work = $this->queue->getActive($workId)) !== false) { $this->workOnError($work,null,true); } } $this->checkWorkers(true); if(empty($this->processes)) { $this->queue->updateProgress(true); } } private function processOnData(WorkerProcess $process,$chunk=null) { $this->logWorker($process,"<-- data:",$this->logDisplay($chunk)." "); $process->setMetaData("lastTime",microtime(true)); if(($message = Message::fromPacket($chunk)) !== false) { //dhGlobal::outLine("[worker]",$process->getPid(),"packet:",$message->getWorkId()); if($message->getWorkId() == "init") { $this->queue->updateWorkerStatus($process->getId(),""); } elseif($message->getWorkId() == "ack") { } elseif($message->getWorkId() == "ready") { $this->queue->updateWorkerStatus($process->getId(),""); if(!$this->checkWorkerWorkCount($process)) { $this->sendNoWorkPacket($process); return; } if(($work = $this->queue->next()) !== false) { $this->sendProcessPacket($process,$work); $this->processWork[$process->getId()] = $work->getId(); $this->queue->updateWorkerStatus($process->getId(),$work->getMetaData("name",$work->getId())); $this->getProcessWorkCount($process,true); return; } else { //Maybe add a delay here to cut down on a lot of IO? $this->sendNoWorkPacket($process); return; } } elseif(($work = $this->queue->getActive($message->getWorkId())) !== false) { $process->setMetaData("status",""); $this->queue->updateWorkerStatus($process->getId(),""); unset($this->processWork[$process->getId()]); if($message->isError()) { $this->workOnError($work,$message); if($this->killOnError) { $process->stop(); } } else { $this->workOnDone($work,$message); } } } } private function workOnDone(Work $work,$message=null) { return $this->queue->workOnDone($work,$message); } private function workOnError(Work $work,$message=null,$workerClosed=false) { return $this->queue->workOnError($work,$message,$workerClosed); } private function sendProcessPacket(WorkerProcess $process,Work $work) { $message = new Message(); $message->setWorkId($work->getId()); $message->setData($work->getWork()); dhGlobal::trace("send","work",$message->toPacket()); $process->write($message->toPacket()."\n"); $this->logWorker($process,"-->","work",$message->toDisplay()); } private function sendNoWorkPacket(WorkerProcess $process) { $message = new Message(); $message->setWorkId("nowork"); $message->setData(["time"=>microtime(true)]); dhGlobal::trace("send","noWork",$message->toPacket()); $process->write($message->toPacket()."\n"); $this->logWorker($process,"-->","noWork",$message->toDisplay()); } private function sendBootstrapPacket(WorkerProcess $process) { $message = new Message(); $message->setWorkId("init"); $message->setData(["callable"=>["static","bootstrap"],"args"=>[$this->workerBootstrap]]); dhGlobal::trace("send","init",$message->toPacket()); $process->write($message->toPacket()."\n"); $this->logWorker($process,"-->","init",$message->toDisplay()); } private function logWorker(WorkerProcess $process,...$args) { array_unshift($args,"[WORKER]",":".$process->getId().":"); $this->_log(...$args); } private function _log(...$args) { if($this->log !== false) { if(is_null($this->logger)) { $this->logger = new dhOut(false,$this->log); } $this->logger->line(...$args); } } private function logDisplay($var,$len=150) { if(is_array($var)) { $var = json_encode($var); } if(!is_array($var) && !is_object($var)) { if(strlen($var)>$len) { $packet = substr($var,0,$len-4)." ..."; } } return $var; } /** * Get the value of numWorkers * @return mixed */ public function getNumWorkers() { return $this->numWorkers; } /** * Set the value of numWorkers * @param mixed $numWorkers * @return self */ public function setNumWorkers($numWorkers) { $this->numWorkers = $numWorkers; return $this; } /** * Get the value of workerBootstrap * @return mixed */ public function getWorkerBootstrap() { return $this->workerBootstrap; } /** * Set the value of workerBootstrap * @param mixed $workerBootstrap * @return self */ public function setWorkerBootstrap($workerBootstrap) { $this->workerBootstrap = $workerBootstrap; return $this; } /** * Get the value of bootstrapAsCallable * @return mixed */ public function getBootstrapAsCallable() { return $this->bootstrapAsCallable; } /** * Set the value of bootstrapAsCallable * @param mixed $bootstrapAsCallable * @return self */ public function setBootstrapAsCallable($bootstrapAsCallable) { $this->bootstrapAsCallable = $bootstrapAsCallable; return $this; } /** * Get the value of workerErrorLimit * @return mixed */ public function getWorkerErrorLimit() { return $this->workerErrorLimit; } /** * Set the value of workerErrorLimit * @param mixed $workerErrorLimit * @return self */ public function setWorkerErrorLimit($workerErrorLimit) { $this->workerErrorLimit = $workerErrorLimit; return $this; } /** * Get the value of maxWorkerWork * @return mixed */ public function getMaxWorkPerWorker() { return $this->maxWorkerWork; } /** * Set the value of maxWorkerWork * @param mixed $maxWorkerWork * @return self */ public function setMaxWorkPerWorker($maxWorkerWork) { $this->maxWorkerWork = $maxWorkerWork; return $this; } /** * Get the value of workerTimeout * @return mixed */ public function getWorkerTimeout() { return $this->workerTimeout; } /** * Set the value of workerTimeout * @param mixed $workerTimeout * @return self */ public function setWorkerTimeout($workerTimeout) { $this->workerTimeout = $workerTimeout; return $this; } /** * Get the value of killOnError * @return mixed */ public function getKillOnError() { return $this->killOnError; } /** * Set the value of killOnError * @param mixed $killOnError * @return self */ public function setKillOnError($killOnError) { $this->killOnError = $killOnError; return $this; } /** * Get the value of log * @return mixed */ public function getLog() { return $this->log; } /** * Set the value of log * @param mixed $log * @return self */ public function setLog($log) { $this->log = $log; return $this; } /** * Get the value of logger * @return mixed */ public function getLogger() { return $this->logger; } /** * Set the value of logger * @param mixed $logger * @return self */ public function setLogger($logger) { $this->logger = $logger; return $this; } /** * Get the value of logDir * @return mixed */ public function getLogDir() { return $this->logDir; } /** * Set the value of logDir * @param mixed $logDir * @return self */ public function setLogDir($logDir) { if(substr($logDir,0,1) != "/") { $cwd = getcwd(); if(substr($cwd,-1) != "/") { $cwd .="/"; } $logDir = $cwd.$logDir; } $this->logDir = $logDir; return $this; } public function getProcessCount() { return count($this->processes); } }src/async/worker/Worker.php000064400000037300144761607200011755 0ustar00$v) { if(substr($v,0,4) == "log:") { unset($argv[$i]); $t = explode(":",$v); if(isset($t[1])) { $logDir = $t[1]; } } if($v == "debug") { unset($argv[$i]); $debug=true; $maxWaitBeforeExit=0; } elseif($v == "preload") { unset($argv[$i]); $preload=true; } elseif(substr($v,0,1) == "#") { unset($argv[$i]); $preloadPackets[] = $v; } elseif(strpos($argv[$i],"/") !== false || strpos($argv[$i],".") !== false) { $includes[] = $argv[$i]; } } } if(!$preload) { $preloadPackets = null; } if(!empty($includes)) { foreach($includes as $include) { if(file_exists($include)) { require_once $include; } } } $pparts = explode(DIRECTORY_SEPARATOR,__DIR__); $pparts = array_reverse($pparts); if($pparts[3] == "dhutils" && $pparts[5] == "vendor") { require_once __DIR__."/../../../../../autoload.php"; } else { require_once __DIR__."/../../../vendor/autoload.php"; } /** * I understand completely that we shouldn't throw random ini_set definitions within libraries, but centos does weird things. */ if(php_sapi_name() == "cli") { ini_set("memory_limit",-1); } if(!is_null($logDir)) { initLogging($logDir); } $utilWorker = new Worker($preloadPackets,$debug,$maxWaitBeforeExit); class Worker { private $id; private $delay = 0.1; private $maxWaitBeforeExit = 5; public static $bootstrap; private $debug = false; private $log = false; private $maxLogResponseLength=300; private $running = false; private $checking = false; private $terminated = false; private $readyLastSent; /** @var \React\Stream\ReadableResourceStream */ private $stdin; /** @var \React\Stream\WritableResourceStream */ private $stdout,$stderr; private $partialLine = ""; public function __construct($preloadPackets=null,$debug=false,$maxWaitBeforeExit=5) { $this->id = uniqid(); $this->maxWaitBeforeExit = $maxWaitBeforeExit; $this->debug = $debug; stream_set_blocking(STDIN, 0); if(!is_null($preloadPackets) && is_array($preloadPackets)) { foreach($preloadPackets as $packet) { $this->processPacket($packet); } } if(dhGlobal::get("asyncLog",false) !== false) { $this->log = true; } if(($ml = dhGlobal::get("asyncMaxLogResponseLength",false)) !== false) { $this->maxLogResponseLength = $ml; } $this->stdin = new ReadableResourceStream(STDIN); $this->stdout = new WritableResourceStream(STDOUT); $this->stderr = new WritableResourceStream(STDERR); $this->stdin ->on("data", function($chunk) { $this->onInput("stdin",$chunk); }); $this->stdout ->on("data", function($chunk) { $this->onInput("stdout",$chunk); }); $this->stderr ->on("data", function($chunk) { $this->onInput("stderr",$chunk); }); $this->stdin ->on("close",function() { $this->onClose("stdin"); }); $this->stdout ->on("close",function() { $this->onClose("stdout"); }); $this->stderr ->on("close",function() { $this->onClose("stderr"); }); $this->stdin ->on("error",function(\Exception $e) { $this->onError("stdin",$e); }); $this->stdout ->on("error",function(\Exception $e) { $this->onError("stdout",$e); }); $this->stderr ->on("error",function(\Exception $e) { $this->onError("stderr",$e); }); Loop::addPeriodicTimer($this->delay,function($timer) { if(!$this->running && !$this->checking) { $this->sendMessage($this->makeReadyMessage(),false); $this->checking=true; $this->readyLastSent = microtime(true); } if($this->maxWaitBeforeExit>0 && $this->checking && !is_null($this->readyLastSent) && microtime(true)-$this->readyLastSent>$this->maxWaitBeforeExit) { $elapsed = microtime(true)-$this->readyLastSent; $this->sendMessage($this->responseMessage("info",["what"=>"terminating","why"=>round($elapsed,2)." seconds elapsed since 'ready' was sent, exceeds limit of ".$this->maxWaitBeforeExit." seconds"]),false); $this->terminate(false); } }); $this->log("init complete, started loop"); } public function __destruct() { if(!$this->terminated) { $this->log("process exited abruptly",debug_backtrace()); } $this->log("process exited"); } private function onInput($streamType,$chunk) { $this->log("[".$streamType."]",trim($chunk)); if($streamType == "stdin") { if(substr($chunk,-1) != "\n") { $this->partialLine.=$chunk; } else { $data = explode("\n",$this->partialLine.$chunk); $this->partialLine=""; foreach($data as $line) { $this->processPacket($line); } } } } private function onClose($streamType) { $this->log("[".$streamType."]","---CLOSED---"); } private function onError($streamType,\Exception $e) { $this->log("[".$streamType."]","---ERROR---",$e->getMessage()); } public function processPacket($framedPacket) { //echo "processPacket: $framedPacket\n"; if(($message = Message::fromPacket($framedPacket)) !== false) { $this->handlePacketMessage($message)->then(function($message) { $this->sendMessage($message); },function($message) { $this->sendMessage($message); }); } } private function handlePacketMessage($message) { $packetDeferred = new Deferred(); $this->running=true; $this->log(" <-- ",json_encode($message->get())); if(!$message->isValid()) { $packetDeferred->resolve($this->makeErrorMessage(0,Message::E_FRAME_ERROR,"unable to parse message frame")); } else { $workId = $message->getWorkId(); $this->sendMessage($this->makeAckMessage($workId),false); if($workId == "nowork"){ $this->processNoWork($workId,$message,$packetDeferred); } elseif($message->get("callable",false) !== false) { $this->processCallable($workId,$message,$packetDeferred); } else { $this->checking=false; $packetDeferred->resolve($this->makeErrorMessage($workId,Message::E_UNKNOWN_FRAME,$message->get())); } } return $packetDeferred->promise(); } private function processNoWork($workId,$message,&$packetDeferred) { $this->checking=false; $packetDeferred->resolve(true); Loop::stop(); $this->terminate(); return $packetDeferred; } private function processCallable($workId,$message,&$packetDeferred) { if(($callable = $message->get("callable",false)) !== false) { $this->checking=false; $args = $message->get("args",[]); $asJson = $message->get("asJson",false); try { $executeResult = $this->execute($workId,$callable,$args,$asJson); } catch(\Exception $e) { $packetDeferred->reject($this->makeErrorMessage($workId,Message::E_EXECUTE_EXCEPTION,$e->getMessage(),$e->getTrace())); } if($executeResult instanceof Message) { $packetDeferred->resolve($executeResult); } elseif($executeResult instanceof PromiseInterface) { $executeResult->then(function($executeMessage) use ($packetDeferred) { $packetDeferred->resolve($executeMessage); },function($e) use($workId,$packetDeferred) { $packetDeferred->reject($this->makeErrorMessage($workId,Message::E_EXECUTE_EXCEPTION,$e->getMessage(),$e->getTrace())); }); } } else { $packetDeferred->resolve(false); } return $packetDeferred; } private function execute($workId,$callable,$args,$asJson=false) { $this->logExecuteStart($callable); if(!is_array($args)) { $args = [$args]; } $result = $output = ""; $trace = null; $success = false; if(!is_callable($callable)) { $this->logExecuteError($callable,Message::E_NOT_CALLABLE); $stringCallable = is_array($callable) ? implode("::",$callable) : $callable; return $this->makeErrorMessage($workId,Message::E_NOT_CALLABLE,"$stringCallable is not callable",null); } try { $result = call_user_func($callable,...$args); $success = true; } catch (WorkerException $e) { $this->logExecuteError($callable,$e->getMessage()); return $this->makeErrorMessage($workId,$e->getType,$e->getMessage()); } catch (\Exception $e) { $success=false; $result = $e->getMessage(); $trace = $e->getTrace(); } if($result instanceof \React\Promise\PromiseInterface) { return $this->handleExecDeferredResult($result,$workId,$success,$output,$callable,$asJson,$trace); } else { return $this->handleExecDirectResult($result,$workId,$success,$output,$callable,$asJson,$trace); } } private function handleExecDirectResult($result,$workId,$success,$output,$callable,$asJson,$trace) { $rawResult = null; if($success && $asJson && !is_array($result)) { $rawResult = $result; $result = json_decode($result,true); } if(!$success) { $this->logExecuteError($callable,Message::E_EXECUTE_EXCEPTION." - ".$rawResult); return $this->makeErrorMessage($workId,Message::E_EXECUTE_EXCEPTION,$result,$trace); } $returnData = ["callable"=>$callable,"result"=>$result,"stdout"=>$output]; if(!is_null($rawResult)) { $returnData["raw"]=$rawResult; } $this->logExecuteSuccess($callable); return $this->makeSuccessMessage($workId,$returnData); } private function handleExecDeferredResult($result,$workId,$success,$output,$callable,$asJson,$trace) { $execDeferred = new Deferred(); $result->then(function($actualResult) use ($workId,$success,$output,$callable,$asJson,$trace,&$execDeferred) { $rawResult = null; if($success && $asJson && !is_array($actualResult)) { $rawResult = $actualResult; $actualResult = json_decode($actualResult,true); } if(!$success) { $this->logExecuteError($callable,Message::E_EXECUTE_EXCEPTION." - ".$rawResult); $execDeferred->resolve($this->makeErrorMessage($workId,Message::E_EXECUTE_EXCEPTION,$actualResult,$trace)); return false; } else { $returnData = ["callable"=>$callable,"result"=>$actualResult,"stdout"=>$output]; if(!is_null($rawResult)) { $returnData["raw"]=$rawResult; } $this->logExecuteSuccess($callable); $execDeferred->resolve($this->makeSuccessMessage($workId,$returnData)); } },function($e) use ($workId,$callable,&$execDeferred) { if(is_object($e) && method_exists($e,"getMessage")) { $result = $e->getMessage(); } else { $result = $e; } if(is_object($e) && method_exists($e,"getTrace")) { $trace = $e->getTrace(); } else { $trace = null; } $this->logExecuteError($callable,Message::E_EXECUTE_EXCEPTION." - ".$result); $execDeferred->resolve($this->makeErrorMessage($workId,Message::E_EXECUTE_EXCEPTION,$result,$trace)); return false; }); return $execDeferred->promise(); } private function logExecuteStart($callable,$msg="") { $stringCallable = is_array($callable) ? implode("::",$callable) : $callable; $this->log("execute",$stringCallable,"START ",$msg); } private function logExecuteSuccess($callable,$msg="") { $stringCallable = is_array($callable) ? implode("::",$callable) : $callable; $this->log("execute",$stringCallable,"SUCCESS",$msg); } private function logExecuteError($callable,$msg="") { $stringCallable = is_array($callable) ? implode("::",$callable) : $callable; $this->log("execute",$stringCallable,"ERROR ",$msg); } public function makeSuccessMessage($workId,$data) { return $this->responseMessage($workId,$data); } public function makeErrorMessage($workId,$code,$message="",$trace=null) { return $this->responseMessage($workId,null,["code"=>$code,"message"=>$message,"trace"=>null]); } public function makeReadyMessage() { return $this->responseMessage("ready",["time"=>microtime(true)]); } public function makeAckMessage($workId=null) { return $this->responseMessage("ack",["workId"=>$workId,"time"=>microtime(true)]); } public function responseMessage($workId,$data=null,$error=null) { $message = new Message(); $message->setWorkId($workId); $message->setData($data); $message->setError($error); return $message; } public function sendMessage(Message $message,$updateRunning=true) { /*if($this->maxLogResponseLength > 0) { $logmsg = substr(json_encode($message->get()),0,$this->maxLogResponseLength); if(strlen($logmsg)>=$this->maxLogResponseLength) { $logmsg.=" ..."; } } else { $logmsg = json_encode($message->get()); } */ $this->log(" --> ",$message->toPacket()); $this->stdout->write($message->toPacket()."\n"); if($updateRunning) { $this->running=false; } } private function terminate($success=true) { $this->terminated=true; if(!$success) { exit(1); } exit(); } private function log(...$args) { if($this->debug || $this->log) { array_unshift($args,"--".$this->id."--"); dhGlobal::outLine(...$args); } } public static function bootstrap($bootstrap) { return WorkerUtils::bootstrap($bootstrap); } public static function testError(...$args) { return WorkerUtils::testError(...$args); } public static function exec(...$args) { return WorkerUtils::exec(...$args); } public static function http(...$args) { return WorkerUtils::http(...$args); } } function initLogging($logdir) { if(!is_dir($logdir)) { exec("mkdir -p ".$logdir); } $log_file = $logdir."/error.log"; ini_set("log_errors",true); ini_set("dispaly_errors","off"); ini_set('error_log', $log_file); dhGlobal::logger("debugger",dhGlobal::LOG_ALL,false,$logdir."/worker.log"); dhGlobal::set("out",new dhOut(false,$logdir."/worker.log")); dhGlobal::set("asyncLog",true); }src/async/worker/WorkerException.php000064400000000536144761607200013635 0ustar00type = $type; parent::__construct($message, $code, $previous); } public function getType() { return $this->type; } }src/async/worker/WorkerProcess.php000064400000031474144761607200013322 0ustar00setCommand($command); $this->setArgs($args); $this->setMeta($meta); //sets all the handlers/etc $this->setPid(uniqid()); $this->buffer = new Buffer(); } /** * Starts the Child Process, returns the Promise for easy chaining * @return Promise|false * @throws UnexpectedValueException * @throws RuntimeException */ public function start() { $args = ''; if(!empty($this->args)) { $args = ProcessQueue::pack($this->args); } $this->process = new \React\ChildProcess\Process('exec '.$this->command.' '.$args); $this->process->start(); $this->setupProcessChannels(); $this->onStart(); return $this->Promise(); } public function stop() { $this->running=false; $this->process->terminate(); } public function terminate() { $this->running=false; $this->process->terminate(); if(!is_null($this->deferred)) { $this->deferred->reject("terminated"); } } public function get() { return [ "pid"=>$this->pid, "running"=>$this->running, "done"=>$this->done, "meta"=>$this->meta, "args"=>$this->args, "command"=>$this->command, "buffer"=>$this->buffer, "exitCode"=>$this->exitCode, "termSignal"=>$this->termSignal, "data"=>$this->data, ]; } public function write($data) { return $this->process->stdin->write($data); } public function isDone() { return $this->done; } public function isRunning() { return $this->running; } public function isReady() { return $this->ready; } /** * Return the processes's stdOut stream interface * @return \React\Stream\ReadableStreamInterface */ public function stdOut() { return $this->process->stdout; } /** * Return the processes's stdErr stream interface * @return \React\Stream\ReadableStreamInterface */ public function stdErr() { return $this->process->stderr; } /** * Return the processes's stdIn stream interface * @return \React\Stream\WritableStreamInterface */ public function stdIn() { return $this->process->stdin; } /** * Get the buffer * @return Buffer */ public function Buffer() { return $this->buffer; } /** * Return the Deferred * @return Deferred */ public function Deferred() { return $this->deferred; } /** * Return the Promise (if exists) * @return Promise|false */ public function Promise() { return !is_null($this->deferred) ? $this->deferred->promise() : false; } /** * Wrapper for \React\Promise\Promise::then() * @param callable|null $onFulfilled * @param callable|null $onRejected * @return PromiseInterface */ public function then(callable $onFulfilled=null,callable $onRejected=null) { return $this->Promise()->then($onFulfilled,$onRejected); } /** Handler Methods */ public function onDone($exitCode=null,$termSignal=null) { $this->done = true; $this->running = false; $this->ready = false; $this->exitCode = $exitCode; $this->termSignal = $termSignal; $this->deferred->resolve($this->buffer); if(!is_null($this->onDone)) { $handler = $this->onDone; $handler($this,$exitCode,$termSignal); } } public function onError($chunk=null) { if(!is_null($this->onError)) { $handler = $this->onError; $handler($this,$chunk); } if($this->useBuffer) { $this->buffer->err($chunk); } } public function onData($chunk=null) { if($this->isMessageFrames) { $data = !empty($this->messageBuffer) ? $this->messageBuffer : ""; $data.=$chunk; if(($message = Message::fromPacket($data)) !== false) { $chunk = $data; $this->messageBuffer = null; } else { $this->messageBuffer = $data; return; } } if(!is_null($this->onData)) { $handler = $this->onData; $handler($this,$chunk); } if($this->useBuffer) { $this->buffer->out($chunk); } } public function onStart() { $this->running = true; $this->ready = false; if(!is_null($this->onStart)) { $handler = $this->onStart; $handler($this); } } public function onStreamClose($type) { if(!is_null($this->onStreamClose)) { $handler = $this->onStreamClose; $handler($this,$type); } } public function onStreamError($type,$chunk=null) { if(!is_null($this->onStreamError)) { $handler = $this->onStreamError; $handler($this,$type,$chunk); } } /** * Set the stdout/stderr channel listening * @return void */ private function setupProcessChannels() { $this->process->stdout->on('data', function($chunk) { $this->onData(trim($chunk)); }); $this->process->stdout->on('error', function($chunk) { $this->onStreamError("stdout",$chunk); }); $this->process->stdout->on('close', function() { $this->onStreamClose("stderr"); }); $this->process->stderr->on('data', function($chunk) { $this->onError(trim($chunk)); }); $this->process->stderr->on('error', function($chunk) { $this->onStreamError("stderr",$chunk); }); $this->process->stderr->on('close', function() { $this->onStreamClose("stderr"); }); $this->process->on('exit', function($exitCode,$termSignal) { $this->onDone(); }); } public function getId() { return $this->getPid(); } /** * Get the value of pid * @return mixed */ public function getPid() { return $this->pid; } public function getPriority() { return $this->getPid(); } /** * Set the value of pid * @param mixed $pid * @return self */ public function setPid($pid) { $this->pid = $pid; return $this; } /** * Get the value of command * @return mixed */ public function getCommand() { return $this->command; } /** * Set the value of command * @param mixed $command * @return self */ public function setCommand($command) { $this->command = $command; return $this; } /** * Get the value of args * @return mixed */ public function getArgs() { return $this->args; } /** * Set the value of args * @param mixed $args * @return self */ public function setArgs($args) { $this->args = $args; return $this; } /** * Get the value of meta * @return mixed */ public function getMeta() { return $this->meta; } /** * Set the value of meta * @param mixed $meta * @return self */ public function setMeta($meta) { $this->meta = $meta; $this->onData = dhGlobal::getVal($meta, "onData", null); $this->onError = dhGlobal::getVal($meta, "onError", null); $this->onStart = dhGlobal::getVal($meta, "onStart", null); $this->onDone = dhGlobal::getVal($meta, "onDone", null); $this->deferred = dhGlobal::getVal($meta, "deferred",null); $this->onStreamClose = dhGlobal::getVal($meta, "onStreamClose",null); $this->onStreamError = dhGlobal::getVal($meta, "onStreamError",null); $this->useBuffer = dhGlobal::getVal($meta, "useBuffer", true); $this->isMessageFrames = dhGlobal::getVal($meta, "isMessageFrames", false); if(is_null($this->deferred)) { $this->deferred = new Deferred(); } return $this; } /** * Get the value of running * @return bool */ public function getRunning() { return $this->running; } /** * Set the value of running * @param bool $running * @return self */ public function setRunning($running) { $this->running = $running; return $this; } /** * Get the value of done * @return bool */ public function getDone() { return $this->done; } /** * Set the value of done * @param bool $done * @return self */ public function setDone($done) { $this->done = $done; return $this; } /** * Set the Deferred that will be resolved on completion * @param Deferred $deferred * @return $this */ public function setDeferred(Deferred $deferred) { $this->deferred = $deferred; return $this; } public function jsonSerialize($array=null) { if(is_null($array)) { $array = $this->get(); } return $array; } public function __toString() { return json_encode($this); } public function setMetaData($key,$val="",$append=false) { if(strpos($key,".") !== false) { if($append) { $check = dhGlobal::getDot($this->metaData,$key); if(!is_null($check)) { if(is_array($check)) { $check[] = $val; $val = $check; } else { $narr = []; $narr[] = $check; $narr[] = $val; $val = $narr; } } } dhGlobal::dotAssign($this->metaData,$key,$val); } else { if(isset($this->metaData[$key]) && $append) { if(is_array($this->metaData[$key])) { $this->metaData[$key][] = $val; } else { $temp = $this->metaData[$key]; $array[$key] = []; $array[$key][] = $temp; $array[$key][] = $val; } } else { $this->metaData[$key] = $val; } } return $this; } public function getMetaData($key=null,$default=null,$exists=false) { if(is_null($key)) { if($exists) { return !empty($this->metaData) ? true : false; } else { return !empty($this->metaData) ? $this->metaData : $default; } } if(strpos($key,".") !== false) { $uniqueid = uniqid("getArray",true); if(($check = dhGlobal::getDot($this->metaData,$key,$uniqueid)) !== $uniqueid) { return $exists ? true : $check; }; } if($exists) { return isset($this->metaData[$key]); } else { return isset($this->metaData[$key]) ? $this->metaData[$key] : $default; } } public function metaDataExists($key) { return $this->getMetaData($key,null,true); } }src/async/worker/WorkerUtils.php000064400000011753144761607200013002 0ustar0048) { throw new \Exception($message,$code); } } public static function exec(...$args) { if(!empty($args) && !is_null($args)) { $command = implode(" ",$args); passthru($command); return ["status"=>true,"command"=>$command]; } throw new \Exception("static::exec requires at least 1 arg"); return ["status"=>false]; } public static function http(...$args) { $reqData = $args[0]; $method = dhGlobal::getVal($reqData,"method",false); $url = dhGlobal::getVal($reqData,"url",false); $getJson = dhGlobal::getVal($reqData,"getJson",false); if($method === false || $url === false) { throw new \Exception("static::http requires 1 arg, that arg should be an array ['method'=>'get|post|..','url'=>'requestUrl','options'=>[]"); return false; } $http = new \boru\dhutils\dhHttp(); $req = $http->request($method,$url); if(($options = dhGlobal::getVal($reqData,"options",false)) !== false) { foreach($options as $type=>$option) { if(method_exists($req,$type)) { if(!isset($option[1])) { $req->$type($option[0]); } else { $req->$type($option[1],$option[2]); } } } } elseif(($requestData = dhGlobal::getval($reqData,"requestData",false)) !== false) { $req->data = $requestData; } $response = $req->send(); if($getJson) { $body = $response->body(true); } else { $body = $response->body(); } return [ "method"=>$method, "url"=>$url, "code"=>$response->code(), "phrase"=>$response->phrase(), "headers"=>$response->header(), "body"=>$body, ]; } public static function bootstrap($bootstrap) { if(!is_null(static::$bootstrap)) { throw new WorkerException(Message::E_BOOSTRAPPED_ALREADY,"already bootstrapped with ".json_encode(static::$bootstrap)); } $bootstrapFile = false; $setupCallable = false; $setupCallableArgs = []; if(!is_array($bootstrap)) { $bootstrapFile = $bootstrap; } elseif(isset($bootstrap["file"]) || isset($bootstrap["callable"])) { $bootstrapFile = isset($bootstrap["file"]) ? $bootstrap["file"] : false; $setupCallable = isset($bootstrap["callable"]) ? $bootstrap["callable"] : false; $setupCallableArgs = isset($bootstrap["args"]) ? $bootstrap["args"] : []; } else { throw new WorkerException(Message::E_BOOTSTRAP_INVALID,"Bootstrap must either be a filename or an array that includes 'file' and/or 'callable'"); } if(!__bootstrap_include($bootstrapFile)) { throw new WorkerException(Message::E_BOOTSTRAP_NOT_FOUND,"File not found: ".$bootstrap); } if(($callableResponse = __bootstrap_call($setupCallable,$setupCallableArgs)) === false) { if(is_array($setupCallable)) { $setupCallable = implode("::",$setupCallable); } throw new WorkerException(Message::E_BOOTSTRAP_NOT_CALLABLE,"Cannot call ".$setupCallable); } static::$bootstrap = $bootstrap; $output = []; if($bootstrapFile !== false) { $output["file"]=$bootstrapFile; } if($callableResponse !== false) { if($setupCallable !== false) { $output["callable"]=$setupCallable; } if($setupCallableArgs !== false) { $output["args"]=$setupCallableArgs; } $output["response"] = $callableResponse; } return ["bootstrap"=>"static::bootstrap"]; } } function __bootstrap_include($file) { if($file === false) { return true; } if(!file_exists($file)) { return false; } include $file; return true; } function __bootstrap_call($callable,$args) { if($callable === false) { return true; } if(!is_callable($callable)) { return false; } try { $return = call_user_func($callable,...$args); } catch (\Exception $e) { $return = false; } return $return; }src/base/Args.php000064400000006125144761607200007665 0ustar00" "]) { $separator = dhGlobal::getVal($options,"separator"," "); if(!is_array($args)) { $this->args = explode($separator,$args); } else { $this->args = $args; } } public function shift() { if(!$this->hasNext()) { return false; } return array_shift($this->args); } public function next() { if(!$this->hasNext()) { return false; } return $this->args[$this->cursor++]; } public function peek($num) { $num-=1; return isset($this->args[$this->cursor+$num]) ? $this->args[$this->cursor+$num] : false; } public function markUsed($peek=false) { $num = $this->cursor; $num-=1; if($peek!==false) { $num+=$peek; } $this->argsUsed[$num]=true; } public function hasNext() { return isset($this->args[$this->cursor]); } public function isLongArg($arg) { //print_r($arg); if($arg[0] == "-" && $arg[1] == "-" && !empty($arg[2])) { return true; } return false; } public function isShortArg($arg) { if($arg[0] == "-" && $arg[1] != "-" && !empty($arg[1])) { return true; } return false; } public function isValue($arg) { return !$this->isArg($arg); } public function isArg($arg) { if($this->isLongArg($arg) || $this->isShortArg($arg)) { return true; } return false; } public function getArgValue($arg) { if($this->isShortArg($arg)) { $o = substr($arg,1); if(strlen($o)<=1) { $peek = $this->peek(1); if($peek === false || $this->isArg($peek)) { return false; } $this->markUsed(1); return $peek; } else { return substr($o,1); } } elseif($this->isLongArg($arg)) { $o = substr($arg,2); $p = strpos($o, '='); if($p) { return substr($o, $p+1); } else { $peek = $this->peek(1); if($peek === false || $this->isArg($peek)) { return false; } $this->markUsed(1); return $peek; } } return false; } public function getArgs($includeUsed=false) { if($includeUsed) { return $this->args; } else { $args = $this->args; foreach($this->argsUsed as $k=>$v) { unset($args[$k]); } return array_values($args); } } }src/base/Container.php000064400000007403144761607200010713 0ustar00toArray(false); } $key = $this->camelKey($key); $value = dhGlobal::getVal($this->data,$key,$defaultValue); if(is_callable($value)) { return $value(); } return $value; } public function set($key,$value=null,$makePublic=true) { if(is_array($key)) { $this->data=$key; return $this; } $key = $this->camelKey($key); if(strpos($key,".")!==false) { if(!$this->dotExists($key)) { if(!$makePublic && !in_array($key,$this->nonPublicFields)) { $this->nonPublicFields[] = $key; } } dhGlobal::dotAssign($this->data,$key,$value); } else { if(!array_key_exists($key,$this->data)) { if(!$makePublic && !in_array($key,$this->nonPublicFields)) { $this->nonPublicFields[] = $key; } } $this->data[$key] = $value; } if(is_callable($value)) { $this->callableKeys[] = $key; } } public function getRaw($key=null,$defaultValue=null) { if(is_null($key)) { return $this->rawData; } return isset($this->rawData[$key]) ? $this->rawData[$key] : $defaultValue; } public function setRaw($key,$value=null) { if(is_array($key)) { $this->rawData=$key; return $this; } $this->rawData[$key] = $value; return $this; } public function toArray($hideNonPublic=true) { $data = $this->data; if($hideNonPublic) { foreach($this->nonPublicFields as $field) { dhGlobal::dotDelete($data,$field); } } foreach($this->callableKeys as $callableKey) { if($this->dotExists($callableKey)) { $value = $this->get($callableKey); dhGlobal::dotAssign($data,$callableKey,$value); } } return $data; } public function __toString() { return json_encode($this->toArray(),JSON_PRETTY_PRINT); } public function jsonSerialize() { return $this->toArray(); } public function setFromArray($array) { if(empty($row)) { return false; } $this->rawData = $array; foreach($row as $key=>$value) { //add it to our data set $this->set($key,$value,false); } } public function camelKey($key) { $parts = explode(".",trim($key)); $newKey = []; for($i=0;$idotExists(implode(".",$tempKey))) { $newPart = dhGlobal::camelize($part); $newKey[] = $newPart; } else { $newKey[] = $part; } } return implode(".",$newKey); } public function dotExists($dotString) { $pieces = explode(".", $dotString); $pointer = $this->data; for ($i = 0; $i < count($pieces); $i++) { if (isset($pointer[$pieces[$i]])) { $pointer = $pointer[$pieces[$i]]; } else { return false; } } return true; } }src/base/dhObject.php000064400000006001144761607200010504 0ustar00set() and ->get() handler */ class dhObject implements \JsonSerializable { protected $data = null; protected $separator = "."; public function __construct($arr=null,$separator=".") { $this->setSeparator($separator); if(is_array($arr) && !empty($arr)) { foreach($arr as $k=>$v) { $this->set($k,$v); } } } public function set($key,$val="",$append=false) { $this->setArray($this->data,$key,$val,$append); return $this; } public function get($key=null,$default=null) { return $this->getArray($this->data,$key,$default); } public function remove($key) { dhGlobal::dotDelete($this->data,$key,$this->separator); return $this; } public function setArray(&$array,$key,$val="",$append=false) { if(strpos($key,$this->separator) !== false) { if($append) { $check = dhGlobal::getDot($array,$key,$this->separator); if(!is_null($check)) { if(is_array($check)) { $check[] = $val; $val = $check; } else { $narr = []; $narr[] = $check; $narr[] = $val; $val = $narr; } } } dhGlobal::dotAssign($array,$key,$val); } else { if(isset($array[$key]) && $append) { if(is_array($array[$key])) { $array[$key][] = $val; } else { $temp = $array[$key]; $array[$key] = []; $array[$key][] = $temp; $array[$key][] = $val; } } else { $array[$key] = $val; } } return $this; } public function getArray($array,$key=null,$default=null) { if(is_null($key)) { return !empty($array) ? $array : $default; } if(strpos($key,$this->separator) !== false) { $check = dhGlobal::getDot($array,$key,null,$this->separator); if(!is_null($check)) { return $check; } } return isset($array[$key]) ? $array[$key] : $default; } public function jsonSerialize($array=null) { if(is_null($array)) { $array = $this->data; } return $array; } /** * Get the value of dotSeparator */ public function getSeparator() { return $this->separator; } /** * Set the value of dotSeparator * * @return self */ public function setSeparator($separator) { $this->separator = $separator; return $this; } }src/dAsyncHttp.php000064400000016726144761607200010150 0ustar00withTimeout($timeout)->get($url,$headers)->then($successCallback, $errorCallback); } /** * * @param mixed $url * @param array $headers * @param string $body * @param callable|null $onSuccess * @param callable|null $onError * @return \React\Promise\PromiseInterface * @throws InvalidArgumentException */ public static function post($url,$headers=[],$body='',callable $onSuccess=null,callable $onError=null,$timeout=true) { static::init(); $successCallback = static::makeSuccessCallback($url,"post",$onSuccess); $errorCallback = static::makeErrorCallback($url,"post",$onError); if(is_array($body)) { $body = json_encode($body); } return static::$browser->withTimeout($timeout)->post($url,$headers,$body)->then($successCallback, $errorCallback); } /** * * @param mixed $url * @param array $headers * @param string $body * @param callable|null $onSuccess * @param callable|null $onError * @return \React\Promise\PromiseInterface * @throws InvalidArgumentException */ public static function put($url,$headers=[],$body='',callable $onSuccess=null,callable $onError=null,$timeout=true) { static::init(); $successCallback = static::makeSuccessCallback($url,"put",$onSuccess); $errorCallback = static::makeErrorCallback($url,"put",$onError); if(is_array($body)) { $body = json_encode($body); } return static::$browser->withTimeout($timeout)->put($url,$headers,$body)->then($successCallback, $errorCallback); } /** * * @param mixed $url * @param array $headers * @param string $body * @param callable|null $onSuccess * @param callable|null $onError * @return \React\Promise\PromiseInterface * @throws InvalidArgumentException */ public static function patch($url,$headers=[],$body='',callable $onSuccess=null,callable $onError=null,$timeout=true) { static::init(); $successCallback = static::makeSuccessCallback($url,"patch",$onSuccess); $errorCallback = static::makeErrorCallback($url,"patch",$onError); if(is_array($body)) { $body = json_encode($body); } return static::$browser->withTimeout($timeout)->patch($url,$headers,$body)->then($successCallback, $errorCallback); } /** * * @param mixed $url * @param array $headers * @param string $body * @param callable|null $onSuccess * @param callable|null $onError * @return \React\Promise\PromiseInterface * @throws InvalidArgumentException */ public static function delete($url,$headers=[],$body='',callable $onSuccess=null,callable $onError=null,$timeout=true) { static::init(); $successCallback = static::makeSuccessCallback($url,"delete",$onSuccess); $errorCallback = static::makeErrorCallback($url,"delete",$onError); if(is_array($body)) { $body = json_encode($body); } return static::$browser->withTimeout($timeout)->delete($url,$headers,$body)->then($successCallback, $errorCallback); } /** * * @param mixed $url * @param array $headers * @param callable|null $onSuccess * @param callable|null $onError * @return \React\Promise\PromiseInterface * @throws InvalidArgumentException */ public static function head($url,$headers=[],callable $onSuccess=null,callable $onError=null,$timeout=true) { static::init(); $successCallback = static::makeSuccessCallback($url,"head",$onSuccess); $errorCallback = static::makeErrorCallback($url,"head",$onError); return static::$browser->withTimeout($timeout)->head($url,$headers)->then($successCallback, $errorCallback); } /** * * @param mixed $method * @param mixed $url * @param array $headers * @param mixed $body * @param callable|null $onSuccess * @param callable|null $onError * @return \React\Promise\PromiseInterface * @throws InvalidArgumentException */ public static function request($method,$url,$headers=[],$body,callable $onSuccess=null,callable $onError=null,$timeout=true) { static::init(); $successCallback = static::makeSuccessCallback($url,$method,$onSuccess); $errorCallback = static::makeErrorCallback($url,$method,$onError); if(is_array($body)) { $body = json_encode($body); } return static::$browser->withTimeout($timeout)->request($method,$url,$headers,$body)->then($successCallback, $errorCallback); } //aliases protected static function makeSuccessCallback($url,$method,callable $callback=null) { return function (\Psr\Http\Message\ResponseInterface $response) use($callback,$url,$method) { $resp = new \boru\dhutils\guzzle\Response($response); if(is_callable($callback)) { return $callback($resp); } else { dhGlobal::info("Unhandled dAHttp response from",$method,$url); return $resp; } }; } protected static function makeErrorCallback($url,$method,callable $callback=null) { return function (\Exception $e) use ($callback,$url,$method) { if(is_callable($callback)) { return $callback($e); } else { dhGlobal::error('Uncaught dAHttp Error from',$method,$url,":",$e->getMessage()); return $e; } }; } }src/dhGlobal.php000064400000020513144761607200007570 0ustar00$driver,"config"=>$config]); static::set("dhDB",$db); } $db = static::get("dhDB",false); if($db === false) { //try vtconfig.. maybe it exists if(static::fileIfExists("config.inc.php") !== false) { $db = $dbClass::fromVtigerConfig("config.inc.php"); static::set("dhDB",$db); } } return static::get("dhDB",false); } throw new \Exception("cannot init DB without dhDB being included"); } public static function dateIntervalToElapsed($dateInterval,$short=true,$includeZeros=false,$maxParts=false,$glue=" ") { $parts=[]; if($dateInterval->y > 0 || $includeZeros) { $parts[] = $dateInterval->y. ($short?"Y":" ". static::pluralize($dateInterval->y,"year")); } if(($dateInterval->m > 0 || $includeZeros) && ($maxParts===false || count($parts)<$maxParts)) { $parts[] = $dateInterval->m. ($short?"M":" ". static::pluralize($dateInterval->m,"month")); } if(($dateInterval->d > 0 || $includeZeros) && ($maxParts===false || count($parts)<$maxParts)) { $parts[] = $dateInterval->d. ($short?"D":" ". static::pluralize($dateInterval->d,"day")); } if(($dateInterval->h > 0 || $includeZeros) && ($maxParts===false || count($parts)<$maxParts)) { $parts[] = $dateInterval->h. ($short?"h":" ". static::pluralize($dateInterval->h,"hour")); } if(($dateInterval->i > 0 || $includeZeros) && ($maxParts===false || count($parts)<$maxParts)) { $parts[] = $dateInterval->i. ($short?"m":" ". static::pluralize($dateInterval->i,"minute")); } if(($dateInterval->s > 0 || $includeZeros) && ($maxParts===false || count($parts)<$maxParts)) { $parts[] = $dateInterval->s. ($short?"s":" ". static::pluralize($dateInterval->s,"second")); } return implode($glue,$parts); } public static function pluralize($quantity, $singular, $plural=null) { if($quantity==1 || !strlen($singular)) return $singular; if($plural!==null) return $plural; $last_letter = strtolower($singular[strlen($singular)-1]); switch($last_letter) { case 'y': return substr($singular,0,-1).'ies'; case 's': return $singular.'es'; default: return $singular.'s'; } } /** * * @return Template */ public static function template() { if(($template = static::get("template",false)) !== false) { return $template; } return static::set("template",new Template()); } public static function parseTemplate($templateString,$data=[]) { dhGlobal::template()->parse($templateString,$data); } /** * Get an array backtrace * @param int $levels * @param int $skipLines * @param mixed $traceArr * @param bool $includeArgs * @return array */ public static function backtrace($levels=9999,$skipLines=0,$traceArr=null,$includeArgs=false) { if (!function_exists('debug_backtrace')) return []; global $root_directory; if(is_null($traceArr)) { if($includeArgs) { $traceArr = debug_backtrace(); } else { $traceArr = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } } array_shift($traceArr); array_shift($traceArr); $output = []; foreach ($traceArr as $arr) { if ($skipLines) {$skipLines -= 1; continue;} $levels -= 1; if ($levels < 0) break; $entry = [ "line"=>"", "file"=>"", "func"=>"", ]; $file = $arr['file']; if(!empty($root_directory) && substr($arr['file'],0,strlen($root_directory) == $root_directory)) { $file = substr($arr["file"],strlen($root_directory)); } $func = ""; if(isset($arr["class"])) { $func = $arr["class"]; if(isset($arr["type"])) { $func.=$arr["type"]; } } if(isset($arr["function"])) { $func.=$arr["function"]."()"; } $entry["line"] = $arr["line"]; $entry["file"] = $file; $entry['func'] = $func; $output[] = $entry; } return $output; } }src/dhHttp.php000064400000010467144761607200007316 0ustar00true]; protected $client; protected $options; protected $response; protected $url; protected $method; public $debug_prefix = "dhHttp:: "; public $debugDoTrace = false; public $doDebug = false; /** * * @param mixed $request * @param array $options * @return \boru\dhutils\guzzle\Response * @throws GuzzleException */ public function send($request,$options=[]) { $method = $request->getMethod(); $url = $request->getUrl(); if(!empty($options)) { $request->replace($options); } $this->debug("Sending $method to $url"); $guzzleRequest = new \GuzzleHttp\Psr7\Request(strtoupper($method),$url); $tries = 0; $this->response = $this->client->send($guzzleRequest,$request->get()); while($this->response->getStatusCode() >= 500 && $tries < static::$retryLimit) { sleep(static::$retryDelay); $tries++; $this->debug("(retry $tries of ".static::$retryLimit.") Sending $method to $url"); $this->response = $this->client->send($guzzleRequest,$request->get()); } return $this->response; } public function __construct($method="",$url="",$options=[]) { $stack = new \GuzzleHttp\HandlerStack(); $stack->setHandler(new \GuzzleHttp\Handler\CurlHandler()); $stack->push(\GuzzleHttp\Middleware::mapResponse(function (\Psr\Http\Message\ResponseInterface $response) { return new \boru\dhutils\guzzle\Response($response); })); if(empty($options)) { $clientOpts = static::$clientOptions; } else { if(isset($options["trace"])) { $this->debugDoTrace = $options["trace"]; $this->doDebug = true; unset($options["trace"]); } elseif(isset($options["debug"])) { $this->doDebug = true; } $clientOpts = $options; } $clientOpts["handler"] = $stack; $this->client = new \GuzzleHttp\Client($clientOpts); if(!empty($method)) { $this->setMethod($method); } if(!empty($url)) { $this->setUrl($url); } } protected function debug(...$args) { if($this->doDebug) { array_unshift($args,$this->debug_prefix." - "); $level = $this->debugDoTrace ? "trace" : "debug"; array_unshift($args,$level); return call_user_func_array([$this,"log"],$args); } } public function log(...$args) { return dhGlobal::log(...$args); } /** * * @param mixed $method * @param mixed $url * @param array $options * @return Request */ public function request($method, $url, $options=[]) { return new \boru\dhutils\guzzle\Request($method,$url,$options,$this); } /** * Get the value of url */ public function getUrl() { return $this->url; } /** * Set the value of url * * @return self */ public function setUrl($url) { $this->url = $url; return $this; } /** * Get the value of method */ public function getMethod() { return $this->method; } /** * Set the value of method * * @return self */ public function setMethod($method) { $this->method = $method; return $this; } /** * Get the value of options */ public function getOptions() { return $this->options; } /** * Set the value of options * * @return self */ public function setOptions($options) { $this->options = $options; return $this; } }src/dhHttpLite.php000064400000004555144761607200010135 0ustar00setAuth($auth); } } public function newHeaders() { return new Params("header"); } public function newParams($type="array") { return new Params($type); } public static function params($array,$format="form") { return Params::fromArray($array,$format); } public function get($url,$params=null,$headers=null,$verbose=false) { return $this->call("get",$url,$params,$headers,$verbose); } public function post($url,$params=null,$headers=null,$verbose=false) { return $this->call("post",$url,$params,$headers,$verbose); } public function put($url,$params=null,$headers=null,$verbose=false) { return $this->call("put",$url,$params,$headers,$verbose); } public function delete($url,$params=null,$headers=null,$verbose=false) { return $this->call("delete",$url,$params,$headers,$verbose); } public function options($url,$params=null,$headers=null,$verbose=false) { return $this->call("options",$url,$params,$headers,$verbose); } public function call($method,$url,$params=null,$headers=null,$verbose=false) { $request = new Request(); $request ->method($method) ->url($url); if(!is_null($params)) { $request->param($params); } if(!is_null($headers)) { $request->header($headers); } if(!empty($this->auth)) { $args = []; $args = $this->auth; array_unshift($args,$this->authType); $request->auth(...$args); } $request->verbose($verbose); return $request->call(); } /** * Get the value of auth */ public function getAuth() { return $this->auth; } /** * Set the value of auth * * @return self */ public function setAuth($type="Basic",...$params) { $this->authType = $type; $this->auth = $params; return $this; } }src/dhLoader.php000064400000000255144761607200007577 0ustar00[ "scheme"=>"", "host"=>"", //"port"=>80, "path"=>"", "query"=>"" ], "get"=>[], "post"=>[], "body"=>[], "server"=>[], "headers"=>[] ]; use GetSetArray; public function __construct($get=[],$post=[],$headers=[]) { $this->setGet($get); $this->setPost($post); $this->setHeaders($headers); } public static function fromGlobals($jsonBody=false) { $instance = new self($_GET,$_POST,apache_request_headers()); $body = file_get_contents("php://input"); if(!empty($body)) { $instance->setBody($body,$jsonBody); } $instance->setServer($_SERVER); return $instance; } public function getGet($key=null,$default=null) { if(is_null($key)) { return $this->get("get",$default); } return $this->get("get.".$key,$default); } public function getPost($key=null,$default=null) { if(is_null($key)) { return $this->get("post",$default); } return $this->get("post.".$key,$default); } public function getBody($key=null,$default=null) { if(is_null($key)) { return $this->get("body",$default); } return $this->get("body.".$key,$default); } public function getHeader($key=null,$default=null) { if(is_null($key)) { return $this->get("headers"); } return $this->get("headers.".$key,$default); } public function getServer($key=null,$default=null) { if(is_null($key)) { return $this->get("server",$default); } return $this->get("server.".$key,$default); } public function setGet($get) { $this->set("get",$get); return $this; } public function setPost($post) { $this->set("post",$post); return $this; } public function setBody($body,$json=false) { if($json) { $tempobj=json_decode($body,true); if(!empty($tempobj) && is_array($tempobj)) { foreach($tempobj as $k=>$v) { $body[$k]=$v; } } } if(is_array($body)) { $body = dhGlobal::parseDots($body); } $this->set("body",$body); return $this; } public function getHeaders() { return $this->get("headers"); } public function setHeaders($headers) { $this->set("headers",$headers); return $this; } public function setServer($server) { $method = isset($server['REQUEST_METHOD']) ? $server['REQUEST_METHOD'] : 'GET'; $this->setMethod($method); $this->set("uri.scheme",isset($server["HTTPS"]) ? "https" : "http"); $hasPort = false; if (isset($server['HTTP_HOST'])) { list($host, $port) = self::extractHostAndPortFromAuthority($server['HTTP_HOST']); if ($host !== null) { $this->set("uri.host",$host); } if ($port !== null) { $hasPort = true; $this->set("uri.port",$port); } } elseif (isset($server['SERVER_NAME'])) { $this->set("uri.host",$server['SERVER_NAME']); } elseif (isset($server['SERVER_ADDR'])) { $this->set("uri.host",$server['SERVER_ADDR']); } if (!$hasPort && isset($server['SERVER_PORT'])) { $this->set("uri.port",$server['SERVER_PORT']); } $hasQuery = false; if (isset($server['REQUEST_URI'])) { $requestUriParts = explode('?', $server['REQUEST_URI'], 2); $this->set("uri.path",$requestUriParts[0]); if (isset($requestUriParts[1])) { $hasQuery = true; $this->set("uri.query",$requestUriParts[1]); } } if (!$hasQuery && isset($server['QUERY_STRING'])) { $this->set("uri.query",$server['QUERY_STRING']); } $this->set("server",$server); return $this; } public function setUri($uri=[]) { $this->set("uri",$uri); } private static function extractHostAndPortFromAuthority($authority) { $uri = 'http://' . $authority; $parts = parse_url($uri); if (false === $parts) { return [null, null]; } $host = isset($parts['host']) ? $parts['host'] : null; $port = isset($parts['port']) ? $parts['port'] : null; return [$host, $port]; } /** * Get the value of method * * @return mixed */ public function getMethod() { return $this->method; } /** * Set the value of method * * @param mixed $method * @return self */ public function setMethod($method) { $this->method = $method; return $this; } }src/dhScriptLock.php000064400000000217144761607200010444 0ustar00maxThreads = $maxThreads; $this->throttleDelay = $throttleDelay; $this->setOptions($options); declare(ticks = 1); pcntl_signal(SIGINT,[$this,"__shutdown"]); } public function __destruct() { $this->killAll(); } public function __shutdown() { $this->__destruct(); usleep(0.5*1000); exit(); } public function killThread($thread) { if(is_object($thread)) { $thread->stop(); return true; } elseif(is_numeric($thread)) { posix_kill($thread, 0); return true; } else { return false; } } public function killAll() { if(!$this->stopped) { $this->stopped = true; foreach($this->runningThreads as $thread) { $thread->stop(); } } } public function startScript($file,$data=[],$options=[],$wait=true) { $meta = []; if(!empty($data)) { $args = $this->packData($data); $meta["data"] = $data; } else { $args = ""; $meta = []; } if(is_object($file)) { $command = "php -f ".$file->path()." ".$args; } else { $command = "php -f ".$file." ".$args; } $this->start($command,$meta,$options,$wait); } public function start($command,$meta=[],$options=[],$wait=true) { if(!$this->stopped) { $meta["id"] = $this->threadIndex; $this->threadIndex++; $thread = new Thread($command,$meta,$options); if($wait) { return $this->startThread($thread); } else { return $this->addThread($thread); } } return false; } protected function addThread($thread) { $this->waitingThreads[] = $thread; return $this; } protected function startThread($thread=null) { if(is_null($thread) && !empty($this->waitingThreads)) { $thread = array_shift($this->waitingThreads); } if(!is_null($thread)) { $this->throttle(); if($this->callbackVisualizer instanceof MTCallbackInterface) { $this->callbackVisualizer->threadStart($this,$thread); } $this->runningThreads[] = $thread; dhGlobal::trace("[MT] Started thread with pid=".$thread->pid()); $this->countRunningThreads(); return $thread->pid(); } return false; } public function collect($options=[]) { $collectType = dhGlobal::getVal($options,"collectType",null); $this->wait(); if($collectType == static::COLLECT_ARRAY) { $return = []; foreach($this->finishedThreads as $thread) { $return[] = $thread->output(); } return $return; } elseif($collectType == static::COLLECT_FULL || $collectType == static::COLLECT_OBJECT) { return $this->finishedThreads; } return true; } public function wait() { //dhGlobal::outLine(count($this->waitingThreads)); $this->waiting=true; $this->throttle(1); while($this->startThread() !== false) { } $this->throttle(1); $this->waiting=false; } protected function throttle($maxThreads=null) { if(is_null($maxThreads)) { $maxThreads = $this->maxThreads; } if($maxThreads > 0) { while(($count = $this->countRunningThreads()) >= $maxThreads) { $this->throttled = true; dhGlobal::trace("[MT] $count runningThreads is >= $maxThreads.. waiting.",($this->throttleDelay * 1000000)); usleep($this->throttleDelay * 1000000); } $this->throttled = false; } } protected function countRunningThreads() { foreach($this->runningThreads as $k=>$thread) { if($thread->finished()) { dhGlobal::trace("[MT] thread $k has completed"); $this->finishedThreads[] = $thread; unset($this->runningThreads[$k]); if($this->callbackVisualizer instanceof MTCallbackInterface) { $this->callbackVisualizer->collect($thread->output()); } if($this->callbackVisualizer instanceof MTCallbackInterface) { $this->callbackVisualizer->threadFinished($this,$thread); } } } if($this->callbackVisualizer instanceof MTCallbackInterface) { $this->callbackVisualizer->loop($this); } return count($this->runningThreads); } public function setOptions($options=[]) { //visualizer object.. default to BlankVisualizer. //visualizer holds 3 callbacks, threadStarted, threadFinished, and throttleWait (queued) $cbVisualizerClass = dhGlobal::getVal($options,"callbackVisualizer",new BlankVisualizer()); if(!is_object($cbVisualizerClass) && !is_null($cbVisualizerClass)) { if(class_exists($cbVisualizerClass,true)) { $this->callbackVisualizer = new $cbVisualizerClass(); } } elseif($cbVisualizerClass instanceof MTCallbackInterface) { $this->callbackVisualizer = $cbVisualizerClass; } if(is_null($this->callbackVisualizer)) { $this->callbackVisualizer = new BlankVisualizer(); } if((dhGlobal::getVal($options,"debug",false)) !== false) { $this->debugMode = true; } elseif((dhGlobal::getVal($options,"debugMode",false)) !== false) { $this->debugMode = true; } if(($threadStartCallback = dhGlobal::getval($options,"threadStartCallback",false)) !== false) { $this->callbackVisualizer->setThreadStartCallback($threadStartCallback); } if(($threadFinishedCallback = dhGlobal::getval($options,"threadFinishedCallback",false)) !== false) { $this->callbackVisualizer->setThreadFinishedCallback($threadFinishedCallback); } if(($throttleWaitCallback = dhGlobal::getval($options,"throttleWaitCallback",false)) !== false) { $this->callbackVisualizer->setThrottleWaitCallback($throttleWaitCallback); } } public function debug(...$data) { if($this->debugMode) { array_unshift($data,"[T- main] "); dhGlobal::debug(...$data); } } public function packData($data) { return static::pack($data); } public function unpackData($packedData,$object=false) { return static::unpack($packedData,$object); } public static function pack($data) { if(is_array($data) || is_object($data)) { $data = json_encode($data); } return base64_encode($data); } public static function unpack($packedData,$object=false) { $data = base64_decode($packedData); $test = json_decode($data,!$object); if(is_array($test) || is_object($test)) { return $test; } return $data; } public static function sendChannel($runnerID,$channelType,$data) { if($channelType == static::CHANNEL_MEMCACHED) { if(class_exists('\Memcached')) { return dhGlobal::cache()->lockSet($runnerID,$data); } else { dhGlobal::error("Memcached not work"); } } } public static function readChannel($runnerID,$channelType) { if($channelType == static::CHANNEL_MEMCACHED) { if(class_exists('\Memcached')) { if(($data = dhGlobal::cache()->lockGet($runnerID))!==false) { return $data; } } else { dhGlobal::error("Memcached not work"); } } } }src/filesys/BasePath.php000064400000021564144761607200011230 0ustar00resolvePath($path,$expand); } $this->basePathDebug = $debug; } public static function path($path,$expand=false,$debug=false) { $instance = new self($path,$expand,$debug); return $instance->fullPath($expand); } public function fullPath($expand=false) { return $expand ? $this->expandedPath : $this->fullPath; } public function baseDir($expand=false) { return $expand ? $this->expandedBaseDir : $this->baseDir; } public function isDir() { return $this->isDir(); } public function isLink() { return $this->isLink(); } public function makeDir() { mkdir($this->fullPath,0777,true); $this->resolvePath($this->fullPath); } public function makeFile() { if(!empty($this->remaining) && is_array($this->remaining)) { $this->createBase(); } if(!file_exists($this->fullPath)) { touch($this->fullPath); $this->resolvePath($this->fullPath); } } public function createBase() { if(!empty($this->remaining) && is_array($this->remaining)) { $start = $this->baseDir; $dir = $start.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR,$this->remaining); if(!file_exists($dir)) { mkdir($dir,0777,true); $this->resolvePath($this->fullPath); } } } public function relative($testPath) { if(!is_object($testPath)) { $testPath = new BasePath($testPath); } $expandedParent = $testPath->fullPath(true); $fullPath = $this->fullPath(true); if(substr($fullPath,0,strlen($expandedParent)) !== $expandedParent) { return false; } $t = substr($fullPath,strlen($expandedParent)); if(substr($t,0,strlen(DIRECTORY_SEPARATOR)) == DIRECTORY_SEPARATOR) { $t = substr($t,strlen(DIRECTORY_SEPARATOR)); } return $t; } private function basePathDebug(...$args) { if($this->basePathDebug) { array_unshift($args,"debug"); return dhGlobal::log(...$args); } return false; } protected function resolvePathExists($path,$expand=false) { if(substr($path,-1) == "/") { $path = substr($path,0,-1); } $parts = explode("/",$path); $this->basePathDebug("file exists"); if(substr($path,0,1) !== "/") { $this->baseDir = is_link(getcwd()) ? readlink(getcwd()) : realpath(getcwd()); $this->baseDir.= "/".dirname($path); $this->basePathDebug("relative",$this->baseDir); } else { $this->baseDir = dirname($path); $this->basePathDebug("real",$this->baseDir); } $this->expandedBaseDir = is_link($this->baseDir) ? readlink($this->baseDir) : realpath($this->baseDir); if(is_dir($path)) { $this->isDir = true; } $this->fileName = array_pop($parts); $this->fullPath = $this->baseDir; if(!is_null($this->fileName)) { $this->fullPath .= "/". $this->fileName; } else { $this->fullPath .= "/".array_pop($parts); } if(is_link($this->fullPath)) { $this->isLink = true; $this->expandedPath = readlink($this->fullPath); } else { $this->expandedPath = realpath($this->fullPath); } if(is_dir($this->expandedPath)) { $this->isDir = true; } $this->formatOutput(); if($expand) { return $this->expandedPath; } return $this->fullPath; } private function resolvePathDots($path) { $parts = explode("/",$path); $newParts = []; while(!empty($parts)) { $part = array_shift($parts); if($part == "..") { if(count($newParts)>=1) { array_pop($newParts); } } else { $newParts[] = $part; } } return implode("/",$newParts); } public function resolvePath($path,$expand=false) { $path = $this->toLinuxSlash($path); $this->basePathDebug("using $path"); if(substr($path,-1) == "/") { $this->isDir = true; $path = substr($path,0,-1); } if(strpos($path,"..") !== false) { //resolve ../ first $this->basePathDebug("has .."); $path = $this->resolvePathDots($path,$expand); } $remaining = false; $parts = explode("/",$path); while(end($parts) == ".") { array_pop($parts); $parts = explode("/",implode("/",$parts)); } if(empty($parts) || count($parts) === 1 && empty($parts[0])) { $path = getcwd(); $parts = explode("/",$path); } if(file_exists($path)) { return $this->resolvePathExists($path,$expand); } $this->basePathDebug("file doesnt exist"); if(substr($path,0,1) !== "/") { $this->baseDir = getcwd(); $this->basePathDebug("relative ".$this->baseDir); } else { $this->baseDir = "/"; $this->basePathDebug("real ".$this->baseDir); } $newParts = []; while(!empty($parts)) { $part = array_shift($parts); if(!empty($part)) { if(file_exists($this->baseDir."/".implode("/",$newParts)."/".$part)) { $newParts[] = $part; } else { array_unshift($parts,$part); break; } } } $this->baseDir.= !empty($newParts) ? "/".implode("/",$newParts) : ""; if(!empty($parts)) { $remaining = implode("/",$parts); $this->fileName = array_pop($parts); } unset($newParts); $this->basePathDebug("basedir= ".$this->baseDir); $this->baseDir = $this->baseDir; $this->expandedBaseDir = realpath($this->baseDir); if($remaining !== false) { $this->basePathDebug("remaining= $remaining"); $this->remaining = $parts; $this->fullPath = $this->baseDir."/".$remaining; $this->expandedPath = realpath($this->baseDir); $this->expandedPath.="/".$remaining; } else { $this->fullPath = $this->baseDir; $this->expandedPath = realpath($this->baseDir); } $this->formatOutput(); if($expand) { return $this->expandedPath; } return $this->fullPath; } protected function formatOutput() { $this->fullPath = !is_null($this->fullPath) ? $this->formatPathOut($this->fullPath) : null; $this->expandedPath = !is_null($this->expandedPath) ? $this->formatPathOut($this->expandedPath) : null; $this->baseDir = !is_null($this->baseDir) ? $this->formatPathOut($this->baseDir) : null; $this->expandedBaseDir = !is_null($this->expandedBaseDir) ? $this->formatPathOut($this->expandedBaseDir) : null; //$this->baseDir = !is_null($this->baseDir) ? $this->formatPathOut($this->baseDir) : null; //$this->baseDir = !is_null($this->baseDir) ? $this->formatPathOut($this->baseDir) : null; } private function formatPathOut($path) { return $this->toDirSeparator($this->toLinuxSlash($path)); } private function toLinuxSlash($path) { //make all separators into / style separators if(DIRECTORY_SEPARATOR !== '/') { $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); } $path = str_replace("\\","/",$path); while(strpos($path,"//") !== false) { $path = str_replace("//","/",$path); } //clean out useles '.' references $path = StringUtil::trimString("./",$path,StringUtil::TRIM_START); $path = StringUtil::trimString("/.",$path,StringUtil::TRIM_END); $path = str_replace('/./','/',$path); return $path; } private function toDirSeparator($path) { return str_replace("/",DIRECTORY_SEPARATOR,$path); } }src/filesys/ConfigFile.php000064400000003375144761607200011546 0ustar00file = new File(["path"=>$fileopt,"create"=>true]); } else { $this->file = $fileopt; } $this->prettyJson = dhGlobal::getVal($options,"prettyJson",false); $this->sort = dhGlobal::getVal($options,"sort",true); $this->load(); } public function load() { $content = $this->file->content(["json"=>true]); $this->data = []; if(is_array($content) && !empty($content)) { if($this->sort) { ksort($this->data); } foreach($content as $k=>$v) { $this->set($k,$v); } } return $this; } public function save() { if($this->sort) { ksort($this->data); } if($this->prettyJson) { $this->file->write(json_encode($this,JSON_PRETTY_PRINT)); } else { $this->file->write(json_encode($this)); } return $this; } }src/filesys/DirDiff.php000064400000010760144761607200011044 0ustar00directory = $directory; $this->files = $directory->files(); if(isset($this->files[$this->directory->getMetaFileName()])) { unset($this->files[$this->directory->getMetaFileName()]); } $this->diff($meta); } public function diff($meta=null) { if(is_null($meta)) { $meta = $this->directory->meta(); } if(!$this->hasMetaData($meta)) { $this->new = array_keys($this->files); } else { $this->metaFiles = $meta["files"]; foreach($this->files as $fileName=>$file) { if(!isset($this->metaFiles[$fileName])) { $this->new[] = $fileName; } } foreach($this->metaFiles as $fileName=>$info) { if(!isset($this->files[$fileName])) { $this->missing[] = $fileName; } elseif($info["sha1"] != $this->files[$fileName]->sha1()) { $this->changed[] = $fileName; } else { $this->unchanged[] = $fileName; } } } if(!empty($this->new)) { sort($this->new); } if(!empty($this->missing)) { sort($this->missing); } if(!empty($this->changed)) { sort($this->changed); } if(!empty($this->unchanged)) { sort($this->unchanged); } } private function hasMetaData($meta) { if(is_null($meta) || empty($meta) || (is_array($meta) && !isset($meta["files"])) || (is_array($meta) && empty($meta["files"]))) { return false; } return true; } /** @return File|array|false */ public function getFile($fileName) { if(isset($this->files[$fileName])) { return $this->files[$fileName]; } elseif(isset($this->metaFiles[$fileName])) { return $this->metaFiles[$fileName]; } return false; } /** @return array|false */ public function getNewOrChanged() { $merged = array_unique(array_merge( is_array($this->new) ? $this->new : [], is_array($this->changed) ? $this->changed : [] )); sort($merged); return !empty($merged) ? $merged : false; } public function commitNew($pretty=false) { if($this->getNew() !== false) { foreach($this->getNew() as $fileName) { $this->directory->metaAdd($this->getFile($fileName)); } $this->directory->metaCommit(false,$pretty); $this->diff(); } } public function commitChanged($pretty=false) { if($this->getChanged() !== false) { foreach($this->getChanged() as $fileName) { $this->directory->metaAdd($this->getFile($fileName)); } $this->directory->metaCommit(false,$pretty); $this->diff(); } } public function commitMissing($pretty=false) { if($this->getMissing() !== false) { foreach($this->getMissing() as $fileName) { $this->directory->metaDel($fileName); } $this->directory->metaCommit(false,$pretty); $this->diff(); } } public function commitAll($pretty=false) { $this->directory->metaCommit(true,$pretty); $this->diff(); } /** * Get the value of new * * @return array|false */ public function getNew() { return !empty($this->new) ? $this->new : false; } /** * Get the value of missing * * @return array|false */ public function getMissing() { return !empty($this->missing) ? $this->missing : false; } /** * Get the value of changed * * @return array|false */ public function getChanged() { return !empty($this->changed) ? $this->changed : false; } /** * Get the value of unchanged * * @return array */ public function getUnchanged() { return $this->unchanged; } }src/filesys/Directory.php000064400000032766144761607200011513 0ustar00null, "name"=>null, "scanned"=>0 ]; protected $path; protected $name; protected $pathTrimmed; protected $separator = DIRECTORY_SEPARATOR; protected $files = []; protected $dirs = []; protected $expandSymlink = false; /** @var File */ protected $metaFile; protected $meta; protected $metaFileName = ".bmeta"; protected $metaPerms = "0600"; protected $scanned = false; protected $create = false; /** * Valid options: * * path (default:current directory) path of the directory to load * * scan (default:true) * * recursive (default:false) * * expandSymlink (default:false) * * create (default:false) - create the directory (recursively) if it doesn't exist * * metaFile - sets the filename of the meta file (if meta is saved), default is .bmeta * * metaPerms - sets the file perms of the meta file (if meta is saved), default is 0600 */ public function __construct($options=[]) { $path = Dot::getVal($options,"path",null); $scan = Dot::getVal($options,"scan",true); $recursive = Dot::getVal($options,"recursive",false); $this->create = Dot::getVal($options,"create",false); $this->expandSymlink = Dot::getVal($options,"expandSymlink",false); if(($metaFileName = Dot::getVal($options,"metaFile",false)) !== false) { $this->metaFileName = $metaFileName; } if(($metaPerms = Dot::getVal($options,"metaPerms",false)) !== false) { $this->metaPerms = $metaPerms; } if(!is_null($path)) { $this->setDirectory($path); } else { $this->setDirectory(getcwd()); } if($scan && is_dir($this->path())) { $this->scan($recursive); } } public function isScanned() { return $this->get("scanned",0) > 0 ? true : false; } /** @return DirDiff */ public function metaDiff() { return new DirDiff($this,$this->meta()); } /** Commits meta to file. If no meta exists, it is generated. * @param bool $all if true, overwrites existing meta with current file meta * @return array|null * */ public function metaCommit($all=false,$pretty=true) { $meta = $this->meta(); if(is_null($meta) || empty($meta) || $all) { $this->metaClear(); if(!$this->isScanned()) { $this->scan(); } foreach($this->files() as $file) { $meta["files"][$file->name()]=$file->meta(); } } $meta["generated"] = date("U"); $meta["path"] = $this->path(); if(!isset($meta["files"])) { $meta["files"]=[]; } if(isset($meta["files"][$this->metaFileName])) { unset($meta["files"][$this->metaFileName]); } ksort($meta["files"]); if($pretty) { $this->metaFile->write(json_encode($meta,JSON_PRETTY_PRINT)); } else { $this->metaFile->write(json_encode($meta)); } return $this->meta(true); } public function metaAdd($fileOrName) { $this->meta(); if(($file = $this->getFileFromFileOrName($fileOrName)) !== false) { $this->meta["files"][$file->name()] = $file->meta(); } return $this; } public function metaDel($fileOrName) { $this->meta(); if(($fileName = $this->getFileFromFileOrName($fileOrName,true)) !== false) { if(isset($this->meta[$fileName])) { unset($this->meta[$fileName]); } } return $this; } protected function getFileFromFileOrName($fileOrName,$name=false) { if(!is_object($fileOrName)) { $file = FileUtil::fileIfExists($fileOrName); if($file !== false) { return $name ? $file->name() : $file; } return false; } return $name ? $fileOrName->name() : $fileOrName; } /** @return array|null */ public function meta($reload=false) { if(is_null($this->metaFile)) { $this->metaFile = new File(["path"=>$this->path($this->metaFileName)]); $this->metaFile->perms($this->metaPerms); } if(is_null($this->meta) || $reload) { $this->metaClear(); $this->meta = $this->metaFile->content(["json"=>true]); if(isset($this->meta["path"]) && $this->meta["path"] != $this->path()) { $this->metaClear(); } } return $this->meta; } protected function metaClear() { $this->meta = ["generated"=>date("U"),"path"=>$this->path(),"files"=>[]]; } public function path($fileOrDirName=null) { $path = $this->get("path",null); if(is_null($path)) { return null; } if(!is_null($fileOrDirName)) { return $path.$this->separator.$fileOrDirName; } return $path; } public function name() { return $this->get("name",null); } public function files($objects=true) { if($objects) { return $this->files; } $arr = []; foreach($this->files as $file) { $arr[$file->name()] = $file->get(); } return $arr; } public function dirs($objects=true) { if($objects) { return $this->dirs; } $arr = []; foreach($this->dirs as $dir) { $arr[$dir->name()] = $dir->get(); } return $arr; } public function file($name=null) { return $this->_getFile($name); } public function dir($name=null) { return $this->_getDir($name); } public function createFile($name,$touchOnExists=true) { $parts = explode($this->separator,$name); $name = array_pop($parts); $filename = $this->path().$this->separator.$name; if(file_exists($filename)) { if(!$touchOnExists) { throw new \Exception("File already exists ".$name,-1); } else { touch($filename); $file = new File([ "path"=>$filename, "expandSymlink"=>$this->expandSymlink, ]); } } else { touch($filename); $file = new File([ "path"=>$filename, "expandSymlink"=>$this->expandSymlink, ]); $this->_addFile($name,$file); } clearstatcache(); return $file; } public function scan($recursive=false) { if($handle = opendir($this->path())) { while (false !== ($entry = readdir($handle))) { if($entry != "." && $entry != "..") { if(is_dir($this->path($entry))) { $dir = new Directory([ "path"=>$this->path($entry), "scan"=>$recursive, "recursive"=>$recursive, "expandSymlink"=>$this->expandSymlink, ]); $this->_addDir($entry,$dir); } else { $file = new File([ "path"=>$this->path($entry), "expandSymlink"=>$this->expandSymlink, ]); $this->_addFile($entry,$file); } } } } $size=0; foreach($this->files as $file) { $size+=!is_null($file->size()) ? $file->size() : 0; } $this->set("fileSize",$size); $this->set("scanned",time()); return $this; } /** * * @param array $findOptions * @param array $outputOptions objects=true,fullPathName=false,callback=null * @return File[]|string[]|false */ public function find($findOptions=[],$outputOptions=[]) { $opts = ["depth","mindepth","maxdepth","noleaf","mount","xdev"]; $tests = ["amin","atime","cmin","ctime","empty","false","fstype","gid","group","ilname","iname","inum","iwholename","iregex","links","lname","mmin","mtime","name","newer","nouser","not","nogroup","path","perm","regex","readable","writable","executable","wholename","size","true","type","uid","used","user","xtype","context"]; $cmd = "find ".$this->path()." -maxdepth 1"; $setOpts = ["maxdepth"=>1]; $setTests = ["type"=>"f"]; foreach($findOptions as $optName=>$optVal) { if(in_array($optName,$opts)) { $setOpts[$optName] = $optVal; } elseif(in_array($optName,$tests)) { $setTests[$optName] = $optVal; } } $parts = []; if(!empty($setOpts)) { foreach($setOpts as $optName=>$optVal) { if(is_array($optVal)) { foreach($optVal as $optValVal) { $parts[] = "-".$optName." ".$optValVal; } } else { $parts[] = "-".$optName." ".$optVal; } } } if(!empty($setTests)) { foreach($setTests as $optName=>$optVal) { if(is_array($optVal)) { foreach($optVal as $optValVal) { $parts[] = "-".$optName." ".$optValVal; } } else { $parts[] = "-".$optName." ".$optVal; } } } if(!empty($parts)) { $cmd.=" ".implode(" ",$parts); } $output = []; $files = []; //echo $cmd."\n"; $r = exec($cmd,$output); $objects = Dot::getVal($outputOptions,"objects",true); $fullPathName = Dot::getVal($outputOptions,"fullPathName",false); if(!empty($output)) { foreach($output as $filePath) { if($objects) { $file = new File(["path"=>$filePath]); if($fullPathName) { $files[$filePath] = $file; } else { $files[$file->name()] = $file; } } else { $files[] = $filePath; } } } return !empty($files) ? $files : false; } /** @return File|null */ public function getMetaFile() { return $this->metaFile; } /** @return string */ public function getMetaFileName() { return $this->metaFileName; } /** * Set the value of path * * @return self */ public function setDirectory($path) { $pathObj = new BasePath($path,$this->expandSymlink); if(!file_exists($pathObj->fullPath()) && $this->create) $pathObj->makeDir(); $this->set("path",$pathObj->fullPath($this->expandSymlink)); $this->set("pathObj",$pathObj); $pathTrimmed = substr($this->get("path"), strlen($this->separator)); //$this->set("pathTrimmed",$pathTrimmed); $arr = explode($this->separator,$pathTrimmed); $this->set("name",array_pop($arr)); return $this; } /** * Get the value of separator */ public function getSeparator() { return $this->separator; } /** * Set the value of separator * * @return self */ public function setSeparator($separator) { $this->separator = $separator; return $this; } public function _addFile($name,$file=null) { if(is_null($file)) { $file = new File([ "path"=>$this->path.$this->separator.$name ]); } $this->setArray($this->files,$name,$file,false); return $this; } public function _getFile($name=null) { return $this->getArray($this->files,$name,false); } public function _addDir($name,$dir=null) { if(is_null($dir)) { $dir = new Directory([ "path"=>$this->path.$this->separator.$name ]); } $this->setArray($this->dirs,$name,$dir,false); return $this; } public function _getDir($name=null) { return $this->getArray($this->dirs,$name,false); } public static function fromPathString($pathString,$createIfNoExist=true,$options=[]) { if(!file_exists($pathString) && !$createIfNoExist) { return false; } if(!isset($options["create"])) { $options["create"] = $createIfNoExist; } $options["path"] = $pathString; return new self($options); } public static function fromInput($pathOrDirObject,$createIfNoExist=true,$options=[]) { if(is_object($pathOrDirObject) && $pathOrDirObject instanceof self) { return $pathOrDirObject; } return static::fromPathString($pathOrDirObject,$createIfNoExist,$options); } }src/filesys/File.php000064400000042151144761607200010413 0ustar00null, "name"=>null, "size"=>null, "type"=>null, "mtime"=>null, "ctime"=>null, "atime"=>null, "perms"=>null, ]; protected $content; protected $stream; protected $separator = DIRECTORY_SEPARATOR; protected $expandSymlink = false; protected $create = true; protected $maxAge = false; protected $readMeta = true; /** * Opens a File Object instance on a file. This object provides easy access to read/write/meta information of a file. * * Valid options * * string "path" - (required) - the path, including the filename, of hte file to load * * bool "readMeta" - (default:true) - pre-read basic meta information of the file if it exists * * bool "create" - (default:true) - create a file, including subdirs, if the path/filename does not exist * * bool "overwrite" - (default:false) - overwrite an existing file if it already exists, creating a new file * * string "content" - (optional) - Content to write to the file. If overwrite=false, it is appended to the file if the file exists. * * int "maxAge" - in seconds, if ctime is older than maxAge ago, replace the file */ public function __construct($options=[]) { $path = Dot::getVal($options,"path",null); $this->readMeta = Dot::getVal($options,"readMeta",true); $this->create = Dot::getVal($options,"create",true); $this->maxAge = Dot::getVal($options,"maxAge",false); $overwrite = Dot::getVal($options,"overwrite",false); $content = Dot::getVal($options,"content",null); $this->expandSymlink = Dot::getVal($options,"expandSymlink",false); //$path="",$readMeta=true,$createIfNotExist=false if(!is_null($path)) { if(is_dir($path)) { throw new \Exception("$path is a directory and cannot be loaded as a File instance. Please use a Directory instance instead"); } if(is_link($path) && !$this->expandSymlink) { //$path = } $this->setFile($path); if(file_exists($path) && !is_dir($path)) { if($overwrite) { if(!is_null($content)) { $this->write($content,false); } else { if(($h = fopen($path,"w")) !== false) { fclose($h); } } } else { if(!is_null($content)) { $this->write($content,true); } } if($this->readMeta) { $this->readMeta(); } } else { throw new \Exception("$path does not exist."); } } } public function __destruct() { $this->streamClose(); } public function path() { return $this->get("path",null); } public function name() { return $this->get("name",null); } public function dirPath() { return $this->get("dirPath",null); } public function type() { if(is_null($this->get("type",null))) { if(($type = filetype($this->path())) !== false) { $this->set("type",$type); } } return $this->get("type",null); } public function mimeType() { if(is_null($this->get("mimeType",null))) { if(($mimeType = mime_content_type($this->path())) !== false) { $this->set("mimeType",$mimeType); } } return $this->get("mimeType",null); } public function size() { if(is_null($this->get("size",null))) { if(($size = filesize($this->path())) !== false) { $this->set("size",$size); } } return $this->get("size",null); } public function mtime($timeFormat="timestamp") { if(is_null($this->get("mtime",null))) { if(($mtime = filemtime($this->path())) !== false) { $this->set("mtime",$mtime); } } return $this->timeFormat($this->get("mtime",null),$timeFormat); } public function ctime($timeFormat="timestamp") { if(is_null($this->get("ctime",null))) { if(($ctime = filectime($this->path())) !== false) { $this->set("ctime",$ctime); } } return $this->timeFormat($this->get("ctime",null),$timeFormat); } public function atime($timeFormat="timestamp") { if(is_null($this->get("atime",null))) { if(($atime = fileatime($this->path())) !== false) { $this->set("atime",$atime); } } return $this->timeFormat($this->get("atime",null),$timeFormat); } public function meta() { return [ "mtime"=>$this->mtime("U"), "size"=>$this->size(), "sha1"=>$this->sha1(), "type"=>$this->type(), ]; } /** * * @param null|int if set, chmod's the file.. prefix with 0 (eg 0600 or 0777) * @return int|null */ public function perms($newPerms=null) { if(!is_null($newPerms)) { chmod($this->path(),intval($newPerms, 8)); clearstatcache(true,$this->path()); if(($perms = fileperms($this->path())) !== false) { $perms = decoct($perms & 0777); $this->set("perms",$perms); } } if(is_null($this->get("perms",null))) { if(($perms = fileperms($this->path())) !== false) { $perms = decoct($perms & 0777); $this->set("perms",$perms); } } return $this->get("perms",null); } public function sha1() { if(is_null($this->get("sha1",null))) { if(($size = sha1_file($this->path())) !== false) { $this->set("sha1",$size); } } return $this->get("sha1",null); } public function sha1Content($useStream=false) { if(is_null($this->get("sha1",null))) { if($useStream) { $stream = $this->streamOpen("r"); $context = hash_init("sha1"); hash_update_stream($context,$stream); $hash = hash_final($context); $this->streamClose(); $this->set("sha1",$hash); } else { $hash = sha1($this->content()); $this->set("sha1",$hash); } } return $this->get("sha1"); } public function delete($recreate=false) { unlink($this->path()); $this->clearMeta(); if($recreate) { touch($this->path()); if($this->readMeta) { $this->readMeta(); } } } public function streamOpen($mode="a+") { if(is_null($this->stream)) { $this->stream = fopen($this->path(),$mode); } return $this->stream; } public function streamClose() { if(!is_null($this->stream)) { fclose($this->stream); } $this->stream = null; } public function streamRead() { if(is_null($this->stream)) { throw new \Exception("Stream not open, use File::streamOpen()"); } return fread($this->stream,$this->size()); } public function streamWrite($content,$truncate=false) { if(is_null($this->stream)) { throw new \Exception("Stream not open, use File::streamOpen()"); } if($truncate) { ftruncate($this->stream,0); } fwrite($this->stream,$content); fflush($this->stream); return $this; } public function lock($exclusive=true,$timeout=0) { if(is_null($this->stream)) { throw new \Exception("Stream not open, use File::streamOpen()"); } if($timeout <= 0) { $lockMode = $exclusive ? LOCK_EX : LOCK_SH; flock($this->stream,$lockMode); } else { $time = microtime(true); $lockMode = $exclusive ? LOCK_EX | LOCK_NB : LOCK_SH | LOCK_NB; while(!flock($this->stream,$lockMode)) { usleep(round(rand(0, 100)*1000)); if(microtime(true) - $time > $timeout) { return false; } } } return true; } public function unlock() { if(is_null($this->stream)) { throw new \Exception("Stream not open, use File::streamOpen()"); } if(!is_null($this->stream)) { flock($this->stream,LOCK_UN); } return true; } /** * Reads the contents of the file, passes it into $contentCallback, then writes the return from $contentCallback back into the file * @param callable $contentCallback * @return string The content of the file after the operation has completed */ public function atomicReadWrite(callable $contentCallback,$timeout=10) { if(class_exists('\Memcached')) { $time = microtime(true); while(dhGlobal::cache()->lockVar($this->name()) === false) { usleep(round(rand(0, 100)*1000)); if(microtime(true) - $time > $timeout) { return false; } } $content = $contentCallback($this->content(["save"=>false])); $this->write($content); dhGlobal::cache()->unlockVar($this->name()); } else { throw new \Exception("Please install Memcached"); return false; if(!is_callable($contentCallback)) { throw new \Exception("contentCallback must be callable"); } $this->streamClose(); $this->streamOpen("r+"); if($this->lock(true,$timeout)) { $content = $contentCallback($this->streamRead()); rewind($this->stream); $this->streamWrite($content,true); $this->unlock(); $this->streamClose(); return true; } return false; } } /** * Retrieve the content of the file * * Return the raw contents (default), or use options to transform it. * * Options: * * json - boolean (default:false), parse the contents as json into an array. * * jsonObject - boolean (default:false), parse the contents as json into an object. * * filter - callable, filter/modify the content before returning it through json/jsonObject/plain * * save - boolean (default:true), save (cache) the raw content to the object.. not added to the get() output. */ public function content($options=[]) { $json = Dot::getVal($options,"json",false); $jsonObject = Dot::getVal($options,"jsonObject",false); $lineFilter = Dot::getVal($options,"lineFilter",false); $lineEnding = Dot::getVal($options,"lineEnding",PHP_EOL); $filter = Dot::getVal($options,"filter",false); $save = Dot::getVal($options,"save",true); if(!is_null($this->content) && $save) { $content = $this->content; } else { $content = file_get_contents($this->path()); if($save) { $this->content = $content; } } if(!is_null($lineFilter) && is_callable($lineFilter)) { $newContent = []; foreach(explode($lineEnding,$content) as $line) { if(($line = $lineFilter($line)) !== false) { $newContent[] = $line; } } $content = implode($lineEnding,$newContent); } if(!is_null($filter) && is_callable($filter)) { $content = $filter($content); } if($json) { return json_decode($content,true); } elseif($jsonObject) { return json_decode($content); } else { return $content; } } public function write($content,$append=false) { if($append) { file_put_contents($this->path(),$content,FILE_APPEND); } else { file_put_contents($this->path(),$content); } } protected function timeFormat($timestampValue,$format="timestamp") { if(empty($timestampValue) || is_null($timestampValue) || $timestampValue <= 0) { return $timestampValue; } if(empty($format) || is_null($format)) { return $timestampValue; } try { $dt = new \DateTime(date("Y-m-d H:i:s",$timestampValue),new \DateTimeZone(date_default_timezone_get())); } catch (\Exception $e) { return $timestampValue; } if(!is_array($format)) { $objVals = ["datetime","dt","object"]; if($format == "timestamp") { return $timestampValue; } elseif($format == "millis") { return $timestampValue*1000; } elseif(in_array(strtolower($format),$objVals)) { return $dt; } else { try { $out = $dt->format($format); } catch (\Exception $e) { $out = $timestampValue; } return $out; } } else { if(count($format) >= 2) { if(isset($format["format"]) && isset($format["timezone"])) { $fmt = $format["format"]; $tz = $format["timezone"]; } else { $fmt = $format[0]; $tz = $format[1]; } try { $dt->setTimezone(new \DateTimeZone($tz)); $out = $dt->format($fmt); } catch (\Exception $e) { $out = $timestampValue; } return $out; } return $timestampValue; } return $timestampValue;//shouldn't get here.. but just in case } /** * Set the value of path * * @return self */ public function setFile($path,$readMeta=true,$root=null) { $pathObj = new BasePath($path,$this->expandSymlink); if(!file_exists($path) && $this->create) { $pathObj->makeFile(); } if(file_exists($path)) { $this->set("path",$pathObj->fullPath($this->expandSymlink)); } else { throw new \Exception("File not found ".$path,-1); } if($this->maxAge !== false) { if($this->ctime() < time()-($this->maxAge)) { $this->delete(true); } } $pathTrimmed = substr($this->get("path"), strlen($this->separator)); //$this->set("pathTrimmed",$pathTrimmed); $arr = explode($this->separator,$pathTrimmed); $this->set("name",array_pop($arr)); $this->set("dirPath",$this->separator.implode($this->separator,$arr)); return $this; } public function readMeta() { if(file_exists($this->path())) { $this->size(); $this->type(); $this->mtime(); $this->atime(); $this->ctime(); $this->perms(); $this->mimeType(); } return $this; } public function clearMeta() { $this->set("size",null); $this->set("type",null); $this->set("mtime",null); $this->set("atime",null); $this->set("ctime",null); $this->set("perms",null); $this->set("mimeType",null); } public function reloadMeta() { $this->clearMeta(); $this->readMeta(); } public static function fromPathString($pathString,$createIfNoExist=true,$options=[]) { if(!file_exists($pathString) && !$createIfNoExist) { return false; } if(!isset($options["create"])) { $options["create"] = $createIfNoExist; } $options["path"] = $pathString; return new self($options); } public static function fromInput($pathOrFileObject,$createIfNoExist=true,$options=[]) { if(is_object($pathOrFileObject) && $pathOrFileObject instanceof self) { return $pathOrFileObject; } return static::fromPathString($pathOrFileObject,$createIfNoExist,$options); } public static function from($pathOrFileObject,$createIfNoExist=true,$options=[]) { return static::fromInput($pathOrFileObject,$createIfNoExist,$options); } }src/guzzle/Request.php000064400000000367144761607200011031 0ustar00headers = $headers; } if(($body = dhGlobal::getVal($options,"body",false)) !== false) { $this->body = $body; } if(($onSuccess = dhGlobal::getVal($options,"onSuccess",false)) !== false) { $this->onSuccess = $onSuccess; } elseif(($onSuccess = dhGlobal::getVal($options,"success",false)) !== false) { $this->onSuccess = $onSuccess; } if(($onError = dhGlobal::getVal($options,"onError",false)) !== false) { $this->onError = $onError; } elseif(($onError = dhGlobal::getVal($options,"error",false)) !== false) { $this->onError = $onError; } if(($async = dhGlobal::getVal($options,"async",null)) !== null) { $this->async = $async ? true : false; } if(($timeout = dhGlobal::getVal($options,"timeout",false)) !== false) { $this->timeout = $timeout; } } public function timeout($timeout=null) { $this->timeout = $timeout; return $this; } public function async($async=true) { $this->async = $async; return $this; } public function synchronous($sync=true) { $this->async(!$sync); return $this; } public function header($headerName,$headerValue=null) { $this->headers[$headerName] = $headerValue; return $this; } public function body($keyPath,$value=null,$separator=".") { dhGlobal::dotAssign($this->body,$keyPath,$value,$separator); return $this; } public function bodyFromData($data) { if(!is_array($data)) { $check = json_decode($data,true); if(is_array($check)) { $data = $check; } } if(is_array($data)) { foreach($data as $k=>$v) { $this->body($k,$v); } } else { $this->body = $data; } } public function json($data=null) { $this->header('Content-Type','application/json'); if(!is_null($data)) { $this->bodyFromData($data); } return $this; } public function form($data) { $this->header('Content-Type','application/x-www-form-urlencoded'); if(!is_null($data)) { $this->bodyFromData($data); } return $this; } public function raw($data) { $this->body = $data; return $this; } public function url($url) { $this->url = $url; return $this; } public function get($url=null) { if(!is_null($url)) { $this->url($url); } return $this->send("get"); } public function post($url=null) { if(!is_null($url)) { $this->url($url); } return $this->send("post"); } public function put($url=null) { if(!is_null($url)) { $this->url($url); } return $this->send("put"); } public function patch($url=null) { if(!is_null($url)) { $this->url($url); } return $this->send("patch"); } public function delete($url=null) { if(!is_null($url)) { $this->url($url); } return $this->send("delete"); } public function head($url=null) { if(!is_null($url)) { $this->url($url); } return $this->send("head"); } /** * * @param mixed $method * @return \React\Promise\ExtendedPromiseInterface * @throws InvalidArgumentException * @throws Exception * @throws Throwable * @throws UnexpectedValueException */ public function send($method) { $method = strtolower($method); if(is_null($this->body)) { $this->body = ""; } if($method == "get") { $promise = dAsyncHttp::get($this->url,$this->headers,$this->onSuccess,$this->onError,$this->timeout); } elseif($method == "post") { $promise = dAsyncHttp::post($this->url,$this->headers,$this->body,$this->onSuccess,$this->onError,$this->timeout); } elseif($method == "put") { $promise = dAsyncHttp::put($this->url,$this->headers,$this->body,$this->onSuccess,$this->onError,$this->timeout); } elseif($method == "patch") { $promise = dAsyncHttp::patch($this->url,$this->headers,$this->body,$this->onSuccess,$this->onError,$this->timeout); } elseif($method == "delete") { $promise = dAsyncHttp::delete($this->url,$this->headers,$this->body,$this->onSuccess,$this->onError,$this->timeout); } elseif($method == "head") { $promise = dAsyncHttp::head($this->url,$this->headers,$this->onSuccess,$this->onError,$this->timeout); } return $promise; } public function toArray() { return [ "url"=>$this->url, "headers"=>$this->headers, "body"=>$this->body, "timeout"=>$this->timeout ]; } }src/http/Request.php000064400000011216144761607200010463 0ustar00parent = &$parent; } if(!empty($method)) { $this->setMethod($method); } if(!empty($url)) { $this->setUrl($url); } } /** * * @return \boru\dhutils\guzzle\Response */ public function send() { return $this->parent->send($this); } public function run() { return $this->send(); } public function replace($opts=[]) { $this->data = $opts; } protected function mset($primary,$key,$value=null,$append=false) { if(is_array($key) && is_null($value)) { foreach($key as $k=>$v) { $this->set($primary.".".$k,$v,$append); } } else { $this->set($primary.".".$key,$value,$append); } } public function auth($user,$pass) { $this->set(\GuzzleHttp\RequestOptions::AUTH,[$user,$pass]); return $this; } public function authBasic($user,$pass) { $this->set(\GuzzleHttp\RequestOptions::AUTH,[$user,$pass]); return $this; } public function authToken($token) { $this->mset(\GuzzleHttp\RequestOptions::HEADERS,"Authorization",$token); return $this; } public function authBearer($token) { $this->mset(\GuzzleHttp\RequestOptions::HEADERS,"Authorization","Bearer ".$token); return $this; } public function header($key,$value) { $this->mset(\GuzzleHttp\RequestOptions::HEADERS,$key,$value); return $this; } public function json($key,$value=null) { $this->mset(\GuzzleHttp\RequestOptions::JSON,$key,$value); return $this; } public function rawBody($body) { $this->set(\GuzzleHttp\RequestOptions::BODY,$body); return $this; } public function body($key,$value=null) { if(is_null($value)) { return $this->rawBody($key); } $this->mset(\GuzzleHttp\RequestOptions::BODY,$key,$value); return $this; } public function debug($value=true) { $this->set(\GuzzleHttp\RequestOptions::DEBUG,$value); return $this; } public function ipResolve($value="ipv4") { $this->set(\GuzzleHttp\RequestOptions::FORCE_IP_RESOLVE,$value); return $this; } public function form($key,$value=null) { $this->mset(\GuzzleHttp\RequestOptions::FORM_PARAMS,$key,$value); return $this; } public function multipart($value) { $mp = $this->get(\GuzzleHttp\RequestOptions::MULTIPART,[]); $mp[] = $value; $this->set(\GuzzleHttp\RequestOptions::MULTIPART,$mp); return $this; } public function query($key,$value=null) { $this->mset(\GuzzleHttp\RequestOptions::QUERY,$key,$value); return $this; } public function sink($value) { $this->set(\GuzzleHttp\RequestOptions::SINK,$value); return $this; } public function stream($value=true) { $this->set(\GuzzleHttp\RequestOptions::STREAM,$value); return $this; } public function verify($value=false) { $this->set(\GuzzleHttp\RequestOptions::VERIFY,$value); return $this; } public function timeout($value=0) { $this->set(\GuzzleHttp\RequestOptions::TIMEOUT,$value); return $this; } /** * Get the value of parent */ public function getParent() { return $this->parent; } /** * Set the value of parent * * @return self */ public function setParent($parent) { $this->parent = &$parent; return $this; } /** * Get the value of method */ public function getMethod() { return $this->method; } /** * Set the value of method * * @return self */ public function setMethod($method) { $this->method = $method; return $this; } /** * Get the value of url */ public function getUrl() { return $this->url; } /** * Set the value of url * * @return self */ public function setUrl($url) { $this->url = $url; return $this; } }src/http/Response.php000064400000007332144761607200010635 0ustar00response = $response; $this->container = new \boru\dhutils\base\dhObject($extraAttributes); } public function code() { return $this->getStatusCode(); } public function phrase() { return $this->getReasonPhrase(); } public function header($header=null,$default=false) { if(is_null($header)) { return $this->getHeaders(); } if(!$this->hasHeader($header)) { return $default; } return $this->getHeader($header); } public function headerLine($header,$default=false) { if(!$this->hasHeader($header)) { return $default; } else { return $this->getHeaderLine($header); } } public function attr($attr,$default=false) { return $this->container->get($attr,$default); } public function attrib($attr,$default=false) { return $this->container->get($attr,$default); } public function attribute($attr,$default=false) { return $this->container->get($attr,$default); } public function setAttr($attr,$val) { $this->container->set($attr,$val); return $this; } /** * * @param bool $json * @param bool $arr * @return mixed */ public function body($json=false,$arr=true) { if($json) { return json_decode($this->asString(),$arr); } else { return $this->asString(); } } public function asString() { if(is_null($this->bodyString)) { $this->bodyString = (string) $this->getBody()->__toString(); } return $this->bodyString; } public function asArray() { return $this->body(true); } public function asObject() { return $this->body(true,false); } //ResponseInterface methods: public function getStatusCode() { return $this->response->getStatusCode(); } public function withStatus($code, $reasonPhrase = '') { return $this->response->getStatusCode($code, $reasonPhrase); } public function getReasonPhrase() { return $this->response->getReasonPhrase(); } public function getProtocolVersion() { return $this->response->getProtocolVersion(); } public function withProtocolVersion($version) { return $this->response->withProtocolVersion($version); } public function getHeaderLine($name) { return $this->response->getHeaderLine($name); } public function withHeader($name, $value) { return $this->response->withHeader($name, $value); } public function withAddedHeader($name, $value) { return $this->response->withAddedHeader($name, $value); } public function withoutHeader($name) { return $this->response->withoutHeader($name); } public function hasHeader($header) { return $this->response->hasHeader($header); } public function getHeader($header) { return $this->response->getHeader($header); } public function getHeaders() { return $this->response->getHeaders(); } public function getBody() { return $this->response->getBody(); } public function withBody(\Psr\Http\Message\StreamInterface $body) { return $this->response->withBody($body); } }src/httplite/client/Params.php000064400000005501144761607200012412 0ustar00data = []; if(!empty($format)) { $this->format($format); } } public function getAll($format="") { if(empty($format)) { $format = $this->format; } if($format == "json") { return json_encode($this->data); } elseif($format == "url" || $format == "form") { return http_build_query($this->data); } elseif($format == "header") { $d = []; foreach($this->data as $k=>$v) { $d[] = $k.": ".$v; } return $d; } else { return $this->data; } } public function format($format) { $this->format = $format; } public function pos() { return count($this->data); } public static function fromArray($array,$format="form") { $params = new self($format); if($format == "header" && !dhGlobal::isAssoc($array)) { foreach($array as $k=>$v) { $temp = explode(": ",$v,2); if(count($temp)==2) { $v = isset($temp[1]) ? $temp[1] : ""; $params->set($temp[0],$v); } else { $params->set($k,$v); } } } else { foreach($array as $k=>$v) { $params->set($k,$v); } } return $params; } public static function fromJSON($json,$format="form") { $array = json_decode($json,true); self::FromArray($array,$format); } /** * Header param specifc shortcut for making auth easy */ public function auth($type="Basic",...$params) { $token = ""; $parts = $params; if(!empty($type)) { array_unshift($parts,$type); } switch(strtolower($type)) { case "basic": if(count($params)===1) { $token = "Basic ".$params[0]; } if(count($params)==2) { $token = "Basic ".base64_encode(implode(":",$params)); } break; default: $token = implode(" ",$parts); break; } $this->set("Authorization",$token); } private function isNumericArray($arr) { foreach($arr as $k=>$v) { if($k !== (int) $k) { return false; } } return true; } }src/httplite/client/Request.php000064400000016354144761607200012627 0ustar00headers = $headers; } elseif(is_array($headers)) { $this->headers = Params::fromArray($headers,"header"); } else { if(is_null($this->headers)) { $this->headers = new Params("header"); } $this->headers->set($headers,$val); } return $this; } public function param($params=[],$val=null) { if(is_object($params)) { $this->params = $params; } elseif(is_array($params)) { $this->params = Params::fromArray($params,$this->bodyType); } else { if(is_null($this->params)) { $this->params = new Params($this->bodyType); } $this->headers->set($params,$val); } return $this; } public function url($url) { $this->url = $url; return $this; } public function method($method) { $this->method = $method; return $this; } public function sslVerify($sslVerify) { $this->sslVerify = $sslVerify; return $this; } public function verbose($verbose) { $this->verbose = $verbose; return $this; } public function returnType($type=false) { $this->returnType = $type; return $this; } public function sendType($type="json") { $this->bodyType = $type; return $this; } public function sendJson() { $this->bodyType = "json"; return $this; } public function sendForm() { $this->bodyType = "form"; return $this; } public function auth($type="Basic",...$params) { $token = ""; $parts = $params; if(!empty($type)) { array_unshift($parts,$type); } switch(strtolower($type)) { case "basic": if(count($params)===1) { $token = "Basic ".$params[0]; } if(count($params)==2) { $token = "Basic ".base64_encode(implode(":",$params)); } break; default: $token = implode(" ",$parts); break; } $this->header("Authorization",$token); } public function call() { if(is_null($this->headers)) { $this->headers = new Params("header"); } if(is_null($this->params)) { $this->params = new Params("form"); } $ch = curl_init(); $url = $this->url; curl_setopt($ch, CURLOPT_URL, $url); if($this->verbose === true) { curl_setopt($ch, CURLOPT_VERBOSE, $this->verbose); } curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers->getAll()); curl_setopt($ch, CURLOPT_HEADER,true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->sslVerify); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); switch(strtoupper($this->method)) { case 'GET': curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($this->method)); if(!empty($this->params)) { curl_setopt($ch, CURLOPT_POSTFIELDS, $this->params->getAll()); } break; case 'POST': curl_setopt($ch, CURLOPT_POST, true); if(!empty($this->params)) { curl_setopt($ch, CURLOPT_POSTFIELDS, $this->params->getAll()); } break; case 'DELETE': case 'PUT': case 'OPTIONS': default: curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($this->method)); if(!empty($this->params)) { curl_setopt($ch, CURLOPT_POSTFIELDS, $this->params->getAll()); } } dhGlobal::log("info","dhHttp request to ".$url." started"); $timingStart = microtime(true); $response = curl_exec($ch); $timingEnd = microtime(true); $timingExec = $timingEnd - $timingStart; if(curl_errno($ch)){ $this->setResponse([ "error"=>curl_errno($ch), "message"=>curl_error($ch), "code"=>-1, "headers"=>null, "body"=>null, "execTime"=>$timingExec ]); } else { $info = curl_getinfo($ch); curl_close($ch); $this->setResponse([ "error"=>false, "message"=>false, "code"=>$info["http_code"], "headers"=>trim(substr($response,0,$info['header_size'])), "body"=>substr($response,$info['header_size']), "execTime"=>$timingExec ]); } return $this->getResponse(); } /** * Get the value of response */ public function getReturnType() { return $this->returnType; } /** * Set the value of response * * @return self */ public function setReturnType($returnType) { $this->returnType = $returnType; return $this; } /** * Get the value of response */ public function getResponse($type=null) { if(is_null($this->response)) { return false; } if(is_null($type)) { $type = $this->returnType; } if($type == "object") { if($this->response["error"]!==false) { $resp = new Response($this->response["code"],null,null,$this->response["execTime"]); $resp->setErrorCode($this->response["error"]); $resp->setErrorMessage($this->response["message"]); } else { return new Response($this->response["code"],$this->response["headers"],$this->response["body"],$this->response["execTime"]); } } else { return $this->response; } } /** * Set the value of response * * @return self */ public function setResponse($response) { $this->response = $response; return $this; } /** * Get the value of headers */ public function getHeaders() { if(is_object($this->headers)) { return $this->headers->getAll(); } return $this->headers; } /** * Get the value of params */ public function getParams($json=false) { if(is_object($this->params)) { return $json ? json_encode($this->params->getAll()) : $this->params->getAll(); } return $json ? json_encode($this->params) : $this->params; } }src/httplite/client/Response.php000064400000012433144761607200012767 0ustar00setCode($code); $this->headers = new Params("array"); if(!is_null($headers)) { $this->setHeaders($headers); } if(!is_null($body)) { $this->setBody($body); } $extra = ""; if(!is_null($execTime)) { $this->setExecTime($execTime); $extra = "after ".round($execTime,3)."s "; } dhGlobal::log("info","dhHttp response created ".$extra."with status code ".$code); } public function get($json=false) { $o = $this->getInfo(true); if($json) { $o["json"] = $this->getJson(); } if(!$json || !isset($o["json"]) || is_null($o["json"])) { $o["body"] = $this->getBody(); } return $o; } public function getInfo($headers=false) { $o = [ "code"=>$this->getCode(), "codeMessage"=>$this->getCodeMessage(), "version"=>$this->getVersion(), "execTime"=>$this->getExecTime() ]; if($headers) { $o["headers"] = $this->getHeaders(); } return $o; } public function getVersion() { return !is_null($this->version) ? $this->version : false; } public function getCode() { return !is_null($this->code) ? $this->code : false; } public function getCodeMessage() { return !is_null($this->codeMessage) ? $this->codeMessage : false; } public function getExecTime() { return !is_null($this->execTime) ? $this->execTime : false; } public function getHeaders($header=null) { $array = $this->headers->get(); if(empty($header) || is_null($header)) { return $array; } return isset($array[$header]) ? $array[$header] : null; } public function getBody() { return $this->body; } public function getJson($item=null,$default=null) { if(is_null($this->jsonArray)) { $try = json_decode($this->body,true); if(is_array($try) || is_object($try)) { $this->jsonArray = $try; } } if(empty($item) || is_null($item)) { return $this->jsonArray; } if(strpos($item,".") !== false) { $check = dhGlobal::getDot($this->jsonArray,$item); if(!is_null($check)) { return $check; } } return isset($this->jsonArray[$item]) ? $this->jsonArray[$item] : $default; } public function getJsonObject() { if(is_null($this->jsonObject)) { $try = json_decode($this->body); if(is_object($try)) { $this->jsonObject = $try; } } return $this->jsonObject; } public function setHeaders($headers) { $harr = explode("\n",$headers); if(strpos($harr[0],":") === false && strpos($harr[0],"HTTP") !== false) { $t = explode(" ",$harr[0],3); $this->version = $t[0]; $this->codeMessage = $t[2]; $this->headers->set("@",$harr[0]); array_shift($harr); } foreach($harr as $header) { $temp = explode(": ",trim($header)); $v = isset($temp[1]) ? $temp[1] : ""; $this->headers->set($temp[0],$v); } } public function setBody($body) { $this->body = $body; } public function setExecTime($time) { $this->execTime = $time; } /** * Set the value of code * * @return self */ public function setCode($code) { $this->code = $code; return $this; } /** * Get the value of isError */ public function isError() { return $this->isError; } /** * Set the value of isError * * @return self */ public function setIsError($isError) { $this->isError = $isError; return $this; } /** * Get the value of errorCode */ public function getErrorCode() { return $this->errorCode; } /** * Set the value of errorCode * * @return self */ public function setErrorCode($errorCode) { $this->errorCode = $errorCode; return $this; } /** * Get the value of errorMessage */ public function getErrorMessage() { return $this->errorMessage; } /** * Set the value of errorMessage * * @return self */ public function setErrorMessage($errorMessage) { $this->errorMessage = $errorMessage; return $this; } }src/loader/PSR4.php000064400000003571144761607200010057 0ustar00getClassMap($className)) === false) { //Class is not within our defined map, move on to the next autoloader (if any) return; } $basePath = $map["path"]; $prefix = $map["prefix"]; // get the relative class name $relative_class = substr($className, strlen($prefix)); // replace the namespace prefix with the base directory, replace namespace // separators with directory separators in the relative class name, append // with .php $file = $basePath.str_replace('\\', '/', $relative_class); //dhGlobal::debug("Autoload attempt",$file); // if the file exists, require it if (file_exists($file.".".$this->extension)) { return dhLoader::includeOnce($file,$this->extension); } return; } public function add($prefix="some\\prefix\\",$path=".") { if(substr($path,strlen(DIRECTORY_SEPARATOR)) != DIRECTORY_SEPARATOR) { $path.=DIRECTORY_SEPARATOR; } $this->classMap[$prefix] = $path; } public function getClassMap($className) { foreach($this->classMap as $prefix => $path) { //dhGlobal::debug("checking",$prefix,$path); if (strncmp($prefix, $className, strlen($prefix)) === 0) { return ["prefix"=>$prefix,"path"=>$path]; } } return false; } }src/loader/TypeClass.php000064400000005165144761607200011237 0ustar00 types/Type.php * * Class_Type -> types/Class.php * * Subtype_Class_Type -> types/subtypes/Class.php * * Subtype_Subtype1_Class_Type -> types/subtypes/subtype1s/Class.php * * Subtype_Subtype1_Subtype2_Class_Type -> types/subtypes/subtype1s/subtype2s/Class.php */ class TypeClass { /** * Only attempts to autoLoad if the className begins with this prefix. Default is blank (all classes) * * Useful if adding this loader on top of other loaders that you don't want to interfere with. */ protected $classPrefix = ""; public $debug = false; public function init() { spl_autoload_register([$this,"autoload"]); } /** * Directories are all lowercase, Classe * * Type -> types/Type.php * * Class_Type -> types/Class.php * * Subtype_Class_Type -> types/subtypes/Class.php * * Subtype_Subtype1_Class_Type -> types/subtypes/subtype1s/Class.php * * Subtype_Subtype1_Subtype2_Class_Type -> types/subtypes/subtype1s/subtype2s/Class.php */ public function autoload($className) { //Ipad_Index_View //Ipad_Users_Login_Action $parts = explode('_', $className); $prefix = $this->classPrefix(); if(!empty($prefix)) { $first = array_shift($parts); if($first !== $prefix || empty($parts)) { return false; } } $classParts = []; $type = array_pop($parts); if(empty($parts)) { //Type -> types/Type.php $classParts = [strtolower($type)."s",$type]; } else { $class = array_pop($parts); if(empty($parts)) { //Class_Type -> types/Class.php $classParts = [strtolower($type)."s",$class]; } else { $classParts = [$class]; while(!empty($parts)) { array_unshift($classParts,strtolower(array_pop($parts))."s"); } array_unshift($classParts,strtolower($type)."s"); } } if($this->debug) { dhGlobal::debug("Autoload attempt",$classParts); } return dhLoader::includeOnce(implode(".",$classParts)); } public function classPrefix(string $prefix=null) { if(is_null($prefix)) { return $this->classPrefix; } else { $this->classPrefix = $prefix; } } }src/logger/Handler.php000064400000024535144761607200010720 0ustar00 dhGlobal::LOG_TRACE, "debug" => dhGlobal::LOG_DEBUG, "info" => dhGlobal::LOG_INFO, "warn" => dhGlobal::LOG_WARN, "error" => dhGlobal::LOG_ERROR, ]; protected $logLevelRanges = [ "trace" => [401,500], "debug" => [301,400], "info" => [201,300], "warn" => [101,200], "error" => [1 ,100], "off" => [0,0] ]; protected $logLevels = [ "trace" =>500, "debug" =>400, "info" =>300, "warn" =>200, "error" =>100, "off" =>0, ]; protected $prefixLength = 8; protected $prefixWrapper = ["[","] "]; protected $logPrefix = [ "trace" => "TRACE", "debug" => "DEBUG", "info" => "INFO", "warn" => "WARN", "error" => "ERROR", "off" => "", ]; /** * * @var dhOut[] */ protected $loggers = []; protected $levelLoggers = [ "trace"=>[], "debug"=>[], "info"=>[], "warn"=>[], "error"=>[] ]; /** * Creates a new logger and sets it to the appropriate level * @param mixed $name -- name to identify this logger with * @param array $levels -- bitmask from dhGlobal::LOG_* constants * @param bool $stdOut -- obvious? * @param bool $file -- filename to enable output file logging, false to disabke * @param string $lineSeparator * @return void * @throws Exception */ public function logger($name,$levels=[],$stdOut=true,$file=false,$lineSeparator=PHP_EOL) { if(empty($levels) || (!is_array($levels) && !is_int($levels))) { throw new \Exception("Logger levels must be defined and must be either an array of levels -- eg ['debug'] or ['error','warn'], OR use the dhGlobal::LOG_* constants as a bit mask -- eg LOG_ALL & ~LOG_TRACE"); } $this->loggers[$name] = new dhOut($stdOut,$file,true,"date:Y-m-d H:i:s","\t",$lineSeparator); if(!is_array($levels) && is_int($levels)) { foreach($this->logLevelsToBitMask as $level=>$mask) { if($levels & $mask) { $this->levelLoggers[$level][$name] = &$this->loggers[$name]; } } } if(is_array($levels)) { foreach($levels as $level) { if(isset($this->logLevels[$level])) { $this->levelLoggers[$level][$name] = &$this->loggers[$name]; } } } } public function addLoggerToLevel($level,$loggerName) { if(!isset($this->loggers[$loggerName])) { throw new \Exception("logger $loggerName not initiated or found"); } $label = $this->getLevelLabel($level); if(isset($this->levelLoggers[$label])) { } } public function log($level, ...$params) { $label = $this->getLevelLabel($level); $parent = $this->getParentLabel($level); $haveLoggers = false; if(isset($this->levelLoggers[$label]) && !empty($this->levelLoggers[$label])) { $haveLoggers = true; } if($parent != $label) { if(isset($this->levelLoggers[$parent]) && !empty($this->levelLoggers[$parent])) { $haveLoggers = true; } } if(!$haveLoggers) { return false; } $prefix = $this->getLevelPrefix($label); if(isset($this->levelLoggers[$label]) && !empty($this->levelLoggers[$label])) { foreach($this->levelLoggers[$label] as $logger) { if(!empty($params)) { $logParams = $params; array_unshift($logParams,$this->logPrefix($prefix)); call_user_func_array([$logger,"line"],$logParams); unset($logParams); } else { $logger->add(""); } $logger->end(); } } if($parent != $label && isset($this->levelLoggers[$parent]) && !empty($this->levelLoggers[$parent])) { foreach($this->levelLoggers[$parent] as $logger) { if(!empty($params)) { $logParams = $params; array_unshift($logParams,$this->logPrefix($prefix)); call_user_func_array([$logger,"line"],$logParams); unset($logParams); } else { $logger->add(""); } $logger->end(); } } } /** * define a custom log level. * * Example: * * $logger->defineLogLevel("query","debug","QRY1") -- adds a 'query' level to the debug log display * * @param string $name the name of the level used for logging (eg: $logger->log($name,stuff,to,print)) * @param string $level optional level to add the new level to, ['trace','debug','info','warn','error'] * @param string $prefix optional prefix used if different from the name. 5 characters * @return boolean true if successful, false if failed */ public function defineLogLevel($name,$level,$prefix=null) { if(is_null($prefix)) { $prefix = $name; } if(!is_numeric($level)) { $level = isset($this->logLevels[$level]) ? $level : "debug"; list($start,$end) = $this->logLevelRanges[$level]; $number = false; $levels = array_flip($this->logLevels); for($i=$start;$i<$end;$i++) { if(!isset($levels[$i])) { $number = $i; break; } } if($number === false) { return false; } } else { $number = $level; } $levels = $this->logLevels; $levels[$name] = $number; $temp = array_flip($levels); krsort($temp); $this->logLevels = array_flip($temp); $this->logPrefix[$name] = $prefix; return true; } public function logPrefix($prefix) { if(strlen($prefix)>$this->prefixLength) { $prefix = substr($prefix,0,$this->prefixLength); } elseif(strlen($prefix)<$this->prefixLength) { $prefix = str_pad($prefix,$this->prefixLength," ",STR_PAD_BOTH); } list($left,$right) = $this->prefixWrapper; return $left.strtoupper($prefix).$right; } public function getLogLevels() { return $this->logLevels; } public function getLogPrefixes() { return $this->logPrefix; } public function getLabelLevel($label) { return isset($this->logLevels[$label]) ? $this->logLevels[$label] : false; } public function getLevelPrefix($level) { $label = $this->getLevelLabel($level); if($label === false) { return false; } if(isset($this->logPrefix[$level])) { return $this->logPrefix[$level]; } return false; } public function getLevelLabel($level) { if(!is_numeric($level) && isset($this->logLevels[$level])) { return $level; } if(is_numeric($level)) { $tflip = array_flip($this->logLevels); if(isset($tflip[$level])) { return $tflip[$level]; } } return $this->getParentLabel($level); } public function getParentLabel($level) { if(!is_numeric($level) && isset($this->logLevels[$level])) { $level = $this->logLevels[$level]; } if(is_numeric($level)) { foreach($this->logLevelRanges as $name=>$range) { if($level >= $range[0] && $level <= $range[1]) { return $name; } } } return false; } public function trace(...$data) { array_unshift($data,"trace"); call_user_func_array( [$this,"log"], $data); } public function debug(...$data) { array_unshift($data,"debug"); call_user_func_array( [$this,"log"], $data); } public function info(...$data) { array_unshift($data,"info"); call_user_func_array( [$this,"log"], $data); } public function warn(...$data) { array_unshift($data,"warn"); call_user_func_array( [$this,"log"], $data); } public function error(...$data) { array_unshift($data,"error"); call_user_func_array( [$this,"log"], $data); } protected static $instance; /** * Used to init the singleton with specific settings * @return dhlogger singlton */ public static function init($level=null,$file=null,$dhOut=null) { if(self::$instance !== null) { self::instance()->end(); } self::$instance = new self($level,$file,$dhOut); return self::$instance; } /** * Used to get the singleton isntance, or init with default settings; * @return dhlogger singlton */ public static function instance() { if(self::$instance === null) { self::$instance = self::init(); } return self::$instance; } /** * Get the value of prefixLength */ public function getPrefixLength() { return $this->prefixLength; } /** * Set the value of prefixLength * * @return self */ public function setPrefixLength($prefixLength) { $this->prefixLength = $prefixLength; return $this; } /** * Get the value of prefixWrapper */ public function getPrefixWrapper() { return $this->prefixWrapper; } /** * Set the value of prefixWrapper * * @return self */ public function setPrefixWrapper($prefixWrapper) { $this->prefixWrapper = $prefixWrapper; return $this; } }src/logger/Logger.php000064400000020253144761607200010553 0ustar00500, "debug" =>400, "info" =>300, "warn" =>200, "error" =>100, "off" =>0, ]; protected $logPrefix = [ "trace" => "[TRACE]", "debug" => "[DEBUG]", "info" => "[ INFO]", "warn" => "[ WARN]", "error" => "[ERROR]", "off" => "", ]; protected $logLevel = 0; public function __construct($level=null,$file=null,$dhOut=null) { if(!is_null($dhOut)) { $this->client = $dhOut; } else { $this->client = new dhOut(); } if(!is_null($level)) { $this->setLogLevel($level); } if(!is_null($file)) { $this->setFile($file); } } public function trace(...$data) { array_unshift($data,"trace"); call_user_func_array( [$this,"log"], $data); } public function debug(...$data) { array_unshift($data,"debug"); call_user_func_array( [$this,"log"], $data); } public function info(...$data) { array_unshift($data,"info"); call_user_func_array( [$this,"log"], $data); } public function warn(...$data) { array_unshift($data,"warn"); call_user_func_array( [$this,"log"], $data); } public function error(...$data) { array_unshift($data,"error"); call_user_func_array( [$this,"log"], $data); } public function log($level, ...$params) { if(is_numeric($level)) { $ln = false; $temp = $this->logLevels; $tlevels = array_flip($temp); ksort($tlevels); foreach($tlevels as $lvl=>$nm) { if($lvl>=$level && $ln === false) { $ln = $nm; } } if($ln !== false) { $prefix = $this->logPrefix[$ln]; } else { $this->makeLogLevelPrefix($level); } } elseif(!is_numeric($level) && isset($this->logLevels[$level])) { $prefix = $this->logPrefix[$level]; $level = $this->logLevels[$level]; } else { $level = $this->logLevel; $prefix = $this->makeLogLevelPrefix($level); } if($this->logLevel>0 && $level<=$this->logLevel) { if(empty($params)) { $this->client->add(""); } else { $this->client->add($prefix." "); foreach($params as $param) { $this->client->append(" "); $this->client->append($param); } } $this->client->end(); } } public function setFile($file) { $this->file = $file; $this->client->setFileName($file); } public function setLogLevel($level) { if($level === false) { $this->logLevel = 0; } if(is_numeric($level)) { $this->logLevel = $level; } elseif(isset($this->logLevels[strtolower($level)])) { $this->logLevel = $this->logLevels[strtolower($level)]; } } public function level($level) { return $this->setLogLevel($level); } public function logLevel($level=null) { if(is_null($level)) { return $this->logLevel; } return $this->setLogLevel($level); } public function setLevel($level) { return $this->setLogLevel($level); } public function getMaxLevelForName($name) { $temp = $this->logLevels; $tlevels = array_flip($temp); ksort($tlevels); $max =0; $found = false; $level = $this->logLevels[$name]; foreach($tlevels as $lvl=>$nm) { if($found) { $max = $lvl-1; break; } else { if($nm == $name) { $found = true; } } } if($max>0) { return $max; } { return 9999; } } /** * Add a level to the logger. * * Example: * * $logger->addLogLevel("query","QUERY","debug") -- adds a 'query' level to the debug log display * * @param string $name the name of the level used for logging (eg: $logger->log($name,stuff,to,print)) * @param string $level optional level to add the new level to, ['trace','debug','info','warn','error'] * @param string $prefix optional prefix used if different from the name. 5 characters * @return boolean true if successful, false if failed */ public function addLogLevel($name,$level=null,$prefix=null) { if(is_null($prefix)) { $prefix = $name; } $prefix = $this->makeLogLevelPrefix($prefix); if(!is_numeric($level)) { $start = 999; $end = 0; if(!is_null($level)) { if(isset($this->logLevels[$level])) { $start = $this->logLevels[$level]; $nextLevel = $this->getHigherLogLevel($level); $end = $this->logLevels[$nextLevel]; } } $number = false; $levels = array_flip($this->logLevels); for($i=$end+1;$i<$start;$i++) { if(!isset($levels[$i])) { $number = $i; break; } } if($number === false) { return false; } } else { $number = $level; } $levels = $this->logLevels; $levels[$name] = $number; $temp = array_flip($levels); krsort($temp); $this->logLevels = array_flip($temp); $this->logPrefix[$name] = $prefix; return true; } public function makeLogLevelPrefix($prefix) { if(strlen($prefix)>5) { $prefix = substr($prefix,0,5); } elseif(strlen($prefix)<5) { $prefix = str_pad($prefix,5," ",STR_PAD_BOTH); } return "[".strtoupper($prefix)."]"; } public function getLogLevels() { return $this->logLevels; } public function getLogPrefixes() { return $this->logPrefix; } /** * Standard method wrapper */ public function __call($method,$args) { return call_user_func_array( [$this->client,$method], $args); } protected static $instance; /** * Used to init the singleton with specific settings * @return dhlogger singlton */ public static function init($level=null,$file=null,$dhOut=null) { if(self::$instance !== null) { self::instance()->end(); } self::$instance = new self($level,$file,$dhOut); return self::$instance; } /** * Used to get the singleton isntance, or init with default settings; * @return dhlogger singlton */ public static function instance() { if(self::$instance === null) { self::$instance = self::init(); } return self::$instance; } public function getLabelForLevel($level=100) { $name = false; $temp = $this->logLevels; $tlevels = array_flip($temp); ksort($tlevels); foreach($tlevels as $lvl=>$lvlName) { if($lvl>=$level && $name === false) { $name = $lvlName; return $name; } } } public function getHigherLogLevel($level) { if(is_numeric($level)) { $level = $this->getLabelForLevel($level); } $keys = array_keys($this->logLevels); $current = array_search($level, $keys); $nextKey = $keys[($current===false ? -1 : $current)+1] ?: $keys[0]; return $nextKey; } }src/mail/Imap.php000064400000004444144761607200007671 0ustar00false,"valid"=>[],"invalid"=>[]]; protected $mailConfig = [ "host"=>"", "port"=>0, "user"=>"", "pass"=>"", "options"=>["imap","ssl"], "mailbox"=>"INBOX", "attachmentsDir"=>null, "extra"=>[ "doCount"=>false ] ]; protected $mailbox; public function __construct($mailConfig=[]) { if(!empty($mailConfig)) { $this->config($mailConfig); } } public function setConfig($item,$value) { if(isset($this->mailConfig[$item])) { $this->mailConfig[$item] = $value; } else { $this->mailConfig["extra"][$item] = $value; } } public function getConfig($item,$default=null) { } public function config($mailConfig) { foreach($this->mailConfig as $cfg=>$v) { if(isset($mailConfig[$cfg])) { $this->mailConfig[$cfg] = $mailConfig[$cfg]; unset($mailConfig[$cfg]); } } if(!empty($mailConfig)) { $this->mailConfig["extra"] = $mailConfig; } $v = $this->validateConfig(); } public function getMailbox($connString,$user,$pass,$attachmentsDir=null,$encoding="UTF-8") { return new \PhpImap\Mailbox($connString, $user, $pass, $attachmentsDir,$encoding); } protected function validateConfig($cfg = []) { if(empty($cfg)) { $cfg = $this->mailConfig; } $valid = [ "host"=>false, "port"=>false, "user"=>false, "pass"=>false, "mailbox"=>false ]; foreach($valid as $name=>$false) { if(!empty($this->mailConfig[$name])) { $valid[$name] = true; } } $invalid = []; $overall = true; foreach($valid as $k=>$v) { if(!$v) { $overall=false; $invalid[$k]=true; unset($valid[$k]); } } $this->validateConfig = ["overall"=>$overall,"valid"=>$valid,"invalid"=>$invalid]; return $overall; } }src/mail/SMTP.php000064400000004325144761607200007564 0ustar00"", "port"=>0, "user"=>"", "pass"=>"", "auth"=>true, "secure"=>false, "extra"=>[] ]; protected PHPMailer $client; protected $isConfiged = false; public function __construct($mailConfig=[]) { if(!empty($mailConfig)) { $this->config($mailConfig); } $this->client = new PHPMailer(true); } public function config($mailConfig) { foreach($this->mailConfig as $cfg=>$v) { if(isset($mailConfig[$cfg])) { $this->mailConfig[$cfg] = $mailConfig[$cfg]; unset($mailConfig[$cfg]); } } if(!empty($mailConfig)) { $this->mailConfig["extra"] = $mailConfig; } $this->client->Host = $this->mailConfig["host"]; $this->client->Port = $this->mailConfig["port"]; $this->client->SMTPAuth = $this->mailConfig["auth"]; $this->client->Username = $this->mailConfig["user"]; $this->client->SMTPSecure = $this->mailConfig["secure"]; $this->isConfiged = true; } public function output($success=true,$code=0,$message=null) { return ["success"=>$success,"code"=>$code,"message"=>$message]; } public function send() { if(!$this->isConfiged) { return $this->output(false,-100,"Must configure before sending"); } $success=false; $code=-1; $message=null; try { $this->client->send(); $success=true; $code=0; } catch (\Exception $e) { $message = $e->getMessage(); $code = $e->getCode(); } return $this->output($success,$code,$message); } public function __call($method, $args) { if(!method_Exists($this->client, $method)) { throw new \Exception("Method $method does not exist on PHPMailer object",-1); } call_user_func_array(array($this->client, $method), $args); } }src/misc/PSR4Bootstrap.php000064400000003633144761607200011441 0ustar00threadStartCallback)) { $cb = $this->threadStartCallback; $cb($pool,$thread); } } /** * @param \boru\dhutils\dhThreads $pool the threadpool, so more work can be added if needed * @param \boru\dhutils\dhThreads\multithread\Thread $thread * @return void */ public function threadFinished($pool,$thread) { if(is_callable($this->threadFinishedCallback)) { $cb = $this->threadFinishedCallback; $cb($pool,$thread); } } /** * @param \boru\dhutils\dhThreads $pool * @return void */ public function loop($pool) { $this->lastLoopTime = $this->loopTime; $this->loopTime = microtime(true); if(is_callable($this->loopCallback)) { $cb = $this->loopCallback; $cb($pool); } } /** * @param mixed $data * @return void */ public function collect($data) { if(is_callable($this->collectCallback)) { $cb = $this->collectCallback; $cb($data); } } /** * Get the value of threadStartCallback * * @return callable|null */ public function getThreadStartCallback() { return $this->threadStartCallback; } /** * Set the value of threadStartCallback * * @param callable $threadStartCallback * @return self */ public function setThreadStartCallback(callable $threadStartCallback) { $this->threadStartCallback = $threadStartCallback; return $this; } /** * Get the value of threadFinishedCallback * * @return callable */ public function getThreadFinishedCallback() { return $this->threadFinishedCallback; } /** * Set the value of threadFinishedCallback * * @param callable $threadFinishedCallback * @return self */ public function setThreadFinishedCallback(callable $threadFinishedCallback) { $this->threadFinishedCallback = $threadFinishedCallback; return $this; } /** * Get the value of throttleWaitCallback * * @return callable|null */ public function getThrottleWaitCallback() { return $this->loopCallback; } /** * Set the value of throttleWaitCallback * * @param callable $throttleWaitCallback * @return self */ public function setThrottleWaitCallback(callable $throttleWaitCallback) { $this->loopCallback = $throttleWaitCallback; return $this; } /** * Get the value of throttleWaitCallback * * @return callable|null */ public function getCollectCallback() { return $this->collectCallback; } /** * Set the value of throttleWaitCallback * * @param callable $throttleWaitCallback * @return self */ public function setCollectCallback(callable $collectCallback) { $this->collectCallback = $collectCallback; return $this; } /** * Get the value of loopCallback * * @return callable|null */ public function getLoopCallback() { return $this->loopCallback; } /** * Set the value of loopCallback * * @param callable $collectCallback * @return self */ public function setLoopCallback(callable $loopCallback) { $this->loopCallback = $loopCallback; return $this; } /** * Returns the microtime float difference between the last loop and this loop * @return float */ public function timeSinceLastLoop() { return $this->loopTime - $this->lastLoopTime; } }src/multithread/MTCallbackInterface.php000064400000004360144761607200014156 0ustar00id = dhGlobal::getVal($data,"id",false); $this->command = dhGlobal::getVal($data,"command",false); $this->return = dhGlobal::getVal($data,"return",false); $this->channelType = dhGlobal::getVal($data,"channelType",dhThreads::CHANNEL_NONE); $this->bootstrap = dhGlobal::getVal($data,"bootstrap",false); if((dhGlobal::getVal($data,"debug",false)) !== false) { $this->debugMode = true; } elseif((dhGlobal::getVal($data,"debugMode",false)) !== false) { $this->debugMode = true; } if($this->bootstrap !== false && file_exists($this->bootstrap)) { include $this->bootstrap; } dhThreads::sendChannel($this->id,$this->channelType,$this->run()); } public function run() { $this->debug($this->command); ob_start(); passthru($this->command); $data = ob_get_contents(); ob_end_clean(); return $data; } public function debug(...$data) { if($this->debugMode) { array_unshift($data,"[T- exec] "); dhGlobal::debug(...$data); } } }src/multithread/Thread.php000064400000015154144761607200011612 0ustar00wraper = $wrapper; } $this->json = dhGlobal::getVal($options,"json",false); $this->jsonObject = dhGlobal::getVal($options,"jsonObject",false); $this->channelType = dhGlobal::getVal($options,"channelType",dhThreads::CHANNEL_MEMCACHED); $this->id = dhGlobal::getVal($meta,"id",uniqid()); $this->complete = false; $this->command = $command; $this->started = new \DateTime(); $this->meta = $meta; $this->runnerId = uniqid("dhmt",true); if((dhGlobal::getVal($options,"debug",false)) !== false) { $this->debugMode = true; } elseif((dhGlobal::getVal($options,"debugMode",false)) !== false) { $this->debugMode = true; } $this->setupCallbacks($options); $this->execute(); } public function __destruct() { $this->stop(); } protected function execute() { $packet = $this->makePacket(); $runCMD = "php -f ".$this->wrapper." ". dhThreads::pack($packet); $this->pid = exec($runCMD.' > /dev/null 2>&1 & echo $!'); dhGlobal::trace("[MT-T] ".$runCMD); $this->debug(dhGlobal::padRight($this->pid,8," "),$runCMD); } protected function makePacket() { $packet = [ "id"=>$this->runnerId, "command"=>$this->command, "return"=>true, "channelType"=>$this->channelType ]; if($this->debugMode) { $packet["debugMode"] = true; } if($this->bootstrap !== false) { $packet["bootstrap"] = $this->bootstrap; } return $packet; } protected function setupCallbacks($options=[]) { $this->bootstrap = false; if(($bootstrapFile = dhGlobal::getVal($options,"bootstrap",false)) !== false) { if(file_exists($bootstrapFile)) { $this->bootstrap = $bootstrapFile; } } //default 'thread finished' callback that does not use a visualizer to process. $this->callback = dhGlobal::getVal($options,"callback",null); } /** * Terminates the thread */ public function stop() { if(!$this->finished() && !$this->stopped) { $this->stopped = true; posix_kill($this->pid, 9); if($this->finished()) { dhGlobal::trace("[MT-T] ".$this->pid()." Stopped by request."); } else { dhGlobal::trace("[MT-T] ".$this->pid()." Recieved stop request.. kill signal sent"); } } } public function collect() { $data = dhThreads::readChannel($this->runnerId,$this->channelType); if($data !== false) { if($this->json) { return json_decode($data,!$this->jsonObject); } return $data; } return false; } /** * Returns true if the thread has completed, false if running */ public function finished() { if($this->complete) { return true; } if(!is_null($this->pid)) { if(!posix_getpgid($this->pid)) { $this->complete = true; $this->output = $this->collect(); if(is_callable($this->callback)) { $cb = $this->callback; $cb($this); } return true; } } $now = time(); if($this->timeout>0 && $this->started->format("U")+$this->timeout > $now) { $this->stop(); return true; } $this->complete = false; return false; } /** * Returns true if the thread has completed, false if running */ public function complete() { return $this->finished(); } /** * Returns true if the thread has completed, false if running */ public function isDone() { return $this->finished(); } /** * Returns true if the thread has completed, false if running */ public function done() { return $this->finished(); } /** * Terminates the thread */ public function kill() { $this->stop(); } /** * Get the value of pid * * @return mixed */ public function pid() { return is_numeric($this->pid) ? $this->pid : false; } /** * Get the value of command * * @return mixed */ public function command() { return $this->command; } /** * Get the value of started * * @return mixed */ public function started() { return $this->started; } /** * Get the value of meta * * @return mixed */ public function meta() { return $this->meta; } /** * Get the value of output * * @return mixed */ public function output() { return $this->output; } /** * Get the value of runnerId * * @return mixed */ public function id() { return $this->id; } /** * Get the value of runnerId * * @return mixed */ public function runnerId() { return $this->runnerId; } public function get() { return [ "pid"=>$this->pid(), "complete"=>$this->complete(), "command"=>$this->command(), "started"=>$this->started(), "meta"=>$this->meta(), ]; } public function jsonSerialize($array=null) { if(is_null($array)) { $array = $this->get(); } return $array; } public function __toString() { return json_encode($this); } public function debug(...$data) { if($this->debugMode) { array_unshift($data,"[T-child] "); dhGlobal::debug(...$data); } } }src/multithread/parts/Buffer.php000064400000005240144761607200012740 0ustar00closed) { return false; } $this->out = array_merge($this->out,explode("\n",$data)); return $this; } public function err($data=null) { if($this->closed) { return false; } $this->err = array_merge($this->out,explode("\n",$data)); return $this; } public function close() { $this->closed; } /** * Read the stdOut and stdErr buffers and optionally consumes them ($consume=true by default) * * @param bool $asArray (default:true) if true, retrieve an array of lines. Else, lines will be glued together with \n * @param bool $consume (default:true) if true, resets the Out and Err buffers to empty * @return (bool|array)[] */ public function read($asArray=true,$consume=true) { $out = $this->readOut($asArray,$consume); $err = $this->readErr($asArray,$consume); $closed = $this->closed; return ["closed"=>$closed,"out"=>$out,"err"=>$err]; } /** * Read the stdOut buffer and optionally consume it ($consume=true by default) * * @param bool $asArray (default:true) if true, retrieve an array of lines. Else, lines will be glued together with \n * @param bool $consume (default:true) if true, resets the Out buffer to empty * @return array */ public function readOut($asArray=true,$consume=true) { $out = $this->out; if($consume) { $this->out = []; } return $asArray ? $out : implode("\n",$out); } /** * Read the stdErr buffer and optionally consume it ($consume=true by default) * * @param bool $asArray (default:true) if true, retrieve an array of lines. Else, lines will be glued together with \n * @param bool $consume (default:true) if true, resets the Err buffer to empty * @return array */ public function readErr($asArray=true,$consume=true) { $err = $this->err; if($consume) { $this->err = []; } return $asArray ? $err : implode("\n",$err); } /** * Returns an array of 'closed' (bool), 'out' (array), 'err' (array) without consuming the data (leaves it in tact) * * @return (bool|array)[] */ public function get() { return ["closed"=>$this->closed,"out"=>$this->out,"err"=>$this->err]; } }src/multithread/parts/WorkResult.php000064400000011632144761607200013652 0ustar00success = dhGlobal::getVal($data,"success",false); $this->success = dhGlobal::getVal($data,"data.workId",false); $this->data = dhGlobal::getVal($data,"data.data",null); $this->stdout = dhGlobal::getVal($data,"data.stdout",null); $this->code = dhGlobal::getVal($data,"data.code",null); $this->message = dhGlobal::getVal($data,"data.message",null); $this->trace = dhGlobal::getVal($data,"data.trace",null); } } } public static function fromFrame(WorkerFrame $frame) { $instance = new self($frame->get()); return $instance; } public static function fromJson($jsonData) { $instance = new self($jsonData); return $instance; } public static function fromArray($arrayData) { $instance = new self($arrayData); return $instance; } public function success() { return $this->getSuccess(); } public function data($keyString=null,$default=null) { if(is_null($keyString)) { return $this->getData(); } else { if(is_array($this->data)) { return dhGlobal::getVal($this->data,$keyString,$default); } return $default; } } public function stdout() { return $this->getStdout(); } public function code() { return $this->getCode(); } public function message() { return $this->getMessage(); } public function trace() { return $this->getTrace(); } public function error() { return ["success"=>$this->success(),"error"=>!$this->success(),"code"=>$this->code(),"message"=>$this->message(),"trace"=>$this->trace()]; } /** * Get the value of success * * @return mixed */ public function getSuccess() { return $this->success; } /** * Set the value of success * * @param mixed $success * @return self */ public function setSuccess($success) { $this->success = $success; return $this; } /** * Get the value of data * * @return mixed */ public function getData() { return $this->data; } /** * Set the value of data * * @param mixed $data * @return self */ public function setData($data) { $this->data = $data; return $this; } /** * Get the value of stdout * * @return mixed */ public function getStdout() { return $this->stdout; } /** * Set the value of stdout * * @param mixed $stdout * @return self */ public function setStdout($stdout) { $this->stdout = $stdout; return $this; } /** * Get the value of code * * @return mixed */ public function getCode() { return $this->code; } /** * Set the value of code * * @param mixed $code * @return self */ public function setCode($code) { $this->code = $code; return $this; } /** * Get the value of message * * @return mixed */ public function getMessage() { return $this->message; } /** * Set the value of message * * @param mixed $message * @return self */ public function setMessage($message) { $this->message = $message; return $this; } /** * Get the value of trace * * @return mixed */ public function getTrace() { return $this->trace; } /** * Set the value of trace * * @param mixed $trace * @return self */ public function setTrace($trace) { $this->trace = $trace; return $this; } /** * Get the value of trace * * @return mixed */ public function getWorkId() { return $this->workId; } /** * Set the value of workId * * @param mixed $workId * @return self */ public function setWorkId($workId) { $this->workId = $workId; return $this; } }src/multithread/parts/WorkerFrame.php000064400000015367144761607200013766 0ustar00data = $data; } $this->rawData = json_encode($this->data); if(is_null($this->type)) { $this->type="base64"; } if(($this->encodedData = $this->encodeType($this->type,$this->rawData)) === false) { $this->lastError = "unknown_frame_type"; return false; } $this->createFrame(); return true; } public function decode($rawPacket) { $this->valid = false; $this->packet = $rawPacket; if(($parsedFrame = $this->parseFrame()) !== false) { if(($this->rawData = $this->decodeType($parsedFrame["type"],$parsedFrame["data"])) === false) { $this->lastError = "unknown_frame_type"; return false; } $this->type = $parsedFrame["type"]; $parsed = $this->parseJson($this->rawData); if(is_array($parsed)) { $this->data = $parsed; $this->valid = true; $this->lastError = null; } else { $this->data = null; $this->valid = false; $this->lastError = "json_failure"; } } else { $this->lastError = "malformed_frame"; return false; } } private function encodeType($type,$rawData) { if($type == "plain") { return $rawData; } elseif($type == "base64") { return base64_encode($rawData); } return false; } private function decodeType($type,$encodedData) { if($type == "plain") { return $this->decodePlain($encodedData); } elseif($type == "base64") { return $this->decodeBase64($encodedData); } return false; } public function getWorkResult() { return new WorkResult($this->get()); } public function packet() { return $this->getPacket(); } public function type() { return $this->getType(); } public function rawData() { return $this->getRawData(); } public function data() { return $this->getData(); } public function valid() { return $this->getValid(); } public function meta() { return [ "packet"=>$this->packet, "type"=>$this->type, "rawData"=>$this->rawData, "valid"=>$this->valid, "lastError"=>$this->lastError, ]; } private function decodePlain($framedData) { return $framedData; } private function decodeBase64($framedData) { return base64_decode($framedData); } private function parseJson($data) { $json = json_decode($data,true); if(is_array($json)) { return $json; } else { return false; } } private function parseFrame() { $rawPacket = $this->packet; $delimStart = $this->startDelimiter; $delimEnd = $this->endDelimiter; $delimType = $this->typeDelimiter; if(substr($rawPacket,0,strlen($delimStart)) == $delimStart && substr($rawPacket,-strlen($delimEnd)) == $delimEnd) { $rawPacket = substr($rawPacket,1); $rawPacket = substr($rawPacket,0,-1); $parts = explode($delimType,$rawPacket,2); if(count($parts) !== 2) { return false; } return ["type"=>$parts[0],"data"=>$parts[1]]; } } private function createFrame() { $this->packet = $this->startDelimiter.$this->type.$this->typeDelimiter.$this->encodedData.$this->endDelimiter; return $this->packet; } /** * Get the value of packet * * @return mixed */ public function getPacket() { if(is_null($this->packet) && !is_null($this->data)) { $this->encode(); } return $this->packet; } /** * Set the value of packet * * @param mixed $packet * @return self */ public function setPacket($packet) { $this->packet = $packet; return $this; } /** * Get the value of type * * @return mixed */ public function getType() { return $this->type; } /** * Set the value of type * * @param mixed $type * @return self */ public function setType($type) { $this->type = $type; return $this; } /** * Get the value of rawData * * @return mixed */ public function getRawData() { return $this->rawData; } /** * Set the value of rawData * * @param mixed $rawData * @return self */ public function setRawData($rawData) { $this->rawData = $rawData; return $this; } /** * Get the value of data * * @return mixed */ public function getData() { return $this->data; } /** * Set the value of data * * @param mixed $data * @return self */ public function setData($data) { $this->data = $data; return $this; } /** * Get the value of valid * * @return mixed */ public function getValid() { return $this->valid; } public static function fromPacket($rawPacket) { $instance = new self(); $instance->decode($rawPacket); return $instance; } public static function fromData($data=[],$type="base64") { $instance = new self(); return $instance->setType($type)->setData($data); } }src/multithread/process/ClassProcess.php000064400000010555144761607200014465 0ustar00setCallable($callable); $this->setBootstrap($bootstrap); if(!empty($args)) { $this->setArgs($args); } $meta = [ "onStart"=>function() { $this->onStart(); }, "onDone"=>function($exitCode=null,$termSignal=null) { $this->onDone($exitCode,$termSignal); }, ]; $this->process = new Process("php -f ".__DIR__."/../util/ClassMethod.php",[],$meta); $this->deferred = new Deferred(); } /** * Starts the Child Process, returns the ClassProcess Promise for easy chaining * * @return Promise * @throws UnexpectedValueException * @throws RuntimeException */ public function start() { $this->process->start(); return $this->Promise(); } /** @return \React\Promise\Promise */ public function Promise() { return $this->deferred->promise(); } /** @return \boru\dhutils\multithread\process\Process */ public function Process() { return $this->process; } /** * Wrapper for \React\Promise\Promise::then() * @param callable|null $onFulfilled * @param callable|null $onRejected * @return PromiseInterface */ public function then(callable $onFulfilled=null,callable $onRejected=null) { return $this->Promise()->then($onFulfilled,$onRejected); } private function makePacket() { return json_encode([ "callable"=>$this->callable, "bootstrap"=>$this->bootstrap, "args"=>$this->args, ]); } private function parseOutputPacket($lines) { $started=false; $data = ""; foreach($lines as $line) { if($started) { if($line == "#END#") { break; } $data.=$line."\n"; } if($line == "#START#") { $started = true; } } if($this->json) { return json_decode($data,$this->jsonAsArray); } return $data; } private function onDone($exitCode=null,$termSignal=null) { $buffer = $this->process->Buffer(); $output = $this->parseOutputPacket($buffer->readOut()); $this->deferred->resolve($output); } private function onStart() { $this->process->write($this->makePacket()."\n#END#\n"); } /** * Set the value of callable * * @param mixed $callable * @return self */ public function setCallable(callable $callable) { $this->callable = $callable; return $this; } /** * Set the value of bootstrap * * @param mixed $bootstrap * @return self */ public function setBootstrap($bootstrap) { $this->bootstrap = $bootstrap; return $this; } /** * Set the value of args * * @param mixed $args * @return self */ public function setArgs($args) { $this->args = $args; return $this; } /** * If true, decode the output as JSON * * @param bool $json * @param bool $asArray (default:true) if false, return a stdObject instead of array * @return self */ public function setJson($json,$asArray=true) { $this->json = $json; $this->jsonAsArray = $asArray; return $this; } }src/multithread/process/Process.php000064400000031533144761607200013476 0ustar00setCommand($command); $this->setArgs($args); $this->setMeta($meta); //sets all the handlers/etc $this->setPid(uniqid()); $this->buffer = new Buffer(); } /** * Starts the Child Process, returns the Promise for easy chaining * @return Promise|false * @throws UnexpectedValueException * @throws RuntimeException */ public function start() { $args = ''; if(!empty($this->args)) { $args = ProcessQueue::pack($this->args); } $this->process = new \React\ChildProcess\Process('exec '.$this->command.' '.$args); $this->process->start(); $this->setupProcessChannels(); $this->onStart(); return $this->Promise(); } public function stop() { $this->running=false; $this->process->terminate(); } public function terminate() { $this->running=false; $this->process->terminate(); if(!is_null($this->deferred)) { $this->deferred->reject("terminated"); } } public function get() { return [ "pid"=>$this->pid, "running"=>$this->running, "done"=>$this->done, "meta"=>$this->meta, "args"=>$this->args, "command"=>$this->command, "buffer"=>$this->buffer, "exitCode"=>$this->exitCode, "termSignal"=>$this->termSignal, "data"=>$this->data, ]; } public function write($data) { return $this->process->stdin->write($data); } public function isDone() { return $this->done; } public function isRunning() { return $this->running; } public function isReady() { return $this->ready; } /** * Return the processes's stdOut stream interface * @return \React\Stream\ReadableStreamInterface */ public function stdOut() { return $this->process->stdout; } /** * Return the processes's stdErr stream interface * @return \React\Stream\ReadableStreamInterface */ public function stdErr() { return $this->process->stderr; } /** * Return the processes's stdIn stream interface * @return \React\Stream\WritableStreamInterface */ public function stdIn() { return $this->process->stdin; } /** * Get the buffer * @return Buffer */ public function Buffer() { return $this->buffer; } /** * Return the Deferred * @return Deferred */ public function Deferred() { return $this->deferred; } /** * Return the Promise (if exists) * @return Promise|false */ public function Promise() { return !is_null($this->deferred) ? $this->deferred->promise() : false; } /** * Wrapper for \React\Promise\Promise::then() * @param callable|null $onFulfilled * @param callable|null $onRejected * @return PromiseInterface */ public function then(callable $onFulfilled=null,callable $onRejected=null) { return $this->Promise()->then($onFulfilled,$onRejected); } /** Handler Methods */ public function onDone($exitCode=null,$termSignal=null) { $this->done = true; $this->running = false; $this->ready = false; $this->exitCode = $exitCode; $this->termSignal = $termSignal; $this->deferred->resolve($this->buffer); if(!is_null($this->onDone)) { $handler = $this->onDone; $handler($this,$exitCode,$termSignal); } } public function onError($chunk=null) { if(!is_null($this->onError)) { $handler = $this->onError; $handler($this,$chunk); } if($this->useBuffer) { $this->buffer->err($chunk); } } public function onData($chunk=null) { if($this->isMessageFrames) { $data = !empty($this->messageBuffer) ? $this->messageBuffer : ""; $data.=$chunk; if(($message = Message::fromPacket($data)) !== false) { $chunk = $data; $this->messageBuffer = null; } else { $this->messageBuffer = $data; return; } } if(!is_null($this->onData)) { $handler = $this->onData; $handler($this,$chunk); } if($this->useBuffer) { $this->buffer->out($chunk); } } public function onStart() { $this->running = true; $this->ready = false; if(!is_null($this->onStart)) { $handler = $this->onStart; $handler($this); } } public function onStreamClose($type) { if(!is_null($this->onStreamClose)) { $handler = $this->onStreamClose; $handler($this,$type); } } public function onStreamError($type,$chunk=null) { if(!is_null($this->onStreamError)) { $handler = $this->onStreamError; $handler($this,$type,$chunk); } } /** * Set the stdout/stderr channel listening * @return void */ private function setupProcessChannels() { $this->process->stdout->on('data', function($chunk) { $this->onData(trim($chunk)); }); $this->process->stdout->on('error', function($chunk) { $this->onStreamError("stdout",$chunk); }); $this->process->stdout->on('close', function() { $this->onStreamClose("stderr"); }); $this->process->stderr->on('data', function($chunk) { $this->onError(trim($chunk)); }); $this->process->stderr->on('error', function($chunk) { $this->onStreamError("stderr",$chunk); }); $this->process->stderr->on('close', function() { $this->onStreamClose("stderr"); }); $this->process->on('exit', function($exitCode,$termSignal) { $this->onDone(); }); } public function getId() { return $this->getPid(); } /** * Get the value of pid * @return mixed */ public function getPid() { return $this->pid; } public function getPriority() { return $this->getPid(); } /** * Set the value of pid * @param mixed $pid * @return self */ public function setPid($pid) { $this->pid = $pid; return $this; } /** * Get the value of command * @return mixed */ public function getCommand() { return $this->command; } /** * Set the value of command * @param mixed $command * @return self */ public function setCommand($command) { $this->command = $command; return $this; } /** * Get the value of args * @return mixed */ public function getArgs() { return $this->args; } /** * Set the value of args * @param mixed $args * @return self */ public function setArgs($args) { $this->args = $args; return $this; } /** * Get the value of meta * @return mixed */ public function getMeta() { return $this->meta; } /** * Set the value of meta * @param mixed $meta * @return self */ public function setMeta($meta) { $this->meta = $meta; $this->onData = dhGlobal::getVal($meta, "onData", null); $this->onError = dhGlobal::getVal($meta, "onError", null); $this->onStart = dhGlobal::getVal($meta, "onStart", null); $this->onDone = dhGlobal::getVal($meta, "onDone", null); $this->deferred = dhGlobal::getVal($meta, "deferred",null); $this->onStreamClose = dhGlobal::getVal($meta, "onStreamClose",null); $this->onStreamError = dhGlobal::getVal($meta, "onStreamError",null); $this->useBuffer = dhGlobal::getVal($meta, "useBuffer", true); $this->isMessageFrames = dhGlobal::getVal($meta, "isMessageFrames", false); if(is_null($this->deferred)) { $this->deferred = new Deferred(); } return $this; } /** * Get the value of running * @return bool */ public function getRunning() { return $this->running; } /** * Set the value of running * @param bool $running * @return self */ public function setRunning($running) { $this->running = $running; return $this; } /** * Get the value of done * @return bool */ public function getDone() { return $this->done; } /** * Set the value of done * @param bool $done * @return self */ public function setDone($done) { $this->done = $done; return $this; } /** * Set the Deferred that will be resolved on completion * @param Deferred $deferred * @return $this */ public function setDeferred(Deferred $deferred) { $this->deferred = $deferred; return $this; } public function jsonSerialize($array=null) { if(is_null($array)) { $array = $this->get(); } return $array; } public function __toString() { return json_encode($this); } public function setMetaData($key,$val="",$append=false) { if(strpos($key,".") !== false) { if($append) { $check = dhGlobal::getDot($this->metaData,$key); if(!is_null($check)) { if(is_array($check)) { $check[] = $val; $val = $check; } else { $narr = []; $narr[] = $check; $narr[] = $val; $val = $narr; } } } dhGlobal::dotAssign($this->metaData,$key,$val); } else { if(isset($this->metaData[$key]) && $append) { if(is_array($this->metaData[$key])) { $this->metaData[$key][] = $val; } else { $temp = $this->metaData[$key]; $array[$key] = []; $array[$key][] = $temp; $array[$key][] = $val; } } else { $this->metaData[$key] = $val; } } return $this; } public function getMetaData($key=null,$default=null,$exists=false) { if(is_null($key)) { if($exists) { return !empty($this->metaData) ? true : false; } else { return !empty($this->metaData) ? $this->metaData : $default; } } if(strpos($key,".") !== false) { $uniqueid = uniqid("getArray",true); if(($check = dhGlobal::getDot($this->metaData,$key,$uniqueid)) !== $uniqueid) { return $exists ? true : $check; }; } if($exists) { return isset($this->metaData[$key]); } else { return isset($this->metaData[$key]) ? $this->metaData[$key] : $default; } } public function metaDataExists($key) { return $this->getMetaData($key,null,true); } }src/multithread/process/Work.php000064400000014241144761607200012777 0ustar00id = uniqid(); $this->callable = $serializeableCallable; $this->args = dhGlobal::getVal($options, "args", null); $this->asJson = dhGlobal::getVal($options, "asJson", false); $this->onData = dhGlobal::getVal($options, "onData", null); $this->onError = dhGlobal::getVal($options, "onError", null); $this->onStart = dhGlobal::getVal($options, "onStart", null); $this->onDone = dhGlobal::getVal($options, "onDone", null); $this->deferred = dhGlobal::getVal($options, "deferred", null); if(is_null($this->deferred)) { $this->deferred = new Deferred(); } } public function getId() { return $this->id; } public function getPriority() { return $this->priority; } public function isDone() { return $this->done; } public function isRunning() { return $this->running; } public function isReady() { return $this->ready; } public function start() { $this->onStart(); } public function done($result) { $this->result = $result; $this->onDone(); } public function error($data=null) { $this->onError($data); } public function terminate() { } public function getWork() { $packet = []; $packet["id"] = $this->id; $packet["callable"] = $this->callable; $packet["args"] = $this->args; $packet["asJson"] = $this->asJson; return $packet; } /** * Get the value of result * * @return mixed */ public function getResult() { return $this->result; } /** Handler Methods */ public function onDone() { $this->done = true; $this->running = false; $this->ready = false; $this->deferred->resolve($this->result); if(!is_null($this->onDone)) { $handler = $this->onDone; $handler($this,$this->result); } } public function onError($chunk=null) { if(!is_null($this->onError)) { $handler = $this->onError; $handler($this,$chunk); } $this->deferred->reject($chunk); $this->buffer->err($chunk); } public function onData($chunk=null) { if(!is_null($this->onData)) { $handler = $this->onData; $handler($this,$chunk); } $this->buffer->out($chunk); } public function onStart() { $this->running = true; $this->ready = false; if(!is_null($this->onStart)) { $handler = $this->onStart; $handler($this); } } /** * Get the value of running * @return bool */ public function getRunning() { return $this->running; } /** * Set the value of running * @param bool $running * @return self */ public function setRunning($running) { $this->running = $running; return $this; } /** * Get the value of done * @return bool */ public function getDone() { return $this->done; } /** * Set the value of done * @param bool $done * @return self */ public function setDone($done) { $this->done = $done; return $this; } /** * Set the Deferred that will be resolved on completion * @param Deferred $deferred * @return $this */ public function setDeferred(Deferred $deferred) { $this->deferred = $deferred; return $this; } /** * Get the value of deferred * * @return mixed */ public function getDeferred() { return $this->deferred; } /** @return \React\Promise\Promise */ public function Promise() { return $this->deferred->promise(); } /** @return \boru\dhutils\multithread\process\Process */ public function Process() { return $this->process; } /** * Wrapper for \React\Promise\Promise::then() * @param callable|null $onFulfilled * @param callable|null $onRejected * @return \React\Promise\PromiseInterface */ public function then(callable $onFulfilled=null,callable $onRejected=null) { return $this->Promise()->then($onFulfilled,$onRejected); } public function run($options=[]) { $worker = dhGlobal::getVal($options,"worker",null); $bootstrap = dhGlobal::getVal($options,"bootstrap",null); if(($debugPackets = dhGlobal::getVal($options,"debugPackets",false)) === false) { $debugPackets = dhGlobal::getVal($options,"debug",false); } if(is_null($worker)) { $worker = new Worker($bootstrap,null,$debugPackets); } $worker->startWork($this); $this->then(function($result) use ($worker) { $worker->stop(); }); return $this; } }src/multithread/process/Worker.php000064400000013473144761607200013334 0ustar00id = uniqid(); $this->bootstrap = $bootstrap; $this->queue=$queue; $this->debugPackets = $debugPackets; $this->process = new Process("php -f ".__DIR__."/../util/Worker.php",[],$this->getProcessMeta()); } public function isDone() { return $this->done; } public function isRunning() { return $this->running; } public function isReady() { return $this->ready; } public function start() { if(!$this->process->isRunning()) { $this->process->start(); } } public function stop() { $this->process->stop(); } private function onStart() { $this->ready=true; $this->running=false; $this->done=false; $this->onDoneDeferred = new Deferred(); if(!is_null($this->bootstrap)) { $this->sendPacket(["bootstrap"=>$this->bootstrap]); } } private function onData(Process $process,$chunk=null) { $this->processPacket($chunk); } private function onDone(Process $process,$exitCode=null,$termSignal=null) { $this->running = false; $this->ready = false; $this->done = true; $this->onDoneDeferred->resolve($exitCode); } private function sendPacket($data) { $frame = WorkerFrame::fromData($data); $packet = $frame->getPacket(); $this->packetDebug("sent",$packet,$data); $this->process->write($packet."\n"); } public function startWork(Work $work) { if(!$this->process->isRunning()) { $this->process->start(); } $this->ready=false; $this->running=true; $this->done=false; $this->internalDeferred = new Deferred(); $promise = $this->internalDeferred->promise(); if(is_null($this->bootstrap) || $this->bootstrapped) { $this->internalDeferred->resolve(true); } $this->setWork($work); $promise->then(function() { $data = $this->work->getWork(); $this->sendPacket($data); }); } public function doneWork($output) { $this->ready=true; $this->running=false; $this->done=false; if(!is_null($this->queue)) { $this->queue->finishedWork($this->work); } $workResult = new WorkResult($output); $this->work->done($workResult); } private function processPacket($framedPacket) { $frame = WorkerFrame::fromPacket($framedPacket); $this->packetDebug("received",$framedPacket,$frame->get()); $this->processOutput($frame->get()); } private function processOutput($output) { if(!is_null($this->bootstrap) && !$this->bootstrapped) { if(is_array($output) && isset($output["success"]) && isset($output["data"]["bootstrap"]) && $output["success"]) { $this->bootstrapped = true; if(!is_null($this->internalDeferred)) { $this->internalDeferred->resolve(true); } return; } } $this->doneWork($output); } /** * Get the value of work * * @return mixed */ public function getWork() { return $this->work; } /** * Set the value of work * * @param mixed $work * @return self */ public function setWork($work) { $this->work = $work; return $this; } /** * Get the value of id * * @return mixed */ public function getId() { return $this->id; } /** * Set the value of id * * @param mixed $id * @return self */ public function setId($id) { $this->id = $id; return $this; } private function getProcessMeta() { return [ "onStart"=>function(Process $process) { dhGlobal::trace("[worker]",$this->getId(),"started"); $data = $process->Buffer()->read(true,false); $this->onStart(); }, "onDone"=>function(Process $process,$exitCode=null,$termSignal=null) { dhGlobal::trace("[worker]",$this->getId(),"done"); $this->onDone($process,$exitCode,$termSignal); }, "onData"=>function(Process $process,$chunk=null) { $this->onData($process,$chunk); }, ]; } private function packetDebug($direction,$packet,$data) { if($this->debugPackets) { dhGlobal::outLine("\n[worker-packet]",$direction,dhGlobal::padLeft("",15,"*"),"\npacket:",$packet,"\ndata::",json_encode($data),"\n[worker-packet]",$direction,dhGlobal::padLeft("",15,"*")."\n "); } } }src/multithread/util/ClassMethod.php000064400000005465144761607200013572 0ustar00$v) { if($v == "debug") { unset($argv[$i]); $debug=true; } elseif($v == "preload") { unset($argv[$i]); $preload=true; } elseif(substr($v,0,1) == "#") { unset($argv[$i]); $preloadPackets[] = $v; } } } if(!$preload) { $preloadPackets = null; } $utilWorker = new UtilWorker($preloadPackets,$debug); class UtilWorker { public $delay = 0.1; public $bootstrap; private $lines = []; private $stop = false; private $debug = false; private $startTime; public function __construct($preloadPackets=null,$debug=false) { $this->debug = $debug; stream_set_blocking(STDIN, 0); if(!is_null($preloadPackets) && is_array($preloadPackets)) { foreach($preloadPackets as $packet) { $this->processLine($packet."\n"); } } while(!$this->stop) { $this->loop(); usleep($this->delay*1000000); } } public function loop() { while($line = fgets(STDIN)) { if(!empty($line)) { $this->processLine($line); } } } public function processLine($line) { if(substr($line,-1) != "\n") { $this->lines[] = $line; } else { $framedPacket = ""; if(!empty($lines)) { $framedPacket = implode("",$this->lines); } $framedPacket.=trim($line); return $this->processPacket($framedPacket); } } public function processPacket($framedPacket) { $frame = WorkerFrame::fromPacket($framedPacket); $this->debug("[received]",json_encode($frame->get())); if(!$frame->valid()) { return $this->respondWithError("frameFailure","unable to parse message frame"); } if(($bootstrap = $frame->get("bootstrap",false)) !== false) { return $this->bootstrap($bootstrap); } elseif(($callable = $frame->get("callable",false)) !== false) { $args = $frame->get("args",[]); $asJson = $frame->get("asJson",false); $workId = $frame->get("id",false); return $this->execute($callable,$args,$asJson,$workId); } else { print_r($frame->get()); } } private function execute($callable,$args,$asJson=false,$workId) { if(!is_array($args)) { $args = [$args]; } $result = $output = ""; $trace = null; $success = false; if(!is_callable($callable)) { $stringCallable = is_array($callable) ? implode("::",$callable) : $callable; return $this->respondWithError("not_callable","$stringCallable is not callable",null); } ob_start(); try { $result = call_user_func($callable,...$args); $success = true; } catch (\Exception $e) { $success=false; $result = $e->getMessage(); $trace = $e->getTrace(); //return $this->respondWithError("execute_exception",$e->getMessage(),$e->getTrace()); } $output = ob_get_contents(); ob_end_clean(); if($success && $asJson && !is_array($result)) { $result = json_decode($result,true); } if(!$success) { return $this->respondWithError("execute_exception",$result,$trace); } return $this->respondWithSuccess(["id"=>$workId,"callable"=>$callable,"data"=>$result,"stdout"=>$output]); } private function bootstrap($bootstrap) { if(!is_null($this->bootstrap)) { return $this->respondWithError("bootstrapped_already","already bootstrapped with ".json_encode($this->bootstrap)); } $bootstrapFile = false; $setupCallable = false; $setupCallableArgs = []; if(!is_array($bootstrap)) { $bootstrapFile = $bootstrap; } elseif(isset($bootstrap["file"]) || isset($bootstrap["callable"])) { $bootstrapFile = isset($bootstrap["file"]) ? $bootstrap["file"] : false; $setupCallable = isset($bootstrap["callable"]) ? $bootstrap["callable"] : false; $setupCallableArgs = isset($bootstrap["args"]) ? $bootstrap["args"] : []; } else { return $this->respondWithError("bootstrap_invalid","Bootstrap must either be a filename or an array that includes 'file' and/or 'callable'"); } if(!__bootstrap_include($bootstrapFile)) { return $this->respondWithError("bootstrap_not_found","File not found: ".$bootstrap); } if(($callableResponse = __bootstrap_call($setupCallable,$setupCallableArgs)) === false) { if(is_array($setupCallable)) { $setupCallable = implode("::",$setupCallable); } return $this->respondWithError("bootstrap_not_callable","Cannot call ".$setupCallable); } $this->bootstrap = $bootstrap; $output = []; if($bootstrapFile !== false) { $output["file"]=$bootstrapFile; } if($callableResponse !== false) { if($setupCallable !== false) { $output["callable"]=$setupCallable; } if($setupCallableArgs !== false) { $output["args"]=$setupCallableArgs; } $output["response"] = $callableResponse; } return $this->respondWithSuccess(["bootstrap"=>$this->bootstrap]); } public function respondWithSuccess($data) { $this->sendResponse(true,$data); return true; } public function respondWithError($code,$message="",$trace=null) { $this->sendResponse(false,["code"=>$code,"message"=>$message,"trace"=>$trace]); return false; } public function sendResponse($success=true,$data=null) { $output = []; $output["success"] = $success; if(!is_null($data)) { $output["data"] = $data; } $frame = WorkerFrame::fromData($output); $this->debug("[sending]",json_encode($frame->get())); echo $frame->getPacket()."\n"; } public function terminate($success=true) { if(!$success) { exit(1); } exit(); } private function debug(...$args) { if($this->debug) { dhGlobal::outLine(...$args); } } public static function exec(...$args) { if(!empty($args) && !is_null($args)) { $command = implode(" ",$args); passthru($command); return ["status"=>true,"command"=>$command]; } throw new \Exception("static::exec requires at least 1 arg"); return ["status"=>false]; } } function __bootstrap_include($file) { if($file === false) { return true; } if(!file_exists($file)) { return false; } include $file; return true; } function __bootstrap_call($callable,$args) { if($callable === false) { return true; } if(!is_callable($callable)) { return false; } try { $return = call_user_func($callable,...$args); } catch (\Exception $e) { $return = false; } return $return; }src/multithread/visualizer/BlankVisualizer.php000064400000000402144761607200015673 0ustar00total = !is_null($totalWorkload) ? $totalWorkload : $threads; $this->done = 0; for($i=0;$i<$threads;$i++) { $this->threadStatus[$i] = " "; } if(!is_null($statsCallback) && is_callable($statsCallback)) { $this->statsCallback = $statsCallback; } } /** * @param \boru\dhutils\dhThreads $pool the threadpool, so more work can be added if needed * @param \boru\dhutils\dhThreads\multithread\Thread $thread * @return void */ public function threadStart($pool,$thread) { if(is_null($this->startTime)) { $this->startTime = microtime(true); } parent::threadStart($pool,$thread); $this->started++; $this->threadMap[$thread->runnerId()] = $this->nextIndex(); $this->threadStatus[$this->threadMap[$thread->runnerId()]] = '.'; if($this->started>$this->total) { $this->total = $this->started; } } /** * @param \boru\dhutils\dhThreads $pool the threadpool, so more work can be added if needed * @param \boru\dhutils\dhThreads\multithread\Thread $thread * @return void */ public function threadFinished($pool,$thread) { parent::threadFinished($pool,$thread); $this->done++; $this->threadStatus[$this->threadMap[$thread->runnerId()]] = 'x'; unset($this->threadMap[$thread->runnerId()]); $this->display(); } /** * @param \boru\dhutils\dhThreads $pool * @return void */ public function loop($pool) { parent::loop($pool); $this->display(); } /** * @param mixed $data * @return void */ public function collect($data) { parent::collect($data); if(is_callable($this->statsCallback)) { $cb = $this->statsCallback; $this->collectStats = $cb($data,$this); } } public function display() { if(!$this->canDisplay()) { return; } $extraStats=""; if(!empty($this->collectStats)) { $arr = []; foreach($this->collectStats as $k=>$v) { $arr[] = "$k: $v"; } $extraStats = implode(", ",$arr); } $this->lastDisplayTime = microtime(true); dhGlobal::outLine(implode(" ",$this->threadStatus)." ".$this->formatPercent(),$this->formatRuntime(),$extraStats); } protected function formatPercent() { $percent = (float) $this->done / (float) $this->total; $percent *= 100; $percent = "%".dhGlobal::pad(sprintf("%01.2f",$percent),6," ",STR_PAD_LEFT); return dhGlobal::pad($percent,12," ",STR_PAD_LEFT); } protected function formatRuntime() { $runTime = $this->runTime(); if($runTime<=60) { $str = sprintf("%01.2f",$runTime)."s"; } else { $runTime = round($runTime); $dtF = new \DateTime('@0'); $dtT = new \DateTime("@$runTime"); $str = dhGlobal::dateIntervalToElapsed($dtF->diff($dtT),true,false); } return "runTime: ".dhGlobal::pad($str,6," ",STR_PAD_LEFT); } protected function canDisplay() { if(microtime(true) - $this->lastDisplayTime >= $this->getDisplayLoopTime()) { return true; } return false; } /** * time elapsed since threads started * @return float */ protected function runTime() { return !is_null($this->startTime) ? microtime(true) - $this->startTime : 0; } protected function nextIndex() { foreach($this->threadStatus as $i=>$s) { if($s == ' ') { return $i; } } foreach($this->threadStatus as $i=>$s) { if($s == 'x') { return $i; } } } /** * Get time in seconds between display prints * * @return float */ public function getDisplayLoopTime() { return $this->displayLoopTime; } /** * Set time in seconds between display prints * * @param float $displayLoopTime Time in seconds between display prints * @return self */ public function setDisplayLoopTime(float $displayLoopTime) { $this->displayLoopTime = $displayLoopTime; return $this; } }src/progress/Bar.php000064400000006526144761607200010434 0ustar00barFormat($barFormat); $this->template = new Template(); $this->generate(); } public function content() { $this->generate(); return $this->content; } public function registerMethod($methodName,callable $callable) { $this->template->registerMethod($methodName,$callable); } public function registerModifier($modifierName,callable $callable) { $this->template->registerModifier($modifierName,$callable); } private function generate() { if(is_null($this->timeStarted)) { $this->timeStarted = microtime(true); } $timeElapsed = $this->timeElapsed(); $percent = $this->get("percent",0); $remainingTime = $this->remainingTime($percent,$timeElapsed); $percentBar = $this->percentBar($percent); $this->data["elapsedTime"] = $timeElapsed; $this->data["remainingTime"] = $remainingTime; $this->data["percentBar"] = $percentBar; $this->data["memUsage"] = memory_get_usage(); $this->content = $this->template->parse($this->barFormat,$this->data); return $this->content; } public function set($key,$val="") { $this->data[$key] = $val; $this->generate(); return $this; } public function get($key=null,$default=null) { return isset($this->data[$key]) ? $this->data[$key] : $default; } public function timeElapsed() { if(is_null($this->timeStarted)) { $runTime = 0; } else { $runTime = microtime(true)-$this->timeStarted; } return $runTime; } public function remainingTime($percent=null,$elapsedTime=null) { if(is_null($elapsedTime)) { $elapsedTime = $this->timeElapsed(); } if(is_null($percent) || $elapsedTime<=0 || $percent==0) { return "N/A"; } $EC = $elapsedTime / $percent; $RC = 100 - $percent; $remainingTime = $RC * $EC; return $remainingTime; } private function percentBar($percent=null) { if(is_null($percent)) { $percent = 0; } if(is_null($this->percentBar) || $this->percentBarLastPercent != $percent) { $empty = 20; $string = ""; $filled = floor($percent/5); if($filled>0) { $string.= StdOut::ansiColor("green").str_repeat("X",$filled).StdOut::ansiColor("reset"); $empty = 20 - $filled; } if($empty>0) { $string.= StdOut::ansiColor("yellow").str_repeat(".",$empty).StdOut::ansiColor("reset"); } $this->percentBar = " |> ".$string." <| "; } return $this->percentBar; } public function barFormat($barFormat="") { $this->barFormat = $barFormat; } }src/progress/QueueStatus.php000064400000007742144761607200012221 0ustar00setThreads($threads); } $this->bar = new Bar($this->barFormat); /*$this->bar->registerMethod("testing",function($a,$b) { return "Tested!"; }); $this->bar->registerModifier("test",function($bar,$value) { return $value."123"; });*/ $this->bar->set("name",$name); $this->updateBar(); $this->statusBar = new StatusBar(0.5,$this->bar); $this->statusBar->addBar($name,$this->bar); } public function getBar() { return $this->statusBar; } public function updateThread($threadNumber,$key,$value) { $this->threadData[$threadNumber][$key] = $value; usort($this->threadData,function($a,$b) { if($a["display"] == "" || $a["display"] == "" || $a["display"] == "") { $at=-1; } else { $at = isset($a["time"]) ? microtime(true)-$a["time"] : 0; } if($b["display"] == "" || $b["display"] == "" || $b["display"] == "") { $bt=-1; } else { $bt = isset($b["time"]) ? microtime(true)-$b["time"] : 0; } if ($at==$bt) return 0; return ($at<$bt)?1:-1; }); } public function update($done,$active=0,$queued=0,$expected=0) { if(is_null($this->timeStarted)) { $this->statusBar->start(); $this->timeStarted = microtime(true); } if($expected==0) { $expected = $done+$queued+$active; } $this->total = $expected; $this->remaining = $this->total - $done; if($done>0) { $this->percent = ($done / $this->total) * 100; } else { $this->percent = 0; } $this->updateBar(); } public function stop() { $this->statusBar->stop(); } public function draw() { $this->statusBar->draw(); } private function updateBar() { $this->bar->set("percent", number_format($this->percent,2)); $this->bar->set("remaining", $this->remaining); $this->bar->set("total", $this->total); $this->bar->set("threadData", $this->threadData); } public function updateInterval($updateInterval) { $this->statusBar->updateInterval($updateInterval); return $this; } public function showExec($showExec) { $this->statusBar->showExec($showExec); return $this; } public function showMem($showExec) { $this->statusBar->showMem($showExec); return $this; } public function setBarFormat($barFormat) { $this->barFormat = $barFormat; $this->bar->barFormat($this->barFormat); } public function setThreads($threads) { $this->threadData = array_fill(0,$threads,["time"=>null,"display"=>""]); $this->bar->set("threadDataTemplate","[{time|since|timeFormat} {display|max:10|pad:10}]"); if(!$this->threadsAdded) { $this->barFormat .= " {threadData}"; $this->threadsAdded=true; $this->bar->barFormat($this->barFormat); } } }src/queue/Queue.php000064400000015253144761607200010271 0ustar00max = $max; $this->delay = $delay; if(!is_null($visualizer) && is_object($visualizer)) { $this->visualizer = $visualizer; } } /** * * @param QueueableInterface $item * @return QueueableInterface */ public function queue(QueueableInterface $item) { $this->queue[] = $item; return $item; } public function run($ticks=0) { $this->numTicks = 0; $this->limitTicks = $ticks; Loop::addPeriodicTimer($this->delay, function($timer) { $this->loopHandler(); if(!$this->hasRunning() && !$this->hasQueued()) { Loop::cancelTimer($timer); } if(!$this->tickCounter()) { Loop::cancelTimer($timer); Loop::stop(); dhGlobal::outLine("Stopped after",$this->numTicks); } }); } public function runOne() { return $this->startOne(); } public function tickCounter() { $this->numTicks++; //dhGlobal::outLine("tick",$this->numTicks,$this->limitTicks); if($this->limitTicks>0 && $this->numTicks>=$this->limitTicks) { return false; } return true; } /** * Return the next item in the queue, if available, otherwise false * * @return QueueableInterface|false */ public function next() { return $this->loopHandler(); } public function setFinished(QueueableInterface $item) { if(isset($this->running[$item->getId()])) { unset($this->running[$item->getId()]); } $this->visualizerFinished($item->getId()); $this->finished[$item->getId()] = $item; $this->visualizerDisplay(); } public function loopHandler($pull=false) { if(!$pull) { $this->checkFinished(); } $item = null; if($this->hasQueued() && $this->hasRoom()) { $item = $this->startOne(); } $this->visualizerDisplay(); return is_null($item) ? false : $item; } public function hasQueued() { return count($this->queue)>0 ? true : false; } public function hasRunning() { return count($this->running)>0 ? true : false; } public function hasRoom() { if($this->max <= 0) { return true; } return count($this->running)<$this->max ? true : false; } public function getRunning() { return $this->running; } public function getQueued() { return $this->queued; } public function getDone() { return $this->finished; } private function startOne() { if(!empty($this->queue) && $this->hasRoom()) { if(is_null($this->startTime)) { $this->startTime = microtime(true); } $item = array_shift($this->queue); $this->running[$item->getId()] = $item; $item->start(); $this->visualizerStarted($item->getId()); return $item; } return false; } private function checkFinished() { if(!empty($this->running)) { foreach($this->running as $id=>$item) { if($item->isDone()) { $this->visualizerFinished($id); $this->finished[$id] = $item; unset($this->running[$id]); } } } } public function visualizerDisplay() { if(!is_null($this->visualizer)) { $this->visualizer->display(); } } private function visualizerStarted($id) { if(!is_null($this->visualizer)) { $this->visualizer->started($id); } } private function visualizerFinished($id) { if(!is_null($this->visualizer)) { $this->visualizer->finished($id); } } public function max($max=null) { return is_null($max) ? $this->getMax() : $this->setMax($max); } public function delay($delay=null) { return is_null($delay) ? $this->getDelay() : $this->setDelay($delay); } public function startTime($startTime=null) { return is_null($startTime) ? $this->getStartTime() : $this->setStartTime($startTime); } public function visualizer($visualizer=null) { return is_null($visualizer) ? $this->getVisualizer() : $this->setVisualizer($visualizer); } /** * Get the value of max * * @return mixed */ public function getMax() { return $this->max; } /** * Set the value of max * * @param mixed $max * @return self */ public function setMax($max) { $this->max = $max; return $this; } /** * Get the value of delay * * @return mixed */ public function getDelay() { return $this->delay; } /** * Set the value of delay * * @param mixed $delay * @return self */ public function setDelay($delay) { $this->delay = $delay; return $this; } /** * Get the value of visualizer * * @return mixed */ public function getVisualizer() { return $this->visualizer; } /** * Set the value of visualizer * * @param mixed $visualizer * @return self */ public function setVisualizer($visualizer) { $this->visualizer = $visualizer; return $this; } /** * Get the value of startTime * * @return mixed */ public function getStartTime() { return $this->startTime; } /** * Set the value of startTime * * @param mixed $startTime * @return self */ public function setStartTime($startTime) { $this->startTime = $startTime; return $this; } }src/queue/QueueableInterface.php000064400000000454144761607200012733 0ustar00setMax($max); if(!is_null($delay)) { $this->setDelay($delay); } if(!is_null($expected)) { $this->setExpected($expected); } $this->data = array_fill(0,$max,' '); } public function max($max=null) { return is_null($max) ? $this->getmax() : $this->setmax($max); } public function started($itemId) { return $this->startItem($itemId); } public function finished($itemId) { return $this->endItem($itemId); } public function display() { if(!$this->canDisplay()) { return; } $this->lastTime = microtime(true); dhGlobal::outLine(implode(" ",$this->data)," | ",implode(" | ",$this->stats())); } private function stats() { //Runtime $stats = []; $runTime = $this->formatTime($this->runTime()); //Percent complete and ETA if($this->expected > 0) { $percent = (int) $this->finished / (int) $this->expected; $percentRemaining = 1 - $percent; $percent *= 100; $percent = dhGlobal::pad(sprintf("%01.2f",$percent),6," ",STR_PAD_LEFT); $eta = $this->formatTime($this->runTime() * $percentRemaining); } else { $percent = "% N/A"; $eta = "N/A"; } $stats[] = "runTime: ".dhGlobal::pad($runTime,6," ",STR_PAD_LEFT); $stats[] = "%".dhGlobal::pad($percent,8," ",STR_PAD_LEFT); $stats[] = "eta: ".dhGlobal::pad($eta,6," ",STR_PAD_LEFT); return $stats; } private function startItem($itemId) { if(is_null($this->startTime)) { $this->startTime = microtime(true); } $index = $this->nextIndex(); $this->itemMap[$itemId] = $index; $this->data[$index] = '.'; } private function endItem($itemId) { $index = $this->itemMap[$itemId]; $this->data[$index] = 'x'; $this->finished++; } private function nextIndex() { foreach($this->data as $i=>$s) { if($s == ' ') { return $i; } } foreach($this->data as $i=>$s) { if($s == 'x') { return $i; } } } /** * time elapsed since started * @return float */ private function runTime() { return !is_null($this->startTime) ? microtime(true) - $this->startTime : 0; } private function canDisplay() { if(!$this->enabled) { return false; } if(is_null($this->lastTime)) { $this->lastTime = 0; } if(microtime(true) - $this->lastTime >= $this->delay) { return true; } return false; } private function formatTime($time) { if($time<=60) { $str = sprintf("%01.2f",$time)."s"; } else { $time = round($time); $dtF = new \DateTime('@0'); $dtT = new \DateTime("@$time"); $str = dhGlobal::dateIntervalToElapsed($dtF->diff($dtT),true,false); } return $str; } public function delay($delay=null) { return is_null($delay) ? $this->getDelay() : $this->setDelay($delay); } public function expected($expected=null) { return is_null($expected) ? $this->getExpected() : $this->setExpected($expected); } public function enabled($enabled=null) { return is_null($enabled) ? $this->getEnabled() : $this->setEnabled($enabled); } /** * Get the value of delay * * @return mixed */ public function getDelay() { return $this->delay; } /** * Set the value of delay * * @param mixed $delay * @return self */ public function setDelay($delay) { $this->delay = $delay; return $this; } /** * Get the value of expected * * @return mixed */ public function getExpected() { return $this->expected; } /** * Set the value of expected * * @param mixed $expected * @return self */ public function setExpected($expected) { $this->expected = $expected; return $this; } /** * Get the value of enabled * * @return mixed */ public function getEnabled() { return $this->enabled; } /** * Set the value of enabled * * @param mixed $enabled * @return self */ public function setEnabled($enabled) { $this->enabled = $enabled; return $this; } /** * Get the value of max * * @return mixed */ public function getMax() { return $this->max; } /** * Set the value of max * * @param mixed $max * @return self */ public function setMax($max) { $this->max = $max; return $this; } }src/scripts/thread.php000064400000001157144761607200011015 0ustar00diff($dtT),true,false,2,""); } return $str; } }src/tools/DebugTrace.php000064400000004331144761607200011221 0ustar00label = $label; $this->details = is_null($details) ? [] : $details; if(dhGlobal::get("debugTrace",false)) { dhGlobal::outLine("DEBUGTRACE"," ",". . . . ",$this->label,...$this->details); } } public static function enable() { dhGlobal::set("debugTrace",true); } public static function disable() { dhGlobal::set("debugTrace",false); } public static function traceSteps($num) { dhGlobal::set("debugTraceSteps",$num); } public static function create($label=null) { $traceInfo = static::getTraceInfo(dhGlobal::get("debugTraceSteps",2)); $notes = "called @ ".$traceInfo["caller"]["line"].":".$traceInfo["caller"]["file"]; $parts = []; $parts[] = " -> ".dhGlobal::padRight($traceInfo["item"]["func"]."()",50); $parts[] = " @ ".dhGlobal::padLeft($traceInfo["item"]["line"],4," "); $parts[] = " in ".$traceInfo["item"]["file"]; $parts[] = " ".$notes; $label = is_null($label) ? "exectimer" : $label; $label = dhGlobal::padRight($label,20); return new static($label,...$parts); } private static function getTraceInfo($stepsToDiscard=2) { $trace = debug_backtrace(); while($stepsToDiscard-- > 0) { array_shift($trace); } $output["item"] = static::getTraceEntry(array_shift($trace)); $output["caller"] = static::getTraceEntry(array_shift($trace)); return $output; } private static function getTraceEntry($traceEntry) { $trace = [ "file"=>$traceEntry['file'], "line"=>$traceEntry['line'], "func"=>$traceEntry['function'], "args"=>$traceEntry['args'], ]; //if the trace has a classname, append it to the function name if(isset($traceEntry['class'])) { $trace["func"] = $traceEntry['class'] . "::" . $traceEntry['function']; } return $trace; } }src/tools/ExecTimer.php000064400000005103144761607200011077 0ustar00timeStart = microtime(true); $this->label = $label; $this->details = is_null($details) ? [] : $details; } public function __destruct() { $this->timeEnd = microtime(true); if(dhGlobal::get("execTimer",false)) { $timeElapsed = $this->timeEnd - $this->timeStart; $timeElapsed = dhGlobal::padLeft(number_format($timeElapsed,4),8," ")."s"; dhGlobal::outLine("DEBUGTIME"," ",$timeElapsed." ",$this->label,...$this->details); } } public static function enable() { dhGlobal::set("execTimer",true); } public static function disable() { dhGlobal::set("execTimer",false); } public static function traceSteps($num) { dhGlobal::set("execTimerSteps",$num); } public static function create($label=null) { $traceInfo = static::getTraceInfo(dhGlobal::get("execTimerSteps",2)); $notes = "called @ ".$traceInfo["caller"]["line"].":".$traceInfo["caller"]["file"]; $parts = []; $parts[] = " -> ".dhGlobal::padRight($traceInfo["item"]["func"]."()",50); $parts[] = " @ ".dhGlobal::padLeft($traceInfo["item"]["line"],4," "); $parts[] = " in ".$traceInfo["item"]["file"]; $parts[] = " ".$notes; $label = is_null($label) ? "exectimer" : $label; $label = dhGlobal::padRight($label,20); return new static($label,...$parts); } private static function getTraceInfo($stepsToDiscard=2) { $trace = debug_backtrace(); while($stepsToDiscard-- > 0) { array_shift($trace); } $output["item"] = static::getTraceEntry(array_shift($trace)); $output["caller"] = static::getTraceEntry(array_shift($trace)); return $output; } private static function getTraceEntry($traceEntry) { $trace = [ "file"=>$traceEntry['file'], "line"=>$traceEntry['line'], "func"=>$traceEntry['function'], "args"=>$traceEntry['args'], ]; //if the trace has a classname, append it to the function name if(isset($traceEntry['class'])) { $trace["func"] = $traceEntry['class'] . "::" . $traceEntry['function']; } return $trace; } }src/tools/JSONL.php000064400000010643144761607200010104 0ustar00lines = dhGlobal::getVal($options,"lines",[]); $this->lineCount = dhGlobal::getVal($options,"lineCount",0); if(($file = dhGlobal::getVal($options,"file",null))!==null) { $this->setFile($file); } } public function setFile($file,$load=true) { if(is_object($file) && $file instanceof File) { $this->file = $file; if($load) { $this->loadString($file->content()); } } elseif(is_string($file)) { $this->file = File::from($file); if($load) { $this->loadString($this->file->content()); } } else { throw new \Exception("Invalid file type"); } } public function addLine($line,$save=false) { $this->lines[] = $line; $this->lineCount++; if($save && is_object($this->file) && $this->file instanceof File) { $this->file->content($this->__toString()); } } public function append($line) { if(is_object($this->file) && $this->file instanceof File) { $string = ""; if($this->lineCount>=1) { $string = "\n"; } $string.= static::encodeLine($line); $this->file->write($string,true); echo "written\n"; return true; } else { $this->addLine($line); return false; } } public function getLines() { return $this->lines; } public function getLineCount() { return $this->lineCount; } public function loadString($string) { $data = explode("\n",$string); foreach($data as $line) { $this->lines[] = json_decode($line,true); } $this->lineCount = count($this->lines); return $this; } public function toString() { return $this->__toString(); } public function __toString() { $lines = []; foreach($this->lines as $line) { $enc = static::encodeLine($line); if(!empty($enc)) { $lines[] = $enc; } } return implode("\n",$lines); } /** * @param string|File $filename * @return File */ public function toFile($filename) { if(is_object($filename) && $filename instanceof File) { $filename->write($this->__toString()); $this->file = $filename; return $filename; } else { $file = File::from($filename); $file->write($this->__toString()); $this->file = $file; return $file; } } public static function encodeLine($data) { return json_encode($data,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); } public static function appendFile($file,$line) { if(!is_object($file) && !($file instanceof File)) { $file = File::from($file); } if(is_object($file) && $file instanceof File) { if(!isset(self::$filesCache[$file->path()])) { self::$filesCache[$file->path()] = $file->size(); } else { clearstatcache(); } $string = ""; if(self::$filesCache[$file->path()]>0) { $string = "\n"; } else { self::$filesCache[$file->path()]++; } $string.= static::encodeLine($line); $file->write($string,true); return true; } else { return false; } } public static function fromFile($filename) { if(is_object($filename) && $filename instanceof File) { return new JSONL(["file"=>$filename]); } else { $file = File::from($filename); return new JSONL(["file"=>$filename]); } } public static function fromString($string) { $jsonl = new JSONL(); $jsonl->loadString($string); return $jsonl; } }src/tools/Loader.php000064400000006677144761607200010441 0ustar00classPrefix(static::$classPrefix); } if($loader !== false) { static::addLoader($loader); } } public static function addLoader($loader) { if(!is_object($loader)) { $class = "\\boru\\dhutils\\loader\\".$loader; if(class_exists($class,true)) { $loader = new $class; } } $loader->init(); static::$loaders[] = $loader; } /** * Set the baseDir restriction for the dhLoader::includeOnce method * * dhLoader::includeOnce is utilized for the TypeClass loader, and is public static * @param mixed $baseDir * @return string|void */ public static function baseDir($baseDir=null) { if(is_null($baseDir)) { return static::$baseDir; } else { static::$baseDir = dhGlobal::trimString(DIRECTORY_SEPARATOR,$baseDir,dhGlobal::TRIM_END); } } /** * Wrapper for the TypeClass loader * @param string|null $prefix * @return string|void */ public static function classPrefix(string $prefix=null) { if(is_null($prefix)) { return static::$classPrefix; } else { static::$classPrefix = $prefix; } } public static function includeOnce($qualifiedName, $fileExtension="php",$supress=false) { if(isset(static::$includeCache[$qualifiedName])) { return true; } $file = static::resolveNameToPath($qualifiedName,$fileExtension); //dhGlobal::debug("file",$file); if (!file_exists($file)) { dhGlobal::error("File Doesnt Exist - dhLoader - Error when trying to include $file"); return false; } $status = -1; if ($supress) { $status = @include_once $file; } else { $status = include_once $file; } $success = ($status === 0)? false : true; if ($success) { static::$includeCache[$qualifiedName] = $file; } return $success; } public static function resolveNameToPath($qualifiedName, $fileExtension='php') { $allowedExtensions = array('php', 'js', 'css', 'less'); $file = ''; if(!in_array($fileExtension, $allowedExtensions)) { return ''; } $baseDir = static::baseDir(); if(empty($baseDir)) { $LOADER_FILE_DIR = ""; } else { $LOADER_FILE_DIR = $baseDir . DIRECTORY_SEPARATOR; } // TO handle loading vtiger files if (strpos($qualifiedName, '~~') === 0) { $file = str_replace('~~', '', $qualifiedName); $file = $LOADER_FILE_DIR . $file; } else if (strpos($qualifiedName, '~') === 0) { $file = str_replace('~', '', $qualifiedName); $file = $LOADER_FILE_DIR . $file; } else { $file = str_replace('.', DIRECTORY_SEPARATOR, $qualifiedName) . '.' .$fileExtension; $file = $LOADER_FILE_DIR . $file; } return $file; } }src/tools/Output.php000064400000022155144761607200010520 0ustar00end(); } self::$instance = new self($stdOut,$fileName,$prefix,$prefixFormat,$prefixSpacer,$lineSeparator); return self::$instance; } /** * Used to get the singleton isntance, or init with default settings; * @return dhOut singlton */ public static function instance() { if(self::$instance === null) { self::$instance = self::init(); } return self::$instance; } public function __construct($stdOut=true,$fileName=false,$prefix=true,$prefixFormat="date:Y-m-d H:i:s",$prefixSpacer="\t",$lineSeparator=PHP_EOL) { $this->output = []; $this->setStdOut($stdOut); $this->setPrefix($prefix); $this->setPrefixFormat($prefixFormat); $this->setPrefixSpacer($prefixSpacer); $this->setFileName($fileName); $this->setLineSeparator($lineSeparator); $this->lastTime = -microtime(true); } public function __destruct() { if(!empty($this->output)) { $this->end(); } } public function toOutput($content="") { $string = ""; if(empty($content)) { $prevLine = $this->prevLine(); if(!is_null($prevLine) && strlen($prevLine) == strlen(rtrim($this->prevLine(),$this->eol()))) { //$string.=$this->eol(); } $string .= $this->currentLine(); } else { $string = $content; } $string.=$this->eol(); if($this->stdOut) { $commands = ""; StdOut::output($commands.$string); } if(!is_null($this->file) && $this->fileName !== false) { $this->file->write($string,$this->fileAppend); } } public function clear() { $this->end(); } public function a($data,$expectAppend=false) { return $this->add($data,$expectAppend); } public function line(...$args) { $parts = []; foreach($args as $arg) { $parts[] = $this->argToString($arg); } $line = $this->prefix(); $line.= $this->indent(); $line.= implode(" ",$parts); if($this->indent>0) { $line = str_replace($this->eol(),$this->eol().$this->indent(),$line); } $this->output[] = $line; $this->cursor++; $this->toOutput(); return $this; } public function add($data,$expectAppend=false) { $line = $this->prefix(); $line.= $this->indent(); $line.= $this->argToString($data); if($this->indent>0) { $line = str_replace($this->eol(),$this->eol().$this->indent(),$line); } $this->output[] = $line; $this->cursor++; $this->toOutput(); return $this; } protected function argToString($arg) { if(is_array($arg) || is_object($arg)) { return print_r($arg,true); } else { return $arg; } } public function merge(Output $otherOut) { if(!empty($this->output)) { $this->toOutput($this->eol()); } $this->toOutput($otherOut->toString($this->eol(),false)); $this->output = array_merge($this->output,$otherOut->output); } public function end() { $this->output = []; $this->cursor=0; } public function indent() { if($this->indent>0) { return str_pad("",$this->indent," "); } return ""; } private function execTime() { $this->lastTime += microtime(true); $time = round($this->lastTime,4); $this->lastTime = -microtime(true); return $time; } public function prefix() { if($this->prefix) { if($this->prefixFormat == "execTime") { return "exec:{$this->execTime()}".$this->prefixSpacer; } $tt = explode(":",$this->prefixFormat,2); if(count($tt)==2) { if($tt[0] == "date") { return date($tt[1]).$this->prefixSpacer; } } return $this->prefixFormat.$this->prefixSpacer; } return ""; } public function toFile($filename,$flags=FILE_APPEND) { file_put_contents($filename,$this->toString(),$flags); } public function toString($lineSeparator=null,$trailing=true) { if(is_null($lineSeparator)) { $lineSeparator = $this->eol(); } if($this->trailingNewLine && $trailing) { $o = $lineSeparator; } else { $o = ""; } return implode($lineSeparator,$this->output).$o; } public function __toString() { return $this->toString($this->eol()); } public function prevLine() { if($this->cursor>1) { return $this->output[$this->cursor-2]; } return null; } public function currentLine() { if($this->cursor>0) { return $this->output[$this->cursor-1]; } return null; } /** * Set the value of prefix * * @return self */ public function setPrefix($prefix) { $this->prefix = $prefix; return $this; } /** * Set the value of prefixFormat * * @return self */ public function setPrefixFormat($prefixFormat) { $this->prefixFormat = $prefixFormat; return $this; } /** * Set the value of trailingNewLine * * @return self */ public function setTrailingNewLine($trailingNewLine) { $this->trailingNewLine = $trailingNewLine; return $this; } /** * Set the value of fileName * * @return self */ public function setFileName($fileName) { if($fileName !== false) { $this->file = new File(["path"=>$fileName,"create"=>true]); } $this->fileName = $fileName; return $this; } /** * Set the value of stdOut * * @return self */ public function setStdOut($stdOut) { $this->stdOut = $stdOut; return $this; } /** * Set the value of prefixSpacer * * @return self */ public function setPrefixSpacer($prefixSpacer) { $this->prefixSpacer = $prefixSpacer; return $this; } /** * Set the value of fileAppend * * @return self */ public function setFileAppend($fileAppend) { $this->fileAppend = $fileAppend; return $this; } /** * Get the value of indent */ public function getIndent() { return $this->indent; } /** * Set the value of indent * * @return self */ public function setIndent($indent) { $this->indent = $indent; return $this; } private function eol() { return $this->lineSeparator; } /** * Get the value of lineSeparator * * @return mixed */ public function getLineSeparator() { return $this->lineSeparator; } /** * Set the value of lineSeparator * * @param mixed $lineSeparator * @return self */ public function setLineSeparator($lineSeparator) { $this->lineSeparator = $lineSeparator; return $this; } /** * * @param string|string[] $char a string or array of strings with 'start', 'clear', and/or 'up' as values * @return string|void */ private function ansiChar($char="start|clear|up") { if(!is_array($char)) { if($char == "start") { return "\r"; } if($char == "clear") { return "\033[K"; } if($char == "up") { return "\033[1A"; } } else { $chars = []; foreach($char as $c) { $chars[] = $this->ansiChar($c); } return implode("",$chars); } } }src/tools/ProcessQueue.php000064400000014260144761607200011641 0ustar00max = $max; $this->delay = dhGlobal::getVal($options,"delay",$this->delay); $this->expected = dhGlobal::getVal($options,"expected",$this->expected); $this->visualize = dhGlobal::getVal($options,"visualize",false); $this->visualizerDelay = dhGlobal::getVal($options,"visualizerDelay",$this->visualizerDelay); //ClassProcess shortcuts $this->callable = dhGlobal::getVal($options,"callable",null); $this->bootstrap = dhGlobal::getVal($options,"bootstrap",null); $this->initQueue($queue,$visualizer); } public function initQueue($queue=null,$visualizer=null) { if(is_null($queue)) { $this->queue = new Queue($this->max,$this->delay); } else { $this->queue = $queue; } if(method_exists($this->queue,"setVisualizer")) { if(is_null($visualizer)) { if($this->visualize) { $visualizer = new Visualizer($this->max,$this->visualizerDelay,$this->expected); $this->queue->setVisualizer($visualizer); } } else { $this->queue->setVisualizer($visualizer); } } } public function newClassProcess($args=null,$queue=false,callable $then=null) { $cp = new ClassProcess($this->callable,$args,$this->bootstrap); if(!is_null($then)) { $cp->then($then); } if($queue) { $this->queue($cp->Process(),true); } return $cp; } /** * * @param mixed $command * @param array $args * @param array $meta * @param bool $start * @return Process * @throws RuntimeException */ public function queueCommand($command,$args=[],$meta=[],$start=false) { $process = new Process($command,$args,$meta); return $this->queue($process,$start); } /** * * @param Process $process * @param bool $start * @return Process * @throws RuntimeException */ public function queue(Process $process,$start=false) { $this->queue->queue($process); if($start) { $this->queue->runOne(); } return $process; } public function start($ticks=0) { $this->queue->run($ticks); } public function wait() { $this->start(); Loop::run(); } /** * * @param callable|null $onResult * @return array|true */ public function collect(callable $onResult=null) { $this->wait(); $results = true; if(is_null($onResult)) { $results = []; $onResult = function(Process $process) { return $process->Buffer()->readOut(); }; } $finished = $this->queue->getDone(); if(is_array($finished) && !empty($finished)) { foreach($finished as $process) { if(is_array($results)) { $results[] = $onResult($process); } else { $onResult($process); } } } return $results; } /** * Set the value of max * * @param mixed $max * @return self */ public function setMax($max) { $this->max = $max; return $this; } /** * Set the value of delay * * @param mixed $delay * @return self */ public function setDelay($delay) { $this->delay = $delay; return $this; } /** * Set the value of expected * * @param mixed $expected * @return self */ public function setExpected($expected) { $this->expected = $expected; return $this; } /** * Set the value of visualize * * @param mixed $visualize * @return self */ public function setVisualize($visualize) { $this->visualize = $visualize; return $this; } /** * Set the value of visualizerDelay * * @param mixed $visualizerDelay * @return self */ public function setVisualizerDelay($visualizerDelay) { $this->visualizerDelay = $visualizerDelay; return $this; } /** * * @param mixed $command * @param array $args * @param array $meta * @return Process */ public static function Process($command,$args=[],$meta=[]) { $process = new Process($command,$args,$meta); return $process; } public static function ClassProcess($callable,$args=null,$bootstrap=null) { $process = new ClassProcess($callable,$args,$bootstrap); return $process; } public static function pack($data) { if(is_array($data) || is_object($data)) { $data = json_encode($data); } return base64_encode($data); } public static function unpack($packedData,$object=false) { $data = base64_decode($packedData); $test = json_decode($data,!$object); if(is_array($test) || is_object($test)) { return $test; } return $data; } }src/tools/ScriptLock.php000064400000002634144761607200011275 0ustar00lockPath = $lockPath; $this->lockFile = $lockFile; } public function __destruct() { if(file_exists($this->lockFile())) { unlink($this->lockFile()); } } public function lockFile() { if(substr($this->lockPath,-strlen(DIRECTORY_SEPARATOR)) !== DIRECTORY_SEPARATOR) { $this->lockPath.=DIRECTORY_SEPARATOR; } return $this->lockPath.$this->lockFile; } public function isLocked() { # If lock file exists, check if stale. If exists and is not stale, return TRUE # Else, create lock file and return FALSE. if (file_exists($this->lockFile())) { # check if it's stale $lockingPID = trim(file_get_contents($this->lockFile())); # Get all active PIDs. $pids = explode("\n", trim(`ps -e | awk '{print $1}'`)); # If PID is still active, return true if (in_array($lockingPID, $pids)) return true; # Lock-file is stale, so kill it. Then move on to re-creating it. unlink($this->lockFile()); } file_put_contents($this->lockFile(), getmypid() . "\n"); return false; } }src/tools/StatusBar.php000064400000016363144761607200011134 0ustar00lines = $this->getLines(); $this->cols = $this->getCols(); $this->updateInterval = $updateInterval; } public function __destruct() { $this->destroyScrollArea(); if($this->scrollAreaCreated) { $this->destroyScrollArea(); } } public function addBar($name,$bar) { $this->bars[$name] = $bar; $this->progressBarLines = count($this->bars); if($this->started && $this->active) { $this->clearLinesCache=null; $this->setupScrollArea(true); } } public function removeBar($name) { if(isset($this->bars[$name])) { unset($this->bars[$name]); } $this->progressBarLines = count($this->bars); if($this->started && $this->active) { $this->clearLinesCache=null; $this->setupScrollArea(true); } } public function start() { if($this->progressBarLines<=0) { return; } $this->started=true; $this->active=true; $this->setupScrollArea(); $this->draw(); Loop::addPeriodicTimer($this->updateInterval,function($timer) { if($this->active) { $this->draw(); if(is_null($this->timer)) { $this->timer = $timer; } } else { Loop::cancelTimer($timer); $this->stop(); } }); } public function stop() { $this->started = false; $this->active = false; //StdOut::line($this->getBarContent()); if(!is_null($this->timer) && $this->timer instanceof \React\EventLoop\TimerInterface) { Loop::cancelTimer($this->timer); } $this->timer = null; if($this->scrollAreaCreated) { $this->destroyScrollArea(); } } public function draw() { if(!$this->active) { return; } if($this->progressBarLines<=0) { $this->stop(); return; } if(!$this->started) { $this->setupScrollArea(); $this->started=true; } $this->checkResetScrollArea(); $this->drawStatusBars(); } private function drawStatusBars($content=null) { if(is_null($content)) { $content = $this->getBarContent(); } $this->printStatusBar($content); } private function printStatusBar($content=null) { //echo StdOut::CODE_SAVE_CURSOR; echo "\033[s"; echo $this->clearLines(); if(!is_null($content)) { echo $content; } //echo StdOut::CODE_RESTORE_CURSOR; echo "\033[u"; } public function getBarContent() { $start = microtime(true); $content = []; foreach($this->bars as $bar) { $content[] = $bar->content(); } $this->getCols(5); $this->barContent = implode("\n",$content); $endContent = ""; if($this->showExec) { $end = microtime(true); $exec = $end-$start; $endContent.=" [exec: ".number_format($exec,5)."]"; } if($this->showMem) { $mem = memory_get_usage(); $endContent.=" [mem: ".dhGlobal::formatBytes($mem,"%01.2f%s")."]"; } $maxLen = $this->getCols(strlen($endContent)+3); return substr($this->barContent,0,$maxLen) . $endContent; } private function checkResetScrollArea() { $curLines = $this->getLines(); $newLines = $this->getLines(0,true); if($curLines != $newLines) { $this->setupScrollArea(true); $this->clearLinesCache=null; } } private function clearLines() { if(is_null($this->clearLinesCache)) { $this->clearLinesCache = ""; if($this->progressBarLines>0) { $i = 0; while($i<$this->progressBarLines) { $lineNum = $this->getLines($i); $this->clearLinesCache.="\033[".$lineNum.";0f"; $this->clearLinesCache.=StdOut::ERASE_LINE_AFTER; $i++; } } } return $this->clearLinesCache; } private function setupScrollArea($force=false) { if($this->scrollAreaCreated && !$force) { return; } echo str_repeat("\n",$this->progressBarLines); //save cursor position echo StdOut::CODE_SAVE_CURSOR; echo "\033[s"; //set scroll region echo "\033[0;".$this->getLines($this->progressBarLines,true)."r"; echo StdOut::CODE_RESTORE_CURSOR; //move to scroll area echo str_repeat(StdOut::MOVE_UP,$this->progressBarLines); $this->scrollAreaCreated=true; } private function destroyScrollArea($force=false) { if(!$this->scrollAreaCreated && !$force) { return; } echo $this->clearLines(); echo StdOut::CODE_SAVE_CURSOR; echo "\033[0;".$this->getLines(0,true)."r"; echo StdOut::CODE_RESTORE_CURSOR; //move to scroll area echo str_repeat(StdOut::MOVE_UP,$this->progressBarLines); //$this->printStatusBar(); echo StdOut::ERASE_LINE_AFTER.$this->getBarContent(); $this->scrollAreaCreated=false; echo str_repeat("\n",$this->progressBarLines); //echo $this->getBarContent()."\n"; } private function getLines($offset=0,$refresh=false) { if(is_null($this->lines) || $refresh) { $this->lines = exec("tput lines"); } return intval($this->lines-$offset); } private function getCols($offset=0,$refresh=false) { if(is_null($this->cols) || $refresh) { $this->cols = exec("tput cols"); } return intval($this->cols-$offset); } /** * @param float $updateInterval * @return $this */ public function updateInterval($updateInterval) { $this->updateInterval=$updateInterval; return $this; } /** * @param bool $showExec * @return $this */ public function showExec($showExec) { $this->showExec=$showExec?true:false; return $this; } /** * @param bool $showExec * @return $this */ public function showMem($showMem) { $this->showMem=$showMem?true:false; return $this; } }src/tools/StdOut.php000064400000013417144761607200010443 0ustar00drawProgressBar(); } public function drawProgressBar() { if(!is_null($this->progressBar)) { $this->progressBar->draw(); } } /** * STATIC METHODS */ /** * Initialize (if not already) and return the singleton instance. To replace using the $force input, either an instance of StdOut or boolean true. * @param StdOut|true|null $force * @return StdOut */ public static function init($force=null) { if(is_object($force)) { static::$instance = $force; } elseif(is_null(static::$instance) || $force===true) { static::$instance = new self(); } return static::$instance; } /** * Print content to the screen -- does not include a newline automatically. * @param string $content * @return void */ public static function output($content) { $instance = static::init(); $instance->write($content); } public static function drawProgress() { $instance = static::init(); $instance->drawProgressBar(); } /** * Generate a line from one or more inputs, applies a newline automtically * then prints the line to StdOut::output() * @param mixed $parts * @return void */ public static function line(...$parts) { if(!is_array($parts)) { $parts = [$parts]; } foreach($parts as $k=>$part) { $parts[$k] = StdOut::argToString($part); } $content = implode(" ",$parts); static::init(); $content = rtrim($content) . static::$eol; static::output($content); } public static function argToString($arg,$json=false) { if(is_array($arg) || is_object($arg)) { return $json ? json_encode($arg) : print_r($arg,true); } else { return $arg; } } /** * * @param string|string[] $char a string or array of strings with 'start', 'clear', and/or 'up' as values * @return string|void */ public static function ansiChar($char="start|clear|up|color",$extra=null) { if(!is_array($char)) { if($char == "start") { return "\r"; } if($char == "clear") { return "\033[2K"; } if($char == "up") { return "\033[1A"; } if($char == "color") { return is_null($extra) ? : "\033[0m"; "\033[38;5;".$extra."m"; } } else { $chars = []; foreach($char as $c) { $chars[] = static::ansiChar($c); } return implode("",$chars); } } public static function ansiReset() { } public static function ansiColor($color,$bg=false) { $colorArray["black"] = [30,40]; $colorArray["red"] = [31,41]; $colorArray["green"] = [32,42]; $colorArray["yellow"] = [33,43]; $colorArray["blue"] = [34,44]; $colorArray["magenta"] = [35,45]; $colorArray["cyan"] = [36,46]; $colorArray["white"] = [37,47]; $colorArray["default"] = [39,49]; $colorArray["reset"] = [0,0]; if(isset($colorArray[$color])) { $code = $bg? $$colorArray[$color][1] : $colorArray[$color][0]; return "\033[".$code."m"; } return ""; } /** * Getters/Setters */ /** * Get the value of progressBar * @return mixed */ public function getProgressBar() { return $this->progressBar; } /** * Set the value of progressBar * @param mixed $progressBar * @return self */ public function setProgressBar($progressBar) { $this->progressBar = $progressBar; return $this; } /** * * @param StatusBar $progressBar * @return void */ public static function progressBar($progressBar) { $instance = static::init(); $instance->setProgressBar($progressBar); } public static function sigint($sigintVal=2) { if(!is_null(static::$instance->getProgressBar())) { static::$instance->getProgressBar()->stop(); } exit(); } public static $cols; public static $lines; public static function getLines($offset=0,$refresh=false) { if(is_null(static::$lines) || $refresh) { static::$lines = exec("tput lines"); } return intval(static::$lines-$offset); } public static function getCols($offset=0,$refresh=false) { if(is_null(static::$cols) || $refresh) { static::$cols = exec("tput cols"); } return intval(static::$cols-$offset); } } if(function_exists('pcntl_signal')) { pcntl_signal(SIGINT, function() { StdOut::sigint(SIGINT); }); }src/tools/Template.php000064400000012103144761607200010763 0ustar00customMethods[$methodName] = $callable; } public function registerModifier($modifierName,callable $callable) { $this->customModifiers[$modifierName] = $callable; } public function parse($content,$data=[]) { return preg_replace_callback($this->functionParser,function($matches) use ($data) { return $this->variableCallback($matches,$data); },$content); } private function variableCallback($matches,$dataArray=[]) { array_shift($matches); array_shift($matches); $variable = !empty($matches) ? array_shift($matches) : false; $modifiers = []; while(!empty($matches)) { $mod = array_shift($matches); if(!empty($mod)) { $modifiers[] = $mod; } } $value = ""; if($variable !== false && !empty($variable)) { if(isset($dataArray[$variable]) && !is_array($dataArray[$variable])) { $value = $dataArray[$variable]; } elseif(isset($dataArray[$variable]) && is_array($dataArray[$variable])) { if(isset($dataArray[$variable."Template"])) { $tpl = $dataArray[$variable."Template"]; $valueArr = []; foreach($dataArray[$variable] as $vars) { $valueArr[] = preg_replace_callback($this->functionParser,function($matches) use ($vars) { return $this->variableCallback($matches,$vars); },$tpl); } $value = implode(" ",$valueArr); } } elseif(isset($this->customMethods[$variable]) && is_callable($this->customMethods[$variable])) { $value = $this->customMethods[$variable]($this,$dataArray); } } if(!empty($modifiers)) { foreach($modifiers as $modifier) { /*if($modifier == "timeFormat") { $value = $this->formatTime($value); } elseif($modifier == "since") { if(is_null($value) || empty($value)) { $value = 0; } else { $value = microtime(true)-$value; } } elseif(strstr($modifier,":") !== false) { $value = $this->parseFunction($modifier,$value); } elseif(isset($this->customModifiers[$modifier]) && is_callable($this->customModifiers[$modifier])) { $value = $this->customModifiers[$modifier]($this,$value); }*/ $value = $this->parseModifier($modifier,$value); } } return $value; } private function parseModifier($modifierString,$value) { $found = false; if(strstr($modifierString,":") !== false) { $arr = explode(":",$modifierString); $modifier = array_shift($arr); $args = $arr; } else { $modifier = $modifierString; $args = []; } $callable = ["\\boru\\dhutils\\templates\\modifiers\\".$modifier,$modifier]; if(is_callable($callable)) { return call_user_func($callable,$value,...$args); } elseif(isset($this->customModifiers[$modifier]) && is_callable($this->customModifiers[$modifier])) { return $this->customModifiers[$modifier]($this,$value,...$args); } } private function parseFunction($modifier,$value) { $arr = explode(":",$modifier); $func = array_shift($arr); if($func == "pad") { $len = static::shiftArray($arr, 1); $style = static::shiftArray($arr,"left"); if($style == "right") { $value = dhGlobal::padRight($value,$len," "); } elseif($style == "mid") { $value = dhGlobal::padMid($value,$len," "); } else { $value = dhGlobal::padLeft($value,$len," "); } } elseif($func == "max") { $len = static::shiftArray($arr, 10); $value = substr($value,0,$len); } return $value; } public function formatTime($time) { if(!is_numeric($time)) { return $time; } if($time<=60) { $str = sprintf("%01.2f",$time)."s"; } else { $time = round($time); $dtF = new \DateTime('@0'); $dtT = new \DateTime("@$time"); $str = dhGlobal::dateIntervalToElapsed($dtF->diff($dtT),true,false,2,""); } return $str; } public static function shiftArray(&$array,$default=null) { return !empty($array) ? array_shift($array) : $default; } }src/tools/Time.php000064400000005145144761607200010116 0ustar00setInput($input); } else { $this->dateTime = new \DateTime(); $this->timestamp = $this->dateTime->getTimestamp(); } } public function setInput($input) { if(is_numeric($input)) { $this->setTimestamp($input); } elseif(is_object($input) && $input instanceof \DateTime) { $this->setDateTime($input); } elseif(is_string($input)) { $this->setDateTime($input); } return $this; } public function setTimestamp($timestamp) { if(is_numeric($timestamp)) { $this->timestamp = $timestamp; $this->dateTime = new \DateTime(); $this->dateTime->setTimestamp($timestamp); } return $this; } public function setDateTime($dateTime) { if(is_object($dateTime) && $dateTime instanceof \DateTime) { $this->dateTime = $dateTime; $this->timestamp = $dateTime->getTimestamp(); } elseif(is_string($dateTime)) { try { $this->dateTime = new \DateTime($dateTime); $this->timestamp = $this->dateTime->getTimestamp(); } catch(\Exception $e) { $this->dateTime = null; $this->timestamp = null; } } return $this; } public function timestamp() { return $this->timestamp; } public function ts() { return $this->timestamp; } public function dateTime() { return $this->dateTime; } public function dt() { return $this->dateTime; } public function format($format="Y-m-d H:i:s",$timeZone=null) { if(!is_null($timeZone)) { $dt = clone $this->dateTime; if(is_string($timeZone)) { $timeZone = new \DateTimeZone($timeZone); } elseif(!is_object($timeZone) || !$timeZone instanceof \DateTimeZone) { $timeZone = new \DateTimeZone(date_default_timezone_get()); } $dt->setTimezone($timeZone); return $dt->format($format); } return $this->dateTime->format($format); } public static function create($input=null) { return new self($input); } public static function now() { return new self(); } }src/tools/WorkQueue.php000064400000014312144761607200011143 0ustar00max = $max; $this->delay = dhGlobal::getVal($options,"delay",$this->delay); $this->expected = dhGlobal::getVal($options,"expected",$this->expected); $this->visualize = dhGlobal::getVal($options,"visualize",false); $this->visualizerDelay = dhGlobal::getVal($options,"visualizerDelay",$this->visualizerDelay); $this->bootstrap = dhGlobal::getVal($options,"bootstrap",null); $this->collect = dhGlobal::getVal($options,"collect",$this->collect); $this->debugPackets = dhGlobal::getVal($options,"debugPackets",$this->debugPackets); $this->initQueue($queue,$visualizer); $this->initProcesses(); } public function initQueue($queue=null,$visualizer=null) { if(is_null($queue)) { $this->queue = new Queue($this->max,$this->delay); } else { $this->queue = $queue; } if(method_exists($this->queue,"setVisualizer")) { if(is_null($visualizer)) { if($this->visualize) { $visualizer = new Visualizer($this->max,$this->visualizerDelay,$this->expected); $this->queue->setVisualizer($visualizer); } } else { $this->queue->setVisualizer($visualizer); } } } public function initProcesses() { for($i=0;$i<$this->max;$i++) { $this->processes[$i] = new Worker($this->bootstrap,$this,$this->debugPackets); $this->processes[$i]->start(); } $this->started=true; } public function terminateProcesses($graceful=true) { foreach($this->processes as $i=>$worker) { if(!$graceful) { $worker->stop(); } else { while($worker->isRunning()) { dhGlobal::trace("gracefully waiting for worker ",$i); usleep($this->delay*1000000); } $worker->stop(); } unset($this->processes[$i]); } $this->started=false; } /** * * @param Work $work * @return Work */ public function queue(Work $work) { if($this->collect) { $work->then(function($result) use ($work) { $this->collected[$work->getId()] = $result; }); } $this->queue->queue($work); return $work; } public function wait() { if(!$this->running) { $this->start(); } \React\Async\await($this->deferred->promise()); } public function collect() { $this->wait(); $collected = $this->collected; $this->collected=[]; return $this->collect ? $collected : false; } public function start() { $this->deferred = new Deferred(); $this->running=true; if(!$this->isStarted()) { $this->initProcesses(); } Loop::addPeriodicTimer($this->delay, function($timer) { $this->needsWork(); $this->queue->visualizerDisplay(); if(!$this->queue->hasRunning() && !$this->queue->hasQueued()) { dhGlobal::trace("[queue]","terminate time.."); $this->stop(true); Loop::cancelTimer($timer); dhGlobal::trace("[queue]","terminated.."); $this->running=false; $this->deferred->resolve(true); } }); } public function stop($graceful=true) { $this->terminateProcesses($graceful); $this->running=false; } public function finishedWork(Work $work) { $this->queue->setFinished($work); } public function needsWork() { if($this->queue->hasQueued()) { //dhGlobal::trace("[queue]","haveQueued.."); foreach($this->processes as $pid=>$worker) { if($worker->isReady()) { dhGlobal::trace("[queue]","starting work."); if(($work = $this->queue->next()) !== false) { $worker->startWork($work); } } } } else { //dhGlobal::trace("[queue]","noQueue.."); return false; } return true; } public function isStarted() { return $this->started; } /** * Create work and then queue it.. chainable with ->then(); * @param mixed $callable * @param array $args * @param bool $asJson * @return Work */ public function queueWork($callable,$args=[],$asJson=true) { $work = static::work($callable,$args,$asJson); return $this->queue($work); } /** * Create Work * @param mixed $callable * @param array $args * @param bool $asJson * @return Work */ public static function work($callable,$args=[],$asJson=true) { $work = new Work($callable,["args"=>$args,"asJson"=>$asJson]); return $work; } }src/traits/AsyncUtils.php000064400000013236144761607200011464 0ustar00[$command]]); if($run) { return static::asyncWork($work); } else { return $work; } } /** * Create's async Work [static::http], and optionally runs it (if $run=true) * @param Request $request * @param bool $bodyAsJson default=false, if true will parse the http result body as json, returning an associative array * @param bool $run default=false, if true will auto-queue/run it.. otherwise returns Work for manual queue or execution * @return Work * @throws UnexpectedValueException * @throws RuntimeException * @throws Exception */ public static function asyncHttp(\boru\dhutils\http\Request $request,$bodyAsJson=false,$run=false) { $work = new Work(["static","http"],["args"=>[[ "method"=>$request->getMethod(), "url"=>$request->getUrl(), "requestData"=>$request->data, "getJson"=>$bodyAsJson ]]]); if($run) { return static::asyncWork($work); } else { return $work; } } public static function asyncVisualize($visualize) { static::set("asyncQueue.visualize",$visualize); } public static function asyncMax($max) { static::set("asyncQueue.maxPerQueue",$max); } public static function asyncRetries($retries) { static::set("asyncQueue.retriesOnError",$retries); } public static function asyncBootstrap($bootstrap) { static::set("asyncQueue.defaultBootstrap",$bootstrap); } public static function asyncSetSettings($maxPerQueue=3,$retriesOnError=0,$defaultBootstrap=null) { static::set("asyncQueue.maxPerQueue",$maxPerQueue); static::set("asyncqueue.retriesOnError",$retriesOnError); static::set("asyncqueue.defaultBootstrap",$defaultBootstrap); } /** * Shortcut to queue Work * @param Work $work * @param mixed $bootstrap * @return Work * @throws UnexpectedValueException * @throws RuntimeException * @throws Exception */ public static function asyncWork(Work $work,$bootstrap=null) { $queue = static::getAsyncQueue($bootstrap); $queue->queue($work); return $work; } /** * Shortcut to create and queue Work * @param mixed $callable * @param array $args * @param mixed $bootstrap * @return Work * @throws UnexpectedValueException * @throws RuntimeException * @throws Exception */ public static function asyncWorkCallable($callable,$args=[],$bootstrap=null) { $work = new Work($callable,["args"=>$args]); return static::asyncWork($work); } /** * Get a Queue (or create one if not found) based on $bootstrap * @param mixed $bootstrap * @return Queue */ public static function getAsyncQueue($bootstrap=null) { if(is_array($bootstrap)) { ksort($bootstrap); $queueName = sha1(json_encode($bootstrap)); } elseif(!is_null($bootstrap)) { $queueName = sha1($bootstrap); } else { $queueName = "default"; $bootstrap = static::get("asyncqueue.defaultBootstrap",null); } if(($queue = static::get("asyncQueue.".$queueName,false)) === false) { $queue = new Queue(static::get("asyncQueue.maxPerQueue",3),$bootstrap,static::get("asyncqueue.retriesOnError",0)); static::set("asyncQueue.".$queueName,$queue); } return $queue; } /** * * @param Work[]|Work $workArray * @return void */ public static function waitForWork($workArrayOrInstance) { if(!is_array($workArrayOrInstance)) { $workArray = [$workArrayOrInstance]; } else { $workArray = $workArrayOrInstance; } $promises=[]; foreach($workArray as $work) { $promises[] = $work->promise(); } return static::waitForAsync($promises); } /** * * @param Work[]|Work $workArray * @return void */ public static function collectWork($workArrayOrInstance) { return static::waitForWork($workArrayOrInstance); } public static function waitForAsync($promises=[]) { $results = []; foreach($promises as $promise) { $results[] = \React\Async\await($promise); } return $results; } public static function collectAsync($promises=[]) { return static::waitForAsync(); } public static function waitForQueue($bootstrap=null) { $queue = static::getAsyncQueue($bootstrap); $workArray = $queue->getAllWork(); return static::waitForWork($workArray); } public static function collectQueue($bootstrap=null) { return static::waitForQueue($bootstrap); } }src/traits/CommandRun.php000064400000001356144761607200011431 0ustar00commandResult = $result; $cmdName = $result["commandName"]; if(method_exists($this,"cmd_".$cmdName)) { $cmd = "cmd_".$cmdName; $this->$cmd($result); return true; } elseif(method_exists($this,"cmd_default")) { $cmd = "cmd_default"; $this->$cmd($result); return true; } return false; } public function getCommandOption($dotKey,$default=false) { return dhGlobal::getVal($this->commandResult["options"],$dotKey,$default); } }src/traits/GetOption.php000064400000001120144761607200011263 0ustar00setGetSetSeparator($separator); if(is_array($arr) && !empty($arr)) { foreach($arr as $k=>$v) { $this->set($k,$v); } } } public function set($key,$val="",$append=false) { $arr = $this->GetSetMainArray; $this->setArray($this->$arr,$key,$val,$append); return $this; } public function get($key=null,$default=null) { $arr = $this->GetSetMainArray; return $this->getArray($this->$arr,$key,$default); } public function exists($key=null) { $arr = $this->GetSetMainArray; return $this->existsArray($this->$arr,$key); } public function remove($key) { $arr = $this->GetSetMainArray; unset($this->$arr[$key]); return $this; } public function setArray(&$array,$key,$val="",$append=false) { if(strpos($key,$this->GetSetSeparator) !== false) { if($append) { $check = dhGlobal::getDot($array,$key,$this->GetSetSeparator); if(!is_null($check)) { if(is_array($check)) { $check[] = $val; $val = $check; } else { $narr = []; $narr[] = $check; $narr[] = $val; $val = $narr; } } } dhGlobal::dotAssign($array,$key,$val); } else { if(isset($array[$key]) && $append) { if(is_array($array[$key])) { $array[$key][] = $val; } else { $temp = $array[$key]; $array[$key] = []; $array[$key][] = $temp; $array[$key][] = $val; } } else { $array[$key] = $val; } } return $this; } public function getArray($array,$key=null,$default=null,$exists=false) { if(is_null($key)) { if($exists) { return !empty($array) ? true : false; } else { return !empty($array) ? $array : $default; } } if(strpos($key,$this->GetSetSeparator) !== false) { $uniqueid = uniqid("getArray",true); if(($check = dhGlobal::getDot($array,$key,$uniqueid,$this->GetSetSeparator)) !== $uniqueid) { return $exists ? true : $check; }; } if($exists) { return isset($array[$key]); } else { return isset($array[$key]) ? $array[$key] : $default; } } public function existsArray($array,$key) { return $this->getArray($array,$key,null,true); } /** * Get the value of GetSetSeparator */ public function getGetSetSeparator() { return $this->GetSetSeparator; } /** * Set the value of GetSetSeparator * * @return self */ public function setGetSetSeparator($GetSetSeparator) { $this->GetSetSeparator = $GetSetSeparator; return $this; } /** * Get the value of GetSetMainArray */ public function getGetSetMainArray() { return $this->GetSetMainArray; } /** * Set the value of GetSetMainArray * * @return self */ public function setGetSetMainArray($GetSetMainArray) { $this->GetSetMainArray = $GetSetMainArray; return $this; } }src/traits/GlobalDotParse.php000064400000007444144761607200012234 0ustar00['subkey'=>'value']],'key.subkey') -- returns 'value' * * @param array $arr the array to get the element from * @param string $dotString the key to find * @param mixed $default a default value if key isn't found * @return mixed the value if found, $defualt if not */ public static function getDot($arr=[],$dotString,$default=null,$separator=".") { $pieces = explode($separator,$dotString); $pointer = $arr; for($i=0;$idefaults * @param string $keySeparator (default='.') string to split for 'dot search' * @param boolean $single (default=false) if true, returns the first matching value in order of $keyDefault key definitions * @return mixed if $single==false array of values for each of the keyDefault pairs. Otherwise the first matching value */ public static function getMultiVal(array $arr,array $keyValueArray,$single=false,$keySeparator=".") { $values = []; foreach($keyValueArray as $keyString=>$default) { $value = static::getVal($arr,$keyString,$default,$keySeparator); if($single && $value!==$default) { return $value; } $values[] = $value; } return $values; } public static function parseDots(&$array,$separator='.') { foreach($array as $k=>$v) { if(is_array($v)) { $array[$k] = static::parseDots($v,$separator); } if(strpos($k,$separator) !== false) { static::dotAssign($array,$k,$v,$separator); unset($array[$k]); } } } /** * Explodes dot notation and assigns a value based on the key * * Example: * * $arr = ['key'=>['subkey'=>'value']]; * * dhGlobal::dotAssign($arr,'key.subkey2','value2) * * //['key'=>['subkey'=>'value','subkey2'=>'value2']] * * @param array $arr the array to get the element from * @param string $path the dotNotated key * @param mixed $value the value to assign * @param string $separator defaults to '.' * @return void */ public static function dotAssign(&$arr, $path, $value=null, $separator='.') { $keys = explode($separator, $path); foreach ($keys as $key) { $arr = &$arr[$key]; } $arr = $value; } public static function dotDelete(&$arr, $path, $separator='.') { $keys = explode($separator, $path); $end = array_pop($keys); foreach ($keys as $key) { $arr = &$arr[$key]; } unset($arr[$end]); } }src/traits/GlobalFilesys.php000064400000007611144761607200012125 0ustar00$pathString,"readMeta"=>$readMeta]); } /** * Checkes to see if the provided Path exists as a Directory. * Returns a \boru\dhutils\filesys\Directory object if is a directory and exists, otherwise false * * Note, will return false if the path is a File or Symlink. */ /** * Checkes to see if the provided Path exists as a Directory. * @param mixed $pathString * @param bool $scan * @return false|Directory Note, will return false if the path is a File or Symlink. */ public static function dirIfExists($pathString,$scan=true) { if(!file_exists($pathString) || !is_dir($pathString)) { return false; } return new \boru\dhutils\filesys\Directory(["path"=>$pathString,"scan"=>$scan]); } public static function getFileHashAndSize($data, $offset = 0, $partSize = null) { if (!$partSize) { if (is_resource($data)) { // We need to calculate the file's hash incrementally from the stream. $context = hash_init('sha1'); hash_update_stream($context, $data); $hash = hash_final($context); // Similarly, we have to use fstat to get the size of the stream. $size = fstat($data)['size']; // Rewind the stream before passing it to the HTTP client. rewind($data); } else { // We've been given a simple string body, it's super simple to calculate the hash and size. $hash = sha1($data); $size = mb_strlen($data, '8bit'); } } else { $dataPart = static::getPartOfFile($data, (int) $offset, (int) $partSize); $hash = sha1($dataPart); $size = mb_strlen($dataPart, '8bit'); } return array("hash"=>$hash, "size"=>$size); } /** * Return selected part of file * * @param $data * @param int $offset * @param int $partSize * @return bool|string */ public static function getPartOfFile($data, $offset, $partSize) { // Get size and hash of one data chunk if (is_resource($data)) { // Get data chunk fseek($data, $offset); $dataPart = fread($data, $partSize); // Rewind the stream before passing it to the HTTP client. rewind($data); } else { $dataPart = substr($data, $offset, $partSize); } return $dataPart; } /** * Return a santized relative path * * Used to make sure the path is within the defined root. * * @param string $path The path to test * @param string $root default=current directory * @param boolean $relative return relative path to $path if true, otherwise full path * @return mixed sanitized path if within root, false if not */ public static function sanitizePath($path,$root=null,$relative=true) { $bp = new \boru\dhutils\filesys\BasePath($path); if(is_null($root)) { $root = "."; } $rpath = $bp->relative($root); if($rpath === false) { return false; } return $relative ? $rpath : $bp->fullPath(); } }src/traits/GlobalLogHandler.php000064400000004743144761607200012531 0ustar00log(...$args); } public static function trace(...$args) { return static::logHandler()->trace(...$args); } public static function debug(...$args) { return static::logHandler()->debug(...$args); } public static function info(...$args) { return static::logHandler()->info(...$args); } public static function warn(...$args) { return static::logHandler()->warn(...$args); } public static function error(...$args) { return static::logHandler()->error(...$args); } public static function logger($name,$levels=dhGlobal::LOG_ERROR,$stdOut=true,$file=false,$lineSeperator=PHP_EOL) { return static::logHandler()->logger($name,$levels,$stdOut,$file,$lineSeperator); } /** * Add a level to the logger. * * Example: * * dhGlobal::addLogLevel("query","QUERY","debug") -- adds a 'query' level to the debug log display * * @param string $name the name of the level used for logging (eg: $logger->log($name,stuff,to,print)) * @param string $level level to add the new level to, ['trace','debug','info','warn','error'] * @param string $prefix optional prefix used if different from the name. 5 characters * @return boolean true if successful, false if failed */ public static function addLogLevel($name,$level,$prefix=null) { return static::logHandler()->defineLogLevel($name,$level,$prefix); } public static function defineLogLevel($name,$level,$prefix=null) { return static::logHandler()->defineLogLevel($name,$level,$prefix); } }src/traits/GlobalOut.php000064400000001230144761607200011245 0ustar00start($command,$meta,$options); } /** * collect all current running threads from the global threadPool instance * @param mixed $collectType constant that defines the output of the collect() call valid options are: * * dhThreads::COLLECT_FULL -- each completed Thread object * * dhThreads::COLLECT_ARRAY -- array containing just the command output (if a valid dhCache instance is available to the environment (Memcached)) * * dhThreads::COLLECT_NONE -- no output * @return array|true */ public static function threadCollect($collectType=null) { return static::threadPool("threadPool")->collect($collectType); } /** * Full-service wrapper for threading. * * @param array $commands array of commands to execute in the thread pool. (command is either a string or an array of ["exec","data","meta","options"]) * @param array $options options to apply to all commands executed {@see \boru\dhutils\dhThreads->start()} * @param int $numThreads number of threads to run * @param float $throttleDelay delay between waiting for available thread space * @return array array of Thread objects after completion * * @see \boru\dhutils\dhThreads->start() */ public static function threadMultiExec($commands=[],$options=[],$numThreads=4,$throttleDelay=0.1) { $pool = static::threadPool("threadPool",$numThreads,$throttleDelay); $collectOpts=[]; $collectOpts["collectType"] = dhGlobal::getVal($options,"collectType",dhThreads::COLLECT_FULL); if(!is_array($commands)) { $commands = [$commands]; } foreach($commands as $command) { $cmd = ""; $meta = []; $opts = $options; if(is_array($command)) { if(($cmd = dhGlobal::getMultiVal($command,["command"=>false,"cmd"=>false,"exec"=>false],true)) !== false) { $data = dhGlobal::getVal($command,"data",false); $meta = dhGlobal::getVal($command,"meta",[]); if($data !== false) { $meta["data"] = $data; } foreach(dhGlobal::getVal($command,"options",[]) as $k=>$v) { $opts[$k] = $v; } } else { $cmd = $command; } } $pool->start($cmd,$meta,$opts); } return $pool->collect($collectOpts); } }src/traits/GlobalUtils.php000064400000024657144761607200011620 0ustar001]) -- returns true * * @param array $array the array to test * @return boolean true if is assoc, false if not */ public static function isAssoc($array) { if(!is_array($array)) return false; return (bool)count(array_filter(array_keys($array), 'is_string')); } /** * Determine if an array is a numeric list array (true) or an assoc array (false). * * Example: * * dhGlobal::isAssoc(['test'=>1]) -- returns false * * @param array $array the array to test * @return boolean true if is assoc, false if not */ public static function isList($arr) { if ($arr === []) { return true; } return array_keys($arr) === range(0, count($arr) - 1); } public static function exec($cmd,$array=true) { if(!$array) { ob_start(); passthru($cmd); $data = ob_get_contents(); ob_end_clean(); return $data; } else { $data = []; exec($cmd,$data); return $data; } } /** * Truncate a string to a certain length if necessary, * optionally splitting in the middle of a word, and * appending the $etc string or inserting $etc into the middle. * * @param string $string input string * @param integer $length length of truncated text * @param string $etc end string * @param boolean $break_words truncate at word boundary * @param boolean $middle truncate in the middle of text * @return string truncated string */ public static function truncate($string, $length = 80, $etc = '...', $break_words = false, $middle = false) { if ($length == 0) return ''; if (function_exists('mb_strlen')) { if (mb_strlen($string, 'UTF-8') > $length) { $length -= min($length, mb_strlen($etc, 'UTF-8')); if (!$break_words && !$middle) { $string = preg_replace('/\s+?(\S+)?$/u', '', mb_substr($string, 0, $length + 1, 'UTF-8')); } if (!$middle) { return mb_substr($string, 0, $length, 'UTF-8') . $etc; } return mb_substr($string, 0, $length / 2, 'UTF-8') . $etc . mb_substr($string, - $length / 2, $length, 'UTF-8'); } return $string; } // no MBString fallback if (isset($string[$length])) { $length -= min($length, strlen($etc)); if (!$break_words && !$middle) { $string = preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, $length + 1)); } if (!$middle) { return substr($string, 0, $length) . $etc; } return substr($string, 0, $length / 2) . $etc . substr($string, - $length / 2); } return $string; } /** * Pad a $string to $len length using $padstring * * Balances the $string's length to match an even pad if $padstring is more than 1 char */ public static function pad($string,$len,$padstring=". ",$padStyle=STR_PAD_RIGHT) { if(strlen($padstring)>1) { $mod = strlen($string) % strlen($padstring); if($mod !== 0) { $diff = strlen($padstring)-$mod; $string = str_pad($string,strlen($string)+$diff," ",$padStyle); } } return str_pad($string,$len,$padstring,$padStyle); } /** * Right-Pad a $string to $len length using $padstring * * Balances the $string's length to match an even pad if $padstring is more than 1 char */ public static function padRight($string,$len,$padstring=". ") { return static::pad($string,$len,$padstring,STR_PAD_RIGHT); } /** * Left-Pad a $string to $len length using $padstring * * Balances the $string's length to match an even pad if $padstring is more than 1 char */ public static function padLeft($string,$len,$padstring=". ") { return static::pad($string,$len,$padstring,STR_PAD_LEFT); } /** * Middle-Pad a $string to $len length using $padstring * * Balances the $string's length to match an even pad if $padstring is more than 1 char */ public static function padMid($string,$len,$padstring=". ") { return static::pad($string,$len,$padstring,STR_PAD_BOTH); } /** * Used to limit the number of concurrent exec() calls for a given 'tag' * * Output: (int) number of running instances when < $concurrent * * Inputs * * * $tag (string) - identifier that should be included in the exec() command * * * $concurrent (int) - maximum concurrent instances of $tag that can exist * * * $seconds (float) - time to sleep in seconds between checks until < $concurrent * * * $callback (lambda) - callback function to run on each loop after checking */ public static function throttle($tag,$concurrent=5,$seconds=1,$callback=null) { $check_command = 'ps aux | grep "'.$tag.'" | grep -v grep | wc -l'; $data = exec($check_command); if(!is_null($callback)) { $callback(); } while($data>=$concurrent && $data>0) { usleep($seconds*1000000); $data = exec($check_command); if(!is_null($callback)) { $callback(); } } return $data; } /** * Trim a specific string off the beginning, end or both. * * @param string $needle the string to trim off * @param string $haystack the string to trim $needle from * @param int $flag (default=both) dhGlobal::TRIM_BOTH, dhGlobal::TRIM_START, dhGlobal::TRIM_END * @param int $limit (default=0) Number of times to trim (0=unlimited) * @return string */ public static function trimString($needle,$haystack,$flag=dhGlobal::TRIM_BOTH,$limit=0) { $needleLen = strlen($needle); if($flag == dhGlobal::TRIM_BOTH || $flag == dhGlobal::TRIM_START) { $cycles=0; while(strlen($haystack)>=$needleLen && substr($haystack,0,$needleLen) == $needle) { $haystack = substr($haystack,$needleLen); $cycles++; if($limit>0 && $cycles>=$limit) { break; } } } if($flag == dhGlobal::TRIM_BOTH || $flag == dhGlobal::TRIM_END) { $cycles=0; while(strlen($haystack)>=$needleLen && substr($haystack,-$needleLen) == $needle) { $haystack = substr($haystack,0,-$needleLen); $cycles++; if($limit>0 && $cycles>=$limit) { break; } } } return $haystack; } /** * Trim a specific string ($needle) off the beginning and end of $haystack, wrapper for dhGlobal::trimString() * * @param string $needle the string to trim off * @param string $haystack the string to trim $needle from * @param int $limit (default=0) Number of times to trim (0=unlimited) * @return string */ public static function btrimString($needle,$haystack,$limit=0) { return static::trimString($needle,$haystack,dhGlobal::TRIM_BOTH,$limit); } /** * Trim a specific string ($needle) off the beginning of $haystack, wrapper for dhGlobal::trimString() * * @param string $needle the string to trim off * @param string $haystack the string to trim $needle from * @param int $limit (default=0) Number of times to trim (0=unlimited) * @return string */ public static function ltrimString($needle,$haystack,$limit=0) { return static::trimString($needle,$haystack,dhGlobal::TRIM_START,$limit); } /** * Trim a specific string ($needle) off the end of $haystack, wrapper for dhGlobal::trimString() * * @param string $needle the string to trim off * @param string $haystack the string to trim $needle from * @param int $limit (default=0) Number of times to trim (0=unlimited) * @return string */ public static function rtrimString($needle,$haystack,$limit=0) { return static::trimString($needle,$haystack,dhGlobal::TRIM_END,$limit); } /** * Convert bytes into B/KB/MB/GB/TB * * @param mixed $bytes bytes to convert * @param int $precision (2) * @param null|string $forceUnit 'B', 'KB', 'MB', 'GB', 'TB' * @param boolean $space (false) space between value and unit * @return string */ public static function formatBytes($bytes,$format="%01.2f %s",$forceUnit=null) { if(is_null($bytes)) { $bytes = 0; } elseif(!is_numeric($bytes)) { $bytes = 0; } $units = array('B', 'KB', 'MB', 'GB', 'TB'); $bytes = max($bytes, 0); if(($pow = array_search((string) $forceUnit,$units)) === false) { $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); } // Uncomment one of the following alternatives $bytes /= pow(1024, $pow); // $bytes /= (1 << (10 * $pow)); return sprintf($format, $bytes,$units[$pow]); } public static function camelize($input, $lowercaseFirst=true, $separator = [' ','-','_']) { if($lowercaseFirst) { return lcfirst(str_replace($separator, '', ucwords($input, implode('',$separator)))); } else { return str_replace($separator, '', ucwords($input, implode('',$separator))); } } public static function array_is_list($array) { if(!is_array($array)) { return false; } if ([] === $array || $array === array_values($array)) { return true; } $nextKey = -1; foreach ($array as $k => $v) { if ($k !== ++$nextKey) { return false; } } return true; } }test/async/bootstrap.php000064400000002750144761607200011401 0ustar00__DIR__."/scripts/class.php" //dhGlobal::logger("debugger",dhGlobal::LOG_ALL,true,false); dhGlobal::asyncVisualize(true); $queue = new Queue(8,__DIR__."/bootstrap/class.php",null,true); //$queue->setBootstrapAsCallable(true); $queue->setMaxQueued(10); $queue->enableExtendedBar(true); $queue->updateInterval(0.5); $queue->setRetriesOnError(2); dhGlobal::outLine("starting queue process.. "); dhGlobal::outLine("building list "); $numToRun = 10; $callable = ["AsyncBootstrapTest","run"]; //$callable = ["\\boru\\dhutils\\async\\WorkerUtils","testError"]; //$args = [$i,1,3]; $queue->setExpected($numToRun+2); $args = []; for($i=0;$i<$numToRun;$i++) { $work = Queue::callableWork($callable,$i,0,0.3); $work->setMetaData("name","work-".$i); $work->then(function(Result $result) use ($i,$work) { Queue::display("complete",$i,$work->getId()); },function($e) use ($i,$work) { Queue::display("failed",$i,$work->getId()); }); $work->onStart(function() use ($i,$work) { Queue::display("start",$i,$work->getId()); }); //$work->start(); $queue->queue($work); } $queue->wait(); dhGlobal::debug("other stuff");test/async/bootstrap/class.php000064400000003446144761607200012511 0ustar00"setupNoArgs"]); } return json_encode($args); } public static function getRand($minSeconds=0,$maxSeconds=2) { $minMicro = $minSeconds*1000000; $maxMicro = $maxSeconds*1000000; $rand = rand($minMicro,$maxMicro); $seconds = round($rand / 1000000,2); return ["value"=>$rand,"seconds"=>$seconds]; } public static function run($id=0,$minRand=0.5,$maxRand=3) { $rand = static::getRand($minRand,$maxRand); usleep($rand["value"]); $seconds = $rand["seconds"]; $result = []; $result["id"] = $id; $result["sleep"] = $seconds; return $result; } public static function testRun(...$args) { /*if(empty($args)) { return json_encode(["message"=>"emptyArgs"]); } return json_encode($args);*/ return file_get_contents(__DIR__."/../bad.txt"); } public static function http(...$args) { $deferred = new Deferred(); $responded=false; $client = dAsyncHttp::client( [ "async"=>true, ] ); //$client->url("http://localhost/mtest.php?sleepTime=15"); $client->url("http://localhost/mtest.php"); $client->post()->done(function(\boru\dhutils\guzzle\Response $response) use (&$deferred,&$responded) { if(!$responded) { $responded=true; $deferred->resolve($response->body()); } }); return $deferred->promise(); } }test/async/bootstrap/unique.php000064400000000366144761607200012710 0ustar00"setupNoArgs"]); } return json_encode($args); } }test/async/direct.php000064400000001350144761607200010631 0ustar00["ping google.com -c 3"]]); $work->onQueued(function($work) use ($i) { dhGlobal::outLine("Queued $i\n"); })->onStart(function($work) use ($i) { dhGlobal::outLine("Started $i\n"); })->then(function($result) use ($i) { dhGlobal::outLine("Work finished for $i\n"); },function($e) use ($i) { dhGlobal::outLine("Work failed for $i\n"); }); $work->start(); }test/async/global.php000064400000001105144761607200010615 0ustar00then(function($result) use ($i) { dhGlobal::outLine("Work finished for $i",$result->get("result")); },function($e) use ($i) { dhGlobal::outLine("Work failed for $i\n"); }); }test/async/http.php000064400000001537144761607200010345 0ustar00authBasic("test","pass")->json(["test1.three"=>"val1","test1.four"=>"a","test2"=>"val2","test3"=>["a","b","c"]]); //Run it :D $work = dhglobal::asyncHttp($req,false,true); //Optionally do something with the output $work->then(function($result) { dhGlobal::outLine("Finished",$result->result("method"),"to",$result->result("url"),"with a",$result->result("code")); }); }test/async/queue.php000064400000001514144761607200010505 0ustar00true,"args"=>$args]); $work->onQueued(function($work) use ($i) { dhGlobal::outLine("Queued $i"); }); $work->onStart(function($work) use ($i) { dhGlobal::outLine("Started $i"); }); $work->then(function($result) use ($i) { dhGlobal::outLine("Work finished for $i"); },function($e) use ($i) { dhGlobal::outLine("Work failed for $i"); }); $queue->queue($work); }test/async/workgroups.php000064400000002703144761607200011604 0ustar00setMaxQueued(10)->enableExtendedBar(true); dhGlobal::outLine("starting queue process.. "); $numToRun = 50; $groups = 12; $callable = ["AsyncBootstrapTest","run"]; //Setup the groups $numPerGroup = ceil($numToRun/$groups); $groupNum=0; $groupCounter=0; for($i=0;$i<$groups;$i++) { $groupIdentifier = "group".$i; $queue->createWorkGroup($groupIdentifier)->then(function($gid) { Queue::display("complete","*** GROUP:".$gid); },function($gid) { Queue::display("error","*** GROUP:".$gid); }); } //run the queue for($i=0;$i<$numToRun;$i++) { $groupCounter++; if($groupCounter>$numPerGroup) { $groupNum++; $groupCounter=0; } $identifier = "g".$groupNum."-".$i; $groupIdentifier = "group".$groupNum; $work = Queue::callableWork($callable,$groupIdentifier."_".$i,0,5) ->setMetaData("name",$identifier); Queue::basicDisplay($work,$identifier); $queue->queue($work,$groupIdentifier); } $queue->wait(); dhGlobal::debug("other stuff"); Loop::run();test/init.php000064400000000256144761607200007211 0ustar00