PHP code example of uppes / coroutine

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());

/**
 * @see https://golangbot.com/goroutines/
 * @see https://play.golang.org/p/oltn5nw0w3
 */
       print(' '.$i);
    }
}

function alphabets() {
    for ($i = 'a'; $i <= 'e'; $i++) {
        yield \sleep_for(400 * \MS);
        print(' '.$i);
    }
}

function main() {
    yield \go(\numbers());
    yield \go(\alphabets());
    yield \sleep_for(3000 * \MS);
    print(" main terminated");
}

\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);
bash
apt-get install libuv1-dev php-pear php-dev -y
bash
yum install libuv-devel php-pear php-dev -y
powershell
cd C:\Php
Invoke-WebRequest "https://windows.php.net/downloads/pecl/releases/uv/0.2.4/php_uv-0.2.4-7.2-nts-vc15-x64.zip" -OutFile "php_uv-0.2.4.zip"
#Invoke-WebRequest "https://windows.php.net/downloads/pecl/releases/uv/0.2.4/php_uv-0.2.4-7.3-nts-vc15-x64.zip" -OutFile "php_uv-0.2.4.zip"
#Invoke-WebRequest "https://windows.php.net/downloads/pecl/releases/uv/0.2.4/php_uv-0.2.4-7.4-ts-vc15-x64.zip" -OutFile "php_uv-0.2.4.zip"
7z x -y php_uv-0.2.4.zip libuv.dll php_uv.dll
copy php_uv.dll ext\php_uv.dll
del php_uv.dll
del php_uv-0.2.4.zip
echo extension=php_sockets.dll >> php.ini
echo extension=php_uv.dll >> php.ini