1. Go to this page and download the library: Download uppes/coroutine library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
uppes / coroutine example snippets
// This is in the examples folder as "async_scrape.php"
url($url)" . \EOL);
\timer_for($url);
yield \sleep_for(\random_uniform(1, 5));
print("time of fetch_url($url): " . \timer_for($url) . 's' . \EOL);
return "<em>fake</em> page html for $url";
};
function analyze_sentiment($html)
{
print("~ executing analyze_sentiment('$html')" . \EOL);
\timer_for($html . '.url');
yield \sleep_for(\random_uniform(1, 5));
$r = "positive: " . \random_uniform(0, 1);
print("time of analyze_sentiment('$html'): " . \timer_for($html . '.url') . 's' . \EOL);
return $r;
};
function handle_url($url)
{
yield;
$extracted_data = [];
$html = yield fetch_url($url);
$extracted_data[$url] = yield analyze_sentiment($html);
return yield $extracted_data;
};
function main()
{
$urls = [
"https://www.ietf.org/rfc/rfc2616.txt",
"https://en.wikipedia.org/wiki/Asynchronous_I/O"
];
$urlID = [];
\timer_for();
foreach ($urls as $url)
$urlID[] = yield \away(handle_url($url));
$result_data = yield \gather($urlID);
foreach ($result_data as $id => $extracted_data) {
echo "> extracted data:";
\print_r($extracted_data);
}
print("time elapsed: " . \timer_for() . 's');
}
\coroutine_run(main());
use function Async\Path\{ , };
use function Async\Worker\{ , };
use function Async\Stream\{ , };
/**
* Returns a random float between two numbers.
*
* Works similar to Python's `random.uniform()`
* @see https://docs.python.org/3/library/random.html#random.uniform
*/
\random_uniform($min, $max);
/**
* Return the value (in fractional seconds) of a performance counter.
* Using either `hrtime` or system's `microtime`.
*
* The $tag is:
* - A reference point used to set, to get the difference between the results of consecutive calls.
* - Will be cleared/unset on the next consecutive call.
*
* returns float|void
*
* @see https://docs.python.org/3/library/time.html#time.perf_counter
* @see https://nodejs.org/docs/latest-v11.x/api/console.html#console_console_time_label
*/
\timer_for(string $tag = 'perf_counter');
/**
* Makes an resolvable function from label name that's callable with `away`
*/
\async(string $labelFunction, $asyncFunction);
/**
* Wrap the value with `yield`, when placed within this insure that
* any *function/method* will be `awaitable` and the actual return
* value is picked up properly by `gather()`.
*/
return \value($value)
/**
* Add/schedule an `yield`-ing `function/callable/task` for background execution.
* Will immediately return an `int`, and continue to the next instruction.
* Returns an task Id
* - This function needs to be prefixed with `yield`
*
* @see https://docs.python.org/3.7/library/asyncio-task.html#asyncio.create_task
*/
yield \away($awaitedFunction, ...$args) ;
/**
* Performs a clean application exit and shutdown.
*
* Provide $skipTask incase called by an Signal Handler. Defaults to the main parent task.
* - Use `get_task()` to retrieve caller's task id.
*
* - This function needs to be prefixed with `yield`
*/
yield \shutdown($skipTask)
/**
* Wrap the callable with `yield`, this insure the first attempt to execute will behave
* like a generator function, will switch at least once without actually executing, return object instead.
* This function is used by `away` not really called directly.
*
* @see https://docs.python.org/3.7/library/asyncio-task.html#awaitables
*/
\awaitAble($awaitableFunction, ...$args);
/**
* Run awaitable objects in the tasks set concurrently and block until the condition specified by race.
*
* Controls how the `gather()` function operates.
* `gather_wait` will behave like **Promise** functions `All`, `Some`, `Any` in JavaScript.
*
* - This function needs to be prefixed with `yield`
*
* @see https://docs.python.org/3.7/library/asyncio-task.html#waiting-primitives
*/
yield \gather_wait(array $tasks, int $race = 0, bool $exception = true, bool $clear = true)
/**
* Run awaitable objects in the taskId sequence concurrently.
* If any awaitable in taskId is a coroutine, it is automatically scheduled as a Task.
*
* If all awaitables are completed successfully, the result is an aggregate list of returned values.
* The order of result values corresponds to the order of awaitables in taskId.
*
* The first raised exception is immediately propagated to the task that awaits on gather().
* Other awaitables in the sequence won't be cancelled and will continue to run.
* - This function needs to be prefixed with `yield`
*
* @see https://docs.python.org/3.7/library/asyncio-task.html#asyncio.gather
*/
yield \gather(...$taskId);
/**
* Block/sleep for delay seconds.
* Suspends the calling task, allowing other tasks to run.
* A result is returned If provided back to the caller
* - This function needs to be prefixed with `yield`
*/
yield \sleep_for($delay, $result);
/**
* Creates an communications Channel between coroutines, returns an object
* Similar to Google Go language - basic, still needs additional functions
* - This function needs to be prefixed with `yield`
*/
yield \make();
/**
* Send message to an Channel
* - This function needs to be prefixed with `yield`
*/
yield \sender($channel, $message, $taskId);
/**
* Set task as Channel receiver, and wait to receive Channel message
* Will continue other tasks until so.
* - This function needs to be prefixed with `yield`
*/
yield \receiver($channel);
/**
* A goroutine is a function that is capable of running concurrently with other functions.
* To create a goroutine we use the keyword `go` followed by a function invocation
* @see https://www.golang-book.com/books/intro/10#section1
*/
yield \go($goFunction, ...$args);
/**
* Modeled as in `Go` Language.
*
* The behavior of defer statements is straightforward and predictable.
* There are three simple rules:
* 1. *A deferred function's arguments are evaluated when the defer statement is evaluated.*
* 2. *Deferred function calls are executed in Last In First Out order after the* surrounding function returns.
* 3. *Deferred functions can`t modify return values when is type, but can modify content of reference to
*
* @see https://golang.org/doc/effective_go.html#defer
*/
\defer(&$previous, $callback)
/**
* Modeled as in `Go` Language.
*
* Regains control of a panicking `task`.
*
* Recover is only useful inside `defer()` functions. During normal execution, a call to recover will return nil
* and have no other effect. If the current `task` is panicking, a call to recover will capture the value given
* to panic and resume normal execution.
*/
\recover(&$previous, $callback);
/**
* Modeled as in `Go` Language.
*
* An general purpose function for throwing an Coroutine `Exception`,
* or some abnormal condition needing to keep an `task` stack trace.
*/
\panic($message, $code, $previous);
/**
* Return the task ID
* - This function needs to be prefixed with `yield`
*/
yield \get_task();
/**
* kill/remove an task using task id
* - This function needs to be prefixed with `yield`
*/
yield \cancel_task($tid);
/**
* Wait for the callable/task to complete with a timeout.
* Will continue other tasks until so.
* - This function needs to be prefixed with `yield`
*/
yield \wait_for($callable, $timeout);
/**
* Wait on read stream/socket to be ready read from.
* Will continue other tasks until so.
* - This function needs to be prefixed with `yield`
*/
yield \read_wait($stream);
/**
* Wait on write stream/socket to be ready to be written to.
* Will continue other tasks until so.
* - This function needs to be prefixed with `yield`
*/
yield \write_wait($stream);
/**
* Wait on keyboard input.
* Will continue other tasks until so.
* Will not block other task on `Linux`, will continue other tasks until `enter` key is pressed,
* Will block on Windows, once an key is typed/pressed, will continue other tasks `ONLY` if no key is pressed.
* - This function needs to be prefixed with `yield`
*/
yield \input_wait($size);
/**
* An PHP Functional Programming Primitive.
* Return a curryied version of the given function. You can decide if you also
* want to curry optional parameters or not.
*
* @see https://github.com/lstrojny/functional-php/blob/master/docs/functional-php.md#currying
*/
\curry($function, $
use function Async\Worker\{ add_process, spawn_task, spawn_await };
/**
* Add/execute a blocking `subprocess` task that runs in parallel.
* This function will return `int` immediately, use `gather()` to get the result.
* - This function needs to be prefixed with `yield`
*/
yield \spawn_task($command, $timeout);
/**
* Add and wait for result of an blocking `I/O` subprocess that runs in parallel.
* - This function needs to be prefixed with `yield`
*
* @see https://docs.python.org/3.7/library/asyncio-subprocess.html#subprocesses
* @see https://docs.python.org/3.7/library/asyncio-dev.html#running-blocking-code
*/
yield \spawn_await($callable, $timeout, $display, $channel, $channelTask, $signal, $signalTask);
/**
* Add and wait for result of an blocking `I/O` subprocess that runs in parallel.
* This function turns the calling function internal __state/type__ used by `gather()`
* to **process/paralleled** which is handled differently.
* - This function needs to be prefixed with `yield`
*
* @see https://docs.python.org/3.7/library/asyncio-subprocess.html#subprocesses
* @see https://docs.python.org/3.7/library/asyncio-dev.html#running-blocking-code
*/
yield \add_process($command, $timeout);
use function Async\Path\file_***Any File System Command***;
/**
* Executes a blocking system call asynchronously either natively thru `libuv`, `threaded`, or it's `uv_spawn`
* feature, or in a **child/subprocess** by `proc_open`, if `libuv` is not installed.
* - This function needs to be prefixed with `yield`
*/
yield \file_***Any File System Command***( ...$arguments);
function main() {
// Your initialization/startup code will need to be enclosed inside an function.
// This is
/**
* @see https://docs.python.org/3/library/asyncio-task.html#timeouts
*/
yield \sleep_for(3600);
print(' yay!');
}
function keyboard() {
// will begin outputs of `needName` in 1 second
print("What's your name: ");
// Note: I have three Windows systems
// - Windows 10 using PHP 7.2.18 (cli) (built: Apr 30 2019 23:32:39) ( ZTS MSVC15 (Visual C++ 2017) x64 )
// - Windows 10 using PHP 7.1.19 (cli) (built: Jun 20 2018 23:37:54) ( NTS MSVC14 (Visual C++ 2015) x86 )
// - Windows 7 using PHP 7.1.16 (cli) (built: Mar 28 2018 21:15:31) ( ZTS MSVC14 (Visual C++ 2015) x64 )
// Windows 10 blocks STDIN from the beginning with no key press.
// Windows 7 does non-blocking STDIN, if no input attempted. only after typing something it blocks.
return yield \input_wait();
}
function needName() {
$i = 1;
yield \sleep_for(1);
while(true) {
echo $i;
yield \sleep_for(0.05);
$i++;
if ($i == 15) {
print(\EOL.'hey! try again: ');
}
if ($i == 100) {
print(\EOL.'hey! try again, one more time: ');
break;
}
}
}
function main() {
yield \away(\needName());
echo \EOL.'You typed: '.(yield \keyboard()).\EOL;
try {
// Wait for at most 0.5 second
yield \wait_for(\eternity(), 0.5);
} catch (\RuntimeException $e) {
print("\ntimeout!");
// this script should have exited automatically, since
// there are no streams open, nor tasks running, this exception killed `eternity` task
// currently, will continue to run
// task id 2 is `ioWaiting` task, the scheduler added for listening
// for stream socket connections
yield \cancel_task(2);
// This might just be because `main` is task 1,
// and still running by the exception throw, need more testing
}
}
\coroutine_run(\main());
/**
* Template for developing an library package for access
*/
public static function someName($whatever, ...$args)
{
return new Kernel(
function(TaskInterface $task, Coroutine $coroutine) use ($whatever, $args){
// Use/Execute/call some $whatever with ...$args;
//
if ($done) {
// will return $someValue back to the caller
$task->sendValue($someValue);
// will return back to the caller, the callback
$coroutine->schedule($task);
}
}
);
}
// Setup to call
function some_name($whatever, ...$args) {
return Kernel::someName($whatever, ...$args);
}
// To use
yield \some_name($whatever, ...$args);