• This repository has been archived on 21/Nov/2023
  • Stars
    star
    136
  • Rank 266,897 (Top 6 %)
  • Language
    PHP
  • License
    MIT License
  • Created over 8 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Asynchronous cURL executor simply based on resource and Generator.

Co Build Status Coverage Status Scrutinizer Code Quality

Asynchronous cURL executor simply based on resource and Generator

PHP โ“ Feature Restriction
7.0~ ๐Ÿ˜„ Full Support
5.5~5.6 ๐Ÿ˜ง Generator is not so cool
~5.4 ๐Ÿ’ฅ Incompatible
function curl_init_with(string $url, array $options = [])
{
    $ch = curl_init();
    $options = array_replace([
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
    ], $options);
    curl_setopt_array($ch, $options);
    return $ch;
}
function get_xpath_async(string $url) : \Generator
{
    $dom = new \DOMDocument;
    @$dom->loadHTML(yield curl_init_with($url));
    return new \DOMXPath($dom);
}

var_dump(Co::wait([

    'Delay 5 secs' => function () {
        echo "[Delay] I start to have a pseudo-sleep in this coroutine for about 5 secs\n";
        for ($i = 0; $i < 5; ++$i) {
            yield Co::DELAY => 1;
            if ($i < 4) {
                printf("[Delay] %s\n", str_repeat('.', $i + 1));
            }
        }
        echo "[Delay] Done!\n";
    },

    "google.com HTML" => curl_init_with("https://google.com"),

    "Content-Length of github.com" => function () {
        echo "[GitHub] I start to request for github.com to calculate Content-Length\n";
        $content = yield curl_init_with("https://github.com");
        echo "[GitHub] Done! Now I calculate length of contents\n";
        return strlen($content);
    },

    "Save mpyw's Gravatar Image URL to local" => function () {
        echo "[Gravatar] I start to request for github.com to get Gravatar URL\n";
        $src = (yield get_xpath_async('https://github.com/mpyw'))
                 ->evaluate('string(//img[contains(@class,"avatar")]/@src)');
        echo "[Gravatar] Done! Now I download its data\n";
        yield curl_init_with($src, [CURLOPT_FILE => fopen('/tmp/mpyw.png', 'wb')]);
        echo "[Gravatar] Done! Saved as /tmp/mpyw.png\n";
    }

]));

The requests are executed as parallelly as possible ๐Ÿ˜„
Note that there is only 1 process and 1 thread.

[Delay] I start to have a pseudo-sleep in this coroutine for about 5 secs
[GitHub] I start to request for github.com to calculate Content-Length
[Gravatar] I start to request for github.com to get Gravatar URL
[Delay] .
[Delay] ..
[GitHub] Done! Now I calculate length of contents
[Gravatar] Done! Now I download its data
[Delay] ...
[Gravatar] Done! Saved as /tmp/mpyw.png
[Delay] ....
[Delay] Done!
array(4) {
  ["Delay 5 secs"]=>
  NULL
  ["google.com HTML"]=>
  string(262) "<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="https://www.google.co.jp/?gfe_rd=cr&amp;ei=XXXXXX">here</A>.
</BODY></HTML>
"
  ["Content-Length of github.com"]=>
  int(25534)
  ["Save mpyw's Gravatar Image URL to local"]=>
  NULL
}

Table of Contents

Installing

Install via Composer.

composer require mpyw/co:^1.5

And require Composer autoloader in your scripts.

require __DIR__ . '/vendor/autoload.php';

use mpyw\Co\Co;
use mpyw\Co\CURLException;

API

Co::wait()

Wait for all the cURL requests to complete.
The options will override static defaults.

static Co::wait(mixed $value, array $options = []) : mixed

Arguments

  • (mixed) $value
    Any values to be parallelly resolved.
  • (array<string, mixed>) $options
    Associative array of options.
