.gitignore000064400000000036144761607200006542 0ustar00/vendor/ /tests/ composer.lockREADME.md000064400000003312144761607200006031 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 ``` composer require dhayesed/dhutils ``` ## 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 ### dhLoggercomposer.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.php000064400000017165144761607200010146 0ustar00withResponseBuffer(static::$maxResponseSize); } } } /** * * @param mixed $url * @param array $headers * @param callable|null $onSuccess * @param callable|null $onError * @return \React\Promise\ExtendedPromiseInterface * @throws InvalidArgumentException */ public static function get($url,$headers=[],callable $onSuccess=null,callable $onError=null,$timeout=true) { static::init(); if(is_null($headers)) { $headers = []; } $successCallback = static::makeSuccessCallback($url,"get",$onSuccess); $errorCallback = static::makeErrorCallback($url,"get",$onError); return static::$browser->withTimeout($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.php000064400000003761144761607200011545 0ustar00file = new File(["path"=>$fileopt,"create"=>true]); } else { $this->file = $fileopt; } $this->prettyJson = dhGlobal::getVal($options,"prettyJson",true); $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|JSON_UNESCAPED_SLASHES)); } else { $this->file->write(json_encode($this,JSON_UNESCAPED_SLASHES)); } return $this; } public static function loadFile($file) { $config = new ConfigFile(["file"=>$file]); $config->load(); return $config; } }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; } if(($responseMaxSize = dhGlobal::getVal($options,"responseMaxSize",false)) !== false) { $this->responseMaxSize = $responseMaxSize; } } 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.php000064400000004664144761607200011232 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); } public static function getTraceInfo($stepsToDiscard=2,$full=false) { $trace = debug_backtrace(); while($stepsToDiscard-- > 0) { array_shift($trace); } $output = []; if(!$full) { $output["item"] = static::getTraceEntry(array_shift($trace)); $output["caller"] = static::getTraceEntry(array_shift($trace)); } else { foreach($trace as $traceEntry) { $output[] = static::getTraceEntry($traceEntry); } } 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.php000064400000010566144761607200012233 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]); } /** * Flatten a multi-dimensional associative array to dot notation * @param mixed $array * @param string $prepend * @return array */ public static function arrayDot($array, $prepend = '') { $results = []; foreach ($array as $key => $value) { if (is_array($value) && ! empty($value)) { $results = array_merge($results, static::arrayDot($value, $prepend.$key.'.')); } else { $results[$prepend.$key] = $value; } } return $results; } }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