PHPackages                             kawakami-o3/co - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. kawakami-o3/co

ActiveLibrary[HTTP &amp; Networking](/categories/http)

kawakami-o3/co
==============

Asynchronous cURL executor simply based on resource and Generator

v1.5.4.1(8y ago)0121MITPHPPHP &gt;=5.5.0

Since Aug 24Pushed 8y ago1 watchersCompare

[ Source](https://github.com/kawakami-o3/co)[ Packagist](https://packagist.org/packages/kawakami-o3/co)[ RSS](/packages/kawakami-o3-co/feed)WikiDiscussions master Synced 2mo ago

READMEChangelog (1)Dependencies (7)Versions (6)Used By (1)

Co [![Build Status](https://camo.githubusercontent.com/7ed8c1a3e35647615817b9d2a2d98e9a90983369a23e3117701339b7c3249368/68747470733a2f2f7472617669732d63692e6f72672f6d7079772f636f2e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/mpyw/co) [![Coverage Status](https://camo.githubusercontent.com/8998702e16ba71ac00463191fb1ca7b280699c11efbc581e34ba7bdc0c5b1c13/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f6d7079772f636f2f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/mpyw/co?branch=master) [![Scrutinizer Code Quality](https://camo.githubusercontent.com/506e6f3813e91409b0a804f1ed49a6241fedcdd81457d0280090c6fdaba7ca33/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f6d7079772f636f2f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/mpyw/co/?branch=master)
============================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================

[](#co---)

Asynchronous cURL executor simply based on resource and Generator

PHP❓Feature Restriction7.0~😄Full Support5.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) "
302 Moved
302 Moved
The document has moved
here.

"
  ["Content-Length of github.com"]=>
  int(25534)
  ["Save mpyw's Gravatar Image URL to local"]=>
  NULL
}

```

Table of Contents
-----------------

[](#table-of-contents)

- [Installing](#installing)
- [API](#api)
    - **[Co::wait()](#cowait)**
    - **[Co::async()](#coasync)**
    - [Co::isRunning()](#coisrunning)
    - [Co::any() / Co::race() / Co::all()](#coanycoracecoall)
    - [Co::setDefaultOptions() / Co::getDefaultOptions()](#cosetdefaultoptionscogetdefaultoptions)
- [Rules](#rules)
    - **[Conversion on Resolving](#conversion-on-resolving)**
    - **[Exception-safe or Exception-unsafe Priority](#exception-safe-or-exception-unsafe-priority)**
    - **[Pseudo-sleep for Each Coroutine](#pseudo-sleep-for-each-coroutine)**
    - **[Comparison with Generators of PHP7.0+ or PHP5.5~5.6](#comparison-with-generators-of-php70-or-php5556)**
- [Appendix](#appendix)
    - [Timing Charts](#timing-charts)

Installing
----------

[](#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
---

[](#api)

### Co::wait()

[](#cowait)

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

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

#### Arguments

[](#arguments)

- **`(mixed)`** ***$value***
     Any values to be parallelly resolved.
- **`(array)`** ***$options***
     Associative array of options.

KeyDefaultDescription`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` those 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

[](#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

[](#exception)

- Throws `CURLException` or `RuntimeException` in exception-unsafe context.

### Co::async()

[](#coasync)

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

[](#arguments-1)

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

#### Return Value

[](#return-value-1)

**`(null)`**

#### Exception

[](#exception-1)

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

[](#coisrunning)

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

[](#coanycoracecoall)

Return a Generator that resolves with specific value.

```
static Co::any(array $value) : \Generator
static Co::race(array $value) : \Generator
static Co::all(array $value) : \Generator
```

FamilyReturn ValueException`Co::any()`First Success`AllFailedException``Co::race()`First SuccessFirst 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()

[](#cosetdefaultoptionscogetdefaultoptions)

Overrides/gets static default settings.

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

Rules
-----

[](#rules)

### Conversion on Resolving

[](#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.

BeforeAftercURL resource`curl_multi_getconent()` result or `CURLException`ArrayArray (with resolved children) or `RuntimeException`Generator Closure
GeneratorReturn value (after all yields done) or `RuntimeException`"Generator Closure" means Closure that contains `yield` keywords.

### Exception-safe or Exception-unsafe Priority

[](#exception-safe-or-exception-unsafe-priority)

#### Context in Generator

[](#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()`

[](#context-on-cowait)

**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()`

[](#context-on-coasync)

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

[](#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

[](#comparison-with-generators-of-php70-or-php5556)

#### `return` Statements

[](#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

[](#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

[](#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
--------

[](#appendix)

### Timing Charts

[](#timing-charts)

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

#### Basic

[](#basic)

IDWhenQ`curl_multi_exec()` immediately after `curl_multi_add_handle()` calledSProcessing started actuallyDNSDNS resolution completedTCPTCP connection establishedTLSTLS/SSL session establishedHSAll HTTP request headers sentBSWhole HTTP request body sentHRAll HTTP response headers receivedBRWhole HTTP response body receivedConstantTimeCURLINFO\_NAMELOOKUP\_TIMEDNS - SCURLINFO\_CONNECT\_TIMETCP - SCURLINFO\_APPCONNECT\_TIMETLS - SCURLINFO\_PRETRANSFER\_TIMEHS - SCURLINFO\_STARTTRANSFER\_TIMEHR - SCURLINFO\_TOTAL\_TIMEBR - Q#### With Redirections by `CURLOPT_FOLLOWLOCATION`

[](#with-redirections-by-curlopt_followlocation)

IDWhenQ`curl_multi_exec()` immediately after `curl_multi_add_handle()` calledSProcessing started actuallyDNS(1)DNS resolution completedTCP(1)TCP connection establishedTLS(1)TLS/SSL session establishedHS(1)All HTTP request headers sentBS(1)Whole HTTP request body sentHR(1)All HTTP response headers receivedDNS(2)DNS resolution completedTCP(2)TCP connection establishedTLS(2)TLS/SSL session establishedHS(2)All HTTP request headers sentBS(2)Whole HTTP request body sentHR(2)All HTTP response headers receivedBR(2)Whole HTTP response body receivedConstantTimeCURLINFO\_REDIRECT\_TIMEHR(1) - QCURLINFO\_NAMELOOKUP\_TIMEDNS(2) - HR(1)CURLINFO\_CONNECT\_TIMETCP(2) - HR(1)CURLINFO\_APPCONNECT\_TIMETLS(2) - HR(1)CURLINFO\_PRETRANSFER\_TIMEHS(2) - HR(1)CURLINFO\_STARTTRANSFER\_TIMEHR(2) - HR(1)CURLINFO\_TOTAL\_TIMEBR(2) - Q

###  Health Score

27

—

LowBetter than 49% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity5

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 95.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~119 days

Total

5

Last Release

3072d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2c31c65dbc5a041c4fa8037bd048edd3aa4a912211fc587b11804396b092d222?d=identicon)[kawakami-o3](/maintainers/kawakami-o3)

---

Top Contributors

[![mpyw](https://avatars.githubusercontent.com/u/1351893?v=4)](https://github.com/mpyw "mpyw (260 commits)")[![scrutinizer-auto-fixer](https://avatars.githubusercontent.com/u/6253494?v=4)](https://github.com/scrutinizer-auto-fixer "scrutinizer-auto-fixer (5 commits)")[![chitoku-k](https://avatars.githubusercontent.com/u/6535425?v=4)](https://github.com/chitoku-k "chitoku-k (4 commits)")[![kawakami-o3-2nd](https://avatars.githubusercontent.com/u/83420975?v=4)](https://github.com/kawakami-o3-2nd "kawakami-o3-2nd (1 commits)")[![pborreli](https://avatars.githubusercontent.com/u/77759?v=4)](https://github.com/pborreli "pborreli (1 commits)")[![rhenium](https://avatars.githubusercontent.com/u/1033740?v=4)](https://github.com/rhenium "rhenium (1 commits)")

---

Tags

httpasynchronouscurlgeneratorparallel

###  Code Quality

TestsCodeception

### Embed Badge

![Health badge](/badges/kawakami-o3-co/health.svg)

```
[![Health](https://phpackages.com/badges/kawakami-o3-co/health.svg)](https://phpackages.com/packages/kawakami-o3-co)
```

###  Alternatives

[rmccue/requests

A HTTP library written in PHP, for human beings.

3.6k34.5M258](/packages/rmccue-requests)[chuyskywalker/rolling-curl

Rolling-Curl: A non-blocking, non-dos multi-curl library for PHP

207446.6k6](/packages/chuyskywalker-rolling-curl)[php-http/curl-client

PSR-18 and HTTPlug Async client with cURL

48247.0M384](/packages/php-http-curl-client)[khr/php-mcurl-client

wrap curl client (http client) for PHP 5.3; using php multi curl, parallel request and write asynchronous code

71219.8k6](/packages/khr-php-mcurl-client)[meabed/php-parallel-soap

Multi curl SoapClient that allow to perform multiple requests to SoapServer

4389.1k](/packages/meabed-php-parallel-soap)[phpgt/fetch

Asynchronous HTTP client with promises.

3724.0k3](/packages/phpgt-fetch)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