Key Default Description
throw true Whether to throw or capture CURLException or RuntimeException on top-level.
pipeline false Whether to use HTTP/1.1 pipelining.
At most 5 requests for the same destination are bundled into single TCP connection.
multiplex true Whether to use HTTP/2 multiplexing.
All requests for the same destination are bundled into single TCP connection.
autoschedule false Whether to use automatic scheduling by CURLMOPT_MAX_TOTAL_CONNECTIONS.
interval 0.002 curl_multi_select() timeout seconds. 0 means real-time observation.
concurrency 6 Limit of concurrent TCP connections. 0 means unlimited.
The value should be within 10 at most.
  • Throwable which are not extended from RuntimeException, such as Error Exception LogicException are not captured. If you need to capture them, you have to write your own try-catch blocks in your functions.
  • HTTP/1.1 pipelining can be used only if the TCP connection is already established and verified that uses keep-alive session. It means that the first bundle of HTTP/1.1 requests CANNOT be pipelined. You can use it from second yield in Co::wait() call.
  • To use HTTP/2 multiplexing, you have to build PHP with libcurl 7.43.0+ and --with-nghttp2.
  • To use autoschedule, PHP 7.0.7 or later is required.

When autoschedule Disabled:

  • curl_multi_add_handle() call can be delayed.
  • concurrency controlling with pipeline / multiplex CANNOT be correctly driven. You should set higher concurrency in those cases.

When autoschedule Enabled:

  • curl_multi_add_handle() is always immediately called.
  • CURLINFO_TOTAL_TIME CANNOT be correctly calculated. "Total Time" includes the time waiting for other requests are finished.

The details of CURLIFNO_*_TIME timing charts are described at the bottom of this page.

Return Value

(mixed)
Resolved values; in exception-safe context, it may contain...

  • CURLException which has been raised internally.
  • RuntimeException which has been raised by user.

Exception

  • Throws CURLException or RuntimeException in exception-unsafe context.

Co::async()

Execute cURL requests along with Co::wait() call, without waiting resolved values.
The options are inherited from Co::wait().

This method is mainly expected to be used ...

  • When you are not interested in responses.
  • In CURLOPT_WRITEFUNCTION or CURLOPT_HEADERFUNCTION callbacks.
static Co::async(mixed $value, mixed $throw = null) : null

Arguments

  • (mixed) $value
    Any values to be parallelly resolved.
  • (mixed) $throw
    Overrides throw in Co::wait() options when you passed true or false.

Return Value

(null)

Exception

  • CURLException or RuntimeException can be thrown in exception-unsafe context.
    Note that you CANNOT capture top-level exceptions unless you catch outside of Co::wait() call.

Co::isRunning()

Return if Co::wait() is running().
With this check, you can safely call Co::wait() or Co::async().

static Co::isRunning() : bool

Co::any()
Co::race()
Co::all()

Return a Generator that resolves with specific value.

static Co::any(array $value) : \Generator<mixed>
static Co::race(array $value) : \Generator<mixed>
static Co::all(array $value) : \Generator<mixed>
Family Return Value Exception
Co::any() First Success AllFailedException
Co::race() First Success First Failure
  • Jobs CANNOT be canceled.
    Incomplete jobs remain even if Co::any() or Co::race() is resolved.
  • Co::all(...) is just a wrapper of (function () { return yield ...; })().
    It should be only used with Co::race() or Co::any().
Co::wait(function () {
    $group1 = Co::all([$ch1, $ch2, $ch3]);
    $group2 = Co::all([$ch4, $ch5, $ch6]);
    $group1or2 = Co::any([$group1, $group2]);
    var_dump(yield $group1or2);
});

Co::setDefaultOptions()
Co::getDefaultOptions()

Overrides/gets static default settings.

static Co::setDefaultOptions(array $options) : null
static Co::getDefaultOptions() : array

Rules

Conversion on Resolving

The all yielded/returned values are resolved by the following rules.
Yielded values are also resent to the Generator.
The rules will be applied recursively.

Before After
cURL resource curl_multi_getconent() result or CURLException
Array Array (with resolved children) or RuntimeException
Generator Closure
Generator
Return value (after all yields done) or RuntimeException

"Generator Closure" means Closure that contains yield keywords.

Exception-safe or Exception-unsafe Priority

Context in Generator

Exception-unsafe context by default.
The following yield statement specifies exception-safe context.

$results = yield Co::SAFE => [$ch1, $ch2];

This is equivalent to:

$results = yield [
    function () use ($ch1) {
        try {
            return yield $ch1;
        } catch (\RuntimeException $e) {
            return $e;
        }
    },
    function () use ($ch2) {
        try {
            return yield $ch2;
        } catch (\RuntimeException $e) {
            return $e;
        }
    },
];

Context on Co::wait()

Exception-unsafe context by default.
The following setting specifies exception-safe context.

$result = Co::wait([$ch1, $ch2], ['throw' => false]);

This is equivalent to:

$results = Co::wait([
    function () use ($ch1) {
        try {
            return yield $ch1;
        } catch (\RuntimeException $e) {
            return $e;
        }
    },
    function () use ($ch2) {
        try {
            return yield $ch2;
        } catch (\RuntimeException $e) {
            return $e;
        }
    },
]);

Context on Co::async()

Contexts are inherited from Co::wait().
The following setting overrides parent context as exception-safe.

Co::async($value, false);

The following setting overrides parent context as exception-unsafe.

Co::async($value, true);

Pseudo-sleep for Each Coroutine

The following yield statements delay the coroutine processing:

yield Co::DELAY => $seconds
yield Co::SLEEP => $seconds  # Alias

Comparison with Generators of PHP7.0+ or PHP5.5~5.6

return Statements

PHP 7.0+:

yield $foo;
yield $bar;
return $baz;

PHP 5.5~5.6:

yield $foo;
yield $bar;
yield Co::RETURN_WITH => $baz;

Although experimental aliases Co::RETURN_ Co::RET Co::RTN are provided,
Co::RETURN_WITH is recommended in terms of readability.

yield Statements with Assignment

PHP 7.0+:

$a = yield $foo;
echo yield $bar;

PHP 5.5~5.6:

$a = (yield $foo);
echo (yield $bar);

finally Statements

Be careful that return triggers finally while yield Co::RETURN_WITH => does not.

try {
    return '...';
} finally {
    // Reachable
}
try {
    yield Co::RETURN_WITH => '...';
} finally {
    // Unreachable
}

Appendix

Timing Charts

Note that S is equal to Q when autoschedule is disabled.

Basic

ID When
Q curl_multi_exec() immediately after curl_multi_add_handle() called
S Processing started actually
DNS DNS resolution completed
TCP TCP connection established
TLS TLS/SSL session established
HS All HTTP request headers sent
BS Whole HTTP request body sent
HR All HTTP response headers received
BR Whole HTTP response body received
Constant Time
CURLINFO_NAMELOOKUP_TIME DNS - S
CURLINFO_CONNECT_TIME TCP - S
CURLINFO_APPCONNECT_TIME TLS - S
CURLINFO_PRETRANSFER_TIME HS - S
CURLINFO_STARTTRANSFER_TIME HR - S
CURLINFO_TOTAL_TIME BR - Q

With Redirections by CURLOPT_FOLLOWLOCATION

ID When
Q curl_multi_exec() immediately after curl_multi_add_handle() called
S Processing started actually
DNS(1) DNS resolution completed
TCP(1) TCP connection established
TLS(1) TLS/SSL session established
HS(1) All HTTP request headers sent
BS(1) Whole HTTP request body sent
HR(1) All HTTP response headers received
DNS(2) DNS resolution completed
TCP(2) TCP connection established
TLS(2) TLS/SSL session established
HS(2) All HTTP request headers sent
BS(2) Whole HTTP request body sent
HR(2) All HTTP response headers received
BR(2) Whole HTTP response body received
Constant Time
CURLINFO_REDIRECT_TIME HR(1) - Q
CURLINFO_NAMELOOKUP_TIME DNS(2) - HR(1)
CURLINFO_CONNECT_TIME TCP(2) - HR(1)
CURLINFO_APPCONNECT_TIME TLS(2) - HR(1)
CURLINFO_PRETRANSFER_TIME HS(2) - HR(1)
CURLINFO_STARTTRANSFER_TIME HR(2) - HR(1)
CURLINFO_TOTAL_TIME BR(2) - Q

More Repositories

1

axios-case-converter

Axios transformer/interceptor that converts snake_case/camelCase
TypeScript
157
star
2

cowitter

Asynchronous Twitter client compatible with mpyw/co Generator-based flows.
PHP
116
star
3

php-hyper-builtin-server

Reverse proxy for PHP built-in server which supports multiprocessing and TLS/SSL encryption
PHP
106
star
4

eloquent-has-by-non-dependent-subquery

Convert has() and whereHas() constraints to non-dependent subqueries.
PHP
82
star
5

laravel-cached-database-stickiness

Guarantee database stickiness over the same user's consecutive requests
PHP
75
star
6

comphar

Pack all composer dependencies into a single phar file.
PHP
72
star
7

hub-purge

Clear GitHub image caches on README
Shell
35
star
8

FILTER_VALIDATE_EMAIL.js

TypeScript/JavaScript Email validation compatible with PHP's filter_var($value, FILTER_VALIDATE_EMAIL)
TypeScript
26
star
9

eloquent-has-by-join

Convert has() and whereHas() constraints to join() ones for single-result relations.
PHP
24
star
10

EasyCrypt

A class that provides simple interface for decryptable encryption.
PHP
23
star
11

laravel-local-class-scope

A tiny macro that reuse a global scope class as a local scope
PHP
23
star
12

aws-lambda-billing-slack-notification

Node.js 14 ไปฅ้™ๅ‘ใ‘ใฎ AWS ๅฝ“ๆœˆๅˆฉ็”จๆ–™้‡‘ Slack ้€š็Ÿฅใ‚นใ‚ฏใƒชใƒ—ใƒˆ
TypeScript
22
star
13

null-auth

Null Guard for Laravel. Designed for Middleware-based authentication and testing.
PHP
19
star
14

laravel-database-advisory-lock

Advisory Locking Features for Postgres/MySQL/MariaDB on Laravel
PHP
18
star
15

cloudwatch-front-logger

Save your browser console logs to AWS CloudWatch (Inspired by agea/console-cloud-watch)
TypeScript
17
star
16

phpunit-patch-serializable-comparison

Fixes assertSame()/assertEquals() serialization errors running in separate processes.
PHP
14
star
17

laravel-retry-on-duplicate-key

Automatically retry non-atomic upsert operation when unique key constraints are violated.
PHP
13
star
18

compoships-eager-limit

topclaudy/compoships + staudenmeir/eloquent-eager-limit
PHP
12
star
19

scoped-auth

Apply specific scope for user authentication.
PHP
10
star
20

laravel-mysql-system-variable-manager

A tiny extension of MySqlConnection that manages session system variables
PHP
9
star
21

twhelp

Twitter OAuth CLI Helper distributed by Golang cross-compilation.
Go
8
star
22

uuid-ulid-converter

UUID <=> ULID bidirectional converter
PHP
7
star
23

laravel-packages

Library packages list for Laravel
7
star
24

sqlc-restruct

Post-processor for kyleconroy/sqlc
Go
7
star
25

sharable-value-objects

Share value objects that contain the same primitive value as a singleton
PHP
5
star
26

sql-http-proxy

Go
5
star
27

exceper

Provides temporary error handler automatically using set_error_handler() and restore_error_handler().
PHP
5
star
28

laravel-database-mock

[Experimental] Database Mocking Library which mocks PDO underlying Laravel Connection classes
PHP
5
star
29

my-bookmarklet-collection

5
star
30

BaseUTF8

BaseXX encoder/decoder which support any valid UTF-8 sequences.
PHP
4
star
31

privator

Utils for testing private methods and properties on PHP 7.0+
PHP
3
star
32

noerr

Safe property reference chaining without errors
JavaScript
3
star
33

zenn

Zenn ใฎ่จ˜ไบ‹ไฟ็ฎกๅ ดๆ‰€
3
star
34

docker-clean

Remove all dangling images, volumes, networks and randomly named containers
Shell
2
star
35

mockery-pdo

[Experimental] BDD-style PDO Mocking Library for Mockery
PHP
2
star
36

streamable-console

Call interactive artisan command using arbitrary stream instead of STDIN.
PHP
2
star
37

laravel-pdo-emulation-control

Temporarily enable/disable PDO prepared statement emulation
PHP
2
star
38

mpyw

@mpyw's profile
2
star
39

most-similar

Search most similar word from stdin.
C
1
star
40

unique-violation-detector

Detect primary/unique key or constraint violation errors from PDOException.
PHP
1
star
41

amazon-vod-preset-convert-docker

Docker for https://github.com/aws-samples/amazon-vod-preset-convert
Dockerfile
1
star
42

laravel-unique-violation-detector

Detect primary/unique key or constraint violation errors from PDOException.
PHP
1
star
43

oh-my-zsh-lukerandall-extended

oh-my-zsh lukerandall theme which supports extended features
Shell
1
star
44

suve

Interact with AWS Secrets Manager and Parameter Store
Go
1
star
45

laravel-file-errors

A tiny extension that reports validation error details about uploaded files
PHP
1
star
46

FILTER_VALIDATE_EMAIL.html

HTML Pattern Attribute Validation compatible with PHP's filter_var() and mpyw/FILTER_VALIDATE_EMAIL.js
1
star