Giter Club home page Giter Club logo

promises's Introduction

Guzzle

Guzzle, PHP HTTP client

Latest Version Build Status Total Downloads

Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and trivial to integrate with web services.

  • Simple interface for building query strings, POST requests, streaming large uploads, streaming large downloads, using HTTP cookies, uploading JSON data, etc...
  • Can send both synchronous and asynchronous requests using the same interface.
  • Uses PSR-7 interfaces for requests, responses, and streams. This allows you to utilize other PSR-7 compatible libraries with Guzzle.
  • Supports PSR-18 allowing interoperability between other PSR-18 HTTP Clients.
  • Abstracts away the underlying HTTP transport, allowing you to write environment and transport agnostic code; i.e., no hard dependency on cURL, PHP streams, sockets, or non-blocking event loops.
  • Middleware system allows you to augment and compose client behavior.
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');

echo $response->getStatusCode(); // 200
echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8'
echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}'

// Send an asynchronous request.
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
$promise = $client->sendAsync($request)->then(function ($response) {
    echo 'I completed! ' . $response->getBody();
});

$promise->wait();

Help and docs

We use GitHub issues only to discuss bugs and new features. For support please refer to:

Installing Guzzle

The recommended way to install Guzzle is through Composer.

composer require guzzlehttp/guzzle

Version Guidance

Version Status Packagist Namespace Repo Docs PSR-7 PHP Version
3.x EOL (2016-10-31) guzzle/guzzle Guzzle v3 v3 No >=5.3.3,<7.0
4.x EOL (2016-10-31) guzzlehttp/guzzle GuzzleHttp v4 N/A No >=5.4,<7.0
5.x EOL (2019-10-31) guzzlehttp/guzzle GuzzleHttp v5 v5 No >=5.4,<7.4
6.x EOL (2023-10-31) guzzlehttp/guzzle GuzzleHttp v6 v6 Yes >=5.5,<8.0
7.x Latest guzzlehttp/guzzle GuzzleHttp v7 v7 Yes >=7.2.5,<8.4

Security

If you discover a security vulnerability within this package, please send an email to [email protected]. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see Security Policy for more information.

License

Guzzle is made available under the MIT License (MIT). Please see License File for more information.

For Enterprise

Available as part of the Tidelift Subscription

The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

promises's People

Contributors

alexeyshockov avatar andont avatar ansionfor avatar baileylo avatar bpolaszek avatar ediporeboucas avatar enleur avatar erikn69 avatar gmponos avatar grahamcampbell avatar ikeyan avatar imme-emosol avatar itafroma avatar jeremeamia avatar jeskew avatar jimcottrell avatar joshdifabio avatar kicken avatar mcfedr avatar mtdowling avatar ntzm avatar nyholm avatar reedy avatar sagikazarmark avatar stevenwadejr avatar suzuki avatar timwolla avatar tobion avatar villfa avatar yched avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

promises's Issues

resolve() cannot omit the value.

the thenable cannot omit the value for resolve().

PHP Fatal error:  Uncaught ArgumentCountError: Too few arguments to function GuzzleHttp\Promise\Promise::resolve(), 0 passed

but it's very useful for just resolve the promise without returning the value.
For ReactPHP and nodejs, we can omit the resolve value and just resolve the promise directly.

Add splat() function

I was just writing some code that would have been greatly improved by having a splat() function that works similarly to all().

The current code is:

Promise\all($promises)->then(function ($results) {
    list($first, $second) = $results;
    ...
});

But what I would rather write is:

Promise\splat($promises)->then(function ($first, $second) {
    ...
});

This would require PHP >= 5.6 for argument unpacking or the use of call_user_func_array.

Unhandled promise

I think it's good that we have a method like set_exception_handler for promise that sets a user-defined unhandled promise rejection handler function

deliver method used in README.md

In README.md in a few examples the method deliver is used. This method is not available anymore. Maybe the examples should use resolve now? I saw that deliver was a private method in early commits. (Would the examples work with a private method?)

Error in the documentation

Hello,

there is an error in the documentation. The last piece of shown code refers to the deliver method, but this method does not exist in the whole Promises code base:

$promise = new Promise();
$promise->then(function ($value) { echo $value; });
// The promise is the deferred value, so you can deliver a value to it.
$promise->deliver('foo');

This code is broken because the deliver method is not defined, and will throw a PHP fatal error, but we can find this example in the documentation.

ReactTaskQueue in the core

I think it's useful to have task queue adapters (TaskQueueInterface implementations) for popular event loops in the core, such as ReactPHP event loop.

Tag a new release

Please tag a new release so that there's a stable version with Throwable support.

reactphp/promise

How can I convert guzzle/promises to reactphp/promise ?

// $guzzlePromise is an instance of GuzzleHttp\Promise\Promise and is a result of third party library
$reactPromise = convert($guzzlePromise);

Dynamic promise stack

Hello,

TL; DR

Guzzle's all($promises) function should be recursive: resolution of $promises may add new promises into the $promises stack. These new promises should not be left pending.

Example

I'm using Guzzle's all() function to resolve a collection of promises, stored in an iterator.
What if one of these promises, when resolved, adds a new promise to the stack?

Have a look at the following code:

$data       = new ArrayObject();
$promises   = new ArrayIterator();
$promises[] = $fooPromise = new \GuzzleHttp\Promise\Promise(function () use ($promises, &$fooPromise, $data) {
    var_dump('Loading foo');
    $data[] = 'foo';
    $fooPromise->resolve('foo');
});

$fooPromise->then(function () use (&$promises, $data) {

    # When $fooPromise is resolved, for some reason I may add some data with another promise
    $barPromise = new \GuzzleHttp\Promise\Promise(function () use ($promises, &$barPromise, $data) {
        var_dump('Loading bar');
        $data[] = 'bar';
        $barPromise->resolve('bar');
    });

    $promises[] = $barPromise;

});

$mainPromise = \GuzzleHttp\Promise\all($promises);
$mainPromise->wait();
var_dump($data->getArrayCopy());

I expect the following output:

string(11) "Loading foo"
string(11) "Loading bar"
array(2) {
  [0]=>
  string(3) "foo"
  [1]=>
  string(3) "bar"
}

But I get this:

string(11) "Loading foo"
array(1) {
  [0]=>
  string(3) "foo"
}

And my $secondPromise state is pending. Why is it not resolved?

Thank you,
Ben

General question

I can't seem to find a "core" answer that is really important to me, sorry if obvious/written but how does a core promise object can work async? Does it pops a new php process on the server or separate thread?
Thks for the hints.

Producer -> consumer chain?

I don't really understand can I implement producer-consumer scheme using this library?

For example, imagine a producing function which makes some values over time, and another consuming function which takes the values. Is there a way to implement this using Promises?

Done instead of Then

Please see https://github.com/reactphp/promise#done-vs-then
How can I have "done" in guzzle/promises?

I need this feature in Event Loop Integration

Calling done() transfers all responsibility for errors to your code. If an error (either a thrown exception or returned rejection) escapes the $onFulfilled or $onRejected callbacks you provide to done, it will be rethrown in an uncatchable way causing a fatal error.

$onFulfilled and $onRejected functions are never called in a REPL

Running this example code you wrote in a REPL (with php -a) will never output the value string:

use GuzzleHttp\Promise\FulfilledPromise;

$promise = new FulfilledPromise('value');

// Fulfilled callbacks are immediately invoked.
$promise->then(function ($value) {
    echo $value;
});

I tried to write some data in a file instead of writing on the standard output, but the issue is the same: the $onFulfilled and $onRejected functions are never called while it works perfectly in a dedicated PHP file.

This issue also occurs with Psy Shell.

Promise not working as async without wait()

The code works if i do $promise->wait();, however i am not sure how can i call the promise to work async.

foreach ($entries as $key=>$value)
        {     
                $promise = new Promise(function() use (&$promise, $key, $value) 
                {   
                    // Do some heavy lifting work.
                    $mainOutput = array($key, $output, $value);
                    $promise->resolve($mainOutput);
                });

                $promise->then(
                    // $onFulfilled
                    function ($mainOutput)
                    {
                        static::addToResponse($mainOutput);
                    },
                    // $onRejected
                    function ($reason) 
                    {
                        echo 'The promise was rejected.';
                    }
                );
            if(static::$sync)  
            {    
                $promise->wait();
            }
            else
            {

            }
        }

Unit testing

Hi,

I'm attempting to do some unit testing using a class that utilizes the promises library. Here's the general approach (in a file MyTest.php):

use PHPUnit\Framework\TestCase;
use GuzzleHttp\Promise\FulfilledPromise;

class MyTest extends TestCase {

  /**
   * Tests unit testing with callbacks.
   */
  public function testFoo() {
    $p = new FulfilledPromise("");
    $me = $this;
    $p->then(function() use ($me) {
        $me->assertTrue(TRUE, "Foo promise returning method succeeded.");
        print (sprintf("%s::%s line %s: Success callback executed\n", get_class($this), __FUNCTION__, __LINE__));
    }, function() use ($me) {
        $me->assertTrue(FALSE, "Foo promise returning method succeeded.");
        print (sprintf("%s::%s line %s: Failure callback executed\n", get_class($this), __FUNCTION__, __LINE__));
    });
  }
}

The test completes without any assertions occurring (at least, none whose output is recognized by the testing suite). It appears the callbacks execute after the unit test completes:

PHPUnit 5.2.9 by Sebastian Bergmann and contributors.

Runtime:       PHP 5.6.18 with Xdebug 2.3.3
Configuration: [excluded]

.                                                                   1 / 1 (100%)

Time: 154 ms, Memory: 5.25Mb

OK (1 test, 0 assertions)
MyTest::{closure} line 91: Success callback executed

Do you know of a decent approach to using PHPUnit with Guzzle promises?

Can I manipulate the response body on fulfilled promise with a callback?

here's my example:

// Build Asynchronous Promise
$promise = $this->client->requestAsync($this->method, $path, $options);

// Extract target data from response JSON after request fulfilled
$promise->then(
    function ($response) {
        $result = GuzzleResponseParser::parse($response);
        $extracted = $this->getTargetDataFromResult($result);

        //This dumps the right data
        //var_dump($extracted);
        //exit();

        // This, however, does not work as I'd expect.  It doesn't error, but see notes below.                 
        return $extracted;
    }
);

I am later using

$results = Promise\unwrap($promises);
foreach ($results as $key => $result) {
    $this->data[$key] = $result;
}

I was hoping the $extracted in the then() closure would translate to the $result in the post-unwrap loop, but this does not happen. I see the regular result, not the extracted result.

Am I going about this wrong? I've reviewed documentation and can't seem to find what I need. Might the be another way to do this, or is it not possible? Thanks in advance!

Event loop integration makes bad recommendation

It suggests the following as event loop integration for ReactPHP:

$loop = React\EventLoop\Factory::create();
$loop->addPeriodicTimer(0, [$queue, 'run']);

That will basically be an infinite loop that always calls [$queue, 'run'] and hogs the CPU until the entire thing is done. A better way would be a short timer like 10 milliseconds. It's not as responsive, but at least it doesn't consume 100% CPU.

Exceptions in nested handlers are not reported

$p = new Promise();
$p->then(function () {
    $inner = new Promise();
    $inner->then(function () {
        throw new \RuntimeException('This exception should not be trapped');
    });
    $inner->resolve(true);
    $inner->wait();

    print_r(['state' => $inner->getState()]);

    return $inner;
});
$p->resolve(true);
$p->wait();

print_r(['state' => $inner->getState()]);

In this scenario, the RuntimeException should not be trapped. Both promises are correctly fulfilled but the exception is never seen.

Expected Result

  1. RuntimeException is thrown.

Current Result

  1. RuntimeException is never seen.

Promises problem in PHPUnit

I have a problem with my Bearer-Authorization in Guzzle-HTTP. I use it to test my PHP-REST-API with PHPUnit.
Here is my test method:

public function testGetMe()
{
$client = new Client([
'base_uri' => $this->apiBaseURL
]);
$data = ['email' => $email, 'password' => '12345'];
$client->post('register', [
'form_params' => $data]
);
$responseJson = json_decode($response->getBody());
$myToken = $responseJson->data->token;

$response = $client->request('GET', 'users', [
    'headers' => [
        'Authorization'      => 'Bearer '.$myToken
    ],
    'debug' => true
]);

}
`

But if I set the token hard coded like this:
`
public function testGetMe()
{
$client = new Client([
'base_uri' => $this->apiBaseURL
]);
$data = ['email' => $email, 'password' => '12345'];
$client->post('register', [
'form_params' => $data]
);
$responseJson = json_decode($response->getBody());
$myToken = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE0NjQ5NzExMzQsImp0aSI6IjByR3FpOW15Rm1rRGo2TU9sMVhkK3dRU3p1V0pWejM1UEhiU2dTMmg5SEU9IiwiaXNzIjoiQXBwTmFtZSIsIm5iZiI6MTQ2NDk3MTE0NCwiZXhwIjoxNDY0OTczMTQ0LCJzdWIiOiJ0ZXN0QG1haWwuZGUifQ.yA4a_S6ILCeqENm00H712g9uF5g9eSz_BmnaMDdZ2r4p5e1q88g0T09IG2WKCi1oExoBfQ8VTmKeX6ZQv0RydQ;

$response = $client->request('GET', 'users', [
    'headers' => [
        'Authorization'      => 'Bearer '.$myToken
    ],
    'debug' => true
]);

}
`

and also with Postman, it is working. It's the same token which I receive from my REST-API.

When I debug the code & wait on line: $response = $client->request('GET', 'users'... the test is ok... seems to be something wrong with asynch requesting ?

Do you have any ideas what's wrong?

Asynchronous sleep

I was building some async code that triggers a delayed retry in the case of an error, similar to:

$promise = $client->getAsync(...);

$promise->then(
  function ($result) { ... },
  function ($error) {
    sleep(5); 
    $client->getAsync(...);
  }
);

Of course, sleep(5) is blocking. Is there an asynchronous equivalent for sleep, such as a promise that will resolve within a wait() call after the given amount of time? If not, can one be added to the library?

Please tag a version 1.0.2

I do need that feature of conditionally loading the global functions due to messed up autoloading on my side.

Promises callbacks not called until shutdown

Hi

From the guide in the readme:

use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise
    ->then(function ($value) {
        // Return a value and don't break the chain
        return "Hello, " . $value;
    })
    // This then is executed after the first then and receives the value
    // returned from the first then.
    ->then(function ($value) {
        echo $value;
    });

// Resolving the promise triggers the $onFulfilled callbacks and outputs
// "Hello, reader".
$promise->resolve('reader.');

Should output "Hello, reader".

However, this doesn't happen until it appears a shutdown handler is called: https://github.com/guzzle/promises/blob/master/src/TaskQueue.php#L21 or the waitIfPending() method is called.

If a promise is resolved manually, then the callbacks DO NOT fire. I can't seem to work out if this is intentional or a bug, it doesn't make sense that the handlers are called only in one scenario of waiting for a promise.

Can someone explain.

Thanks

Using "then" for determining whether an argument is a promise.

Hi,

I'd just like to make a request about this bit of code in FulfilledPromise:

        if (method_exists($value, 'then')) {
            throw new \InvalidArgumentException(
                'You cannot create a FulfilledPromise with a promise.');
        }

Is there any chance you could change that first line to:

      if ($value instanceof 'GuzzleHttp\Promise\PromiseInterface')){

I'm trying to pass a non-promise object as an argument, and it's bailing because my object has a 'then' method.

cancel() not bubbling up to recursive promises?

The following code yields no response. Shouldn't it trigger the cancel() method in the inner promise? Is this a bug or a feature?

$promise = new Promise();
$promise->resolve(true);
$promise
    ->then(function() {
        return (new Promise())
            ->then(function() {
                echo "inner-promise fulfilled!";
            })
            ->otherwise(function() {
                echo "inner-promise rejected!";
            });
    })
    ->then(function() {
        echo "promise fulfilled!";
    })
    ->otherwise(function() {
        echo "promise rejected!";
    })
    ->cancel();

\GuzzleHttp\Promise\queue()->run();

[DOUBT] Problems implementing a "wrapper" promise

Hello,

I'm implementing a DB client for a database using HTTP as its wire protocol. I'm trying to implement a wrapper for the promises returned by the Guzzle\Client, but it's clear that I've misunderstood something about this library ^_^ .

Before starting delving into details, my problem seems to be simple: In theory my code is pretending to "catch" Guzzle's exceptions and wrapping it with my own class. What is happening is the Guzzle exception is thrown at some point which I can't identify, and remains uncaught.

Here you can see my code, full of debug prints:

<?php


namespace Druid4PHP\Responses;


use Druid4PHP\Errors\DruidRequestError;
use Druid4PHP\Errors\DruidServerError;
use Druid4PHP\Errors\DruidResponseParseError;
use Druid4PHP\Queries\Query;

use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\PromiseInterface;

use Psr\Http\Message\ResponseInterface;


/**
 * @method Response|array wait($unwrap = true)
 */
final class AsyncResponse extends Promise
{
    /** @var Query */
    private $query;

    /** @var PromiseInterface */
    private $httpPromise;

    /** @var bool */
    private $encapsulation;

    /**
     * AsyncResponse constructor.
     * @param Query $query
     * @param PromiseInterface $httpPromise
     * @param bool $encapsulation
     */
    public function __construct(Query $query, PromiseInterface $httpPromise, $encapsulation = true)
    {
        echo "\nINSIDE __construct\n";

        $this->query = $query;
        $this->httpPromise = $httpPromise->then(
            function (ResponseInterface $response) {
                echo "\nIN A FEW nanos WE'LL GET INTO this->onFulfilledHttpPromise\n";
                return $this->onFulfilledHttpPromise($response);
            },
            function (\Exception $e) {
                echo "\nIN A FEW nanos WE'LL GET INTO this->onRejectedHttpPromise\n";
                return $this->onRejectedHttpPromise($e);
            }
        );
        $this->encapsulation = $encapsulation;

        parent::__construct(
            function () {
                echo "\nIN A FEW nanos WE'LL GET INTO this->waitHandler\n";
                return $this->waitHandler();
            },
            function () {
                echo "\nIN A FEW nanos WE'LL GET INTO this->cancelHandler\n";
                return $this->cancelHandler();
            }
        );

        echo "\nGOING OUT OF __construct\n";
    }

    private function waitHandler()
    {
        echo "\nNOW WE'LL CALL httpPromise->wait\n";

        $this->httpPromise->wait();

        echo "\nNOW WE'VE CALLED httpPromise->wait\n\n";
    }

    private function cancelHandler()
    {
        echo "\nNOW WE'LL CALL httpPromise->cancel\n";

        $this->httpPromise->cancel();

        echo "\nNOW WE'VE CALLED httpPromise->cancel\n";
    }

    private function onFulfilledHttpPromise(ResponseInterface $response)
    {
        echo "\nINSIDE onFulfilledHttpPromise\n";

        // TODO: allow stream decoding without having to call getContents.
        $rawResponse = $response->getBody()->getContents();
        $rawResults = json_decode($rawResponse, true);

        if (JSON_ERROR_NONE !== json_last_error()) {
            throw new DruidResponseParseError($this->query, $rawResponse, 'Received invalid JSON');
        }

        echo "\nIN A FEW nanos WE'll GO INSIDE this->resolve\n";

        $this->resolve(
            $this->encapsulation ? $this->query->parseResponse($rawResults) : $rawResults
        );

        echo "\nGOING OUT OF onFulfilledHttpPromise\n";
    }

    private function onRejectedHttpPromise(\Exception $e)
    {
        echo "\nINSIDE onRejectedHttpPromise (".get_class($this).")\n";

        $eCode = $e->getCode();
        if ($eCode >= 500 && $eCode < 600) {
            $this->reject(new DruidServerError($this->query, $e));
        } else {
            $this->reject(new DruidRequestError($this->query, $e));
        }

        echo "\nGOING OUT OF onRejectedHttpPromise\n";
    }
}

and here you can see the output of one of my executions:


Starting with timeseries queries:

INSIDE __construct

GOING OUT OF __construct

IN A FEW nanos WE'LL GET INTO this->waitHandler

NOW WE'LL CALL httpPromise->wait

IN A FEW nanos WE'LL GET INTO this->onRejectedHttpPromise

INSIDE onRejectedHttpPromise (Druid4PHP\Responses\AsyncResponse)

GOING OUT OF onRejectedHttpPromise

NOW WE'VE CALLED httpPromise->wait

PHP Fatal error:  Uncaught exception 'GuzzleHttp\Exception\ServerException' with message 'Server error: `POST http://127.0.0.1:8082/druid/v2/` resulted in a `500 Internal Server Error` response:
{"error":"Instantiation of [simple type, class io.druid.query.timeseries.TimeseriesQuery] value failed: Missing fields [ (truncated...)
' in /home/acorrea/PhpstormProjects/Druid4PHP/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:107
Stack trace:
#0 /home/acorrea/PhpstormProjects/Druid4PHP/vendor/guzzlehttp/guzzle/src/Middleware.php(65): GuzzleHttp\Exception\RequestException::create(Object(GuzzleHttp\Psr7\Request), Object(GuzzleHttp\Psr7\Response))
#1 /home/acorrea/PhpstormProjects/Druid4PHP/vendor/guzzlehttp/promises/src/Promise.php(201): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Response))
#2 /home/acorrea/PhpstormProjects/Druid4PHP/vendor/guzzlehttp/promises/src/Promise.php(154): GuzzleHttp\Promise\Promise::callHandler(1, Object(GuzzleHttp\Psr7\Response), Array)
#3 /home/acorrea/PhpstormProjects/ in /home/acorrea/PhpstormProjects/Druid4PHP/src/Responses/AsyncResponse.php on line 116

what is surprising to me is the exception is being thrown after this piece if code ( $this->httpPromise->wait(); ) gets executed, and not during its execution.

Any idea to fix my implementation?

Thank you in advance.

Confused about the order of the ->then callback resolution

I would expect the 3 ->then to come up in the 3rd place. Is this a bug or as per design? What is the logic behind it?

It looks like a bug because of chaining ->then()->then, since on the fair race, one then comes after the other but always on the promise object instead of in the return value of the last then...

$cheating = new GuzzleHttp\Promise\Promise(
    function () use(&$cheating){
        $cheating->resolve(null);
        echo "\n--- Finish line ---\n";
    }
);

$cheating->then(function() {
    echo "I'm the #1!\n";
})->then(function() {
    echo "I'm the #2...\n";
});

$cheating->then(function() {
    echo "Haha! And I'm the #3? Nop!\n";
});

$cheating->wait(false);

$fairRace = new GuzzleHttp\Promise\Promise(
    function () use(&$fairRace){
        $fairRace->resolve(null);
        echo "\n--- Finish line on Fair Race ---\n";
    }
);

$fairRace->then(function() {
    echo "I'm the #1!\n";
});
$fairRace->then(function() {
    echo "I'm the #2...\n";
});

$fairRace->then(function() {
    echo "I'm the #3 :(\n";
});

$fairRace->wait(false);

This is the output I see:


--- Finish line ---
I'm the #1!
Haha! And I'm the #3? Nop!
I'm the #2...

--- Finish line on Fair Race ---
I'm the #1!
I'm the #2...
I'm the #3 :(

My composer.lock says I'm using 1.2

            "version": "1.2.0",
            "source": {
                "type": "git",
                "url": "https://github.com/guzzle/promises.git",
                "reference": "c10d860e2a9595f8883527fa0021c7da9e65f579"

Omit the parameter in the callback function of then/otherwise

This is not an issue, I just wanted to get your opinion on a topic.

To resolve a promise we do

$promise->then(function[...]

The callback parameter gets the ResponseInterface $response as function parameter. What if I don't want to use that value? If I just want to log something, or dunno, do something else which has nothing to do with the response. Do you think it's good practice to do $promise->then(function () or would you always write $promise->then(function (ResponseInterface $response)) even though you won't need that parameter?

Was just wondering, because omitting the parameter would mean, that we are getting a warning about calling a method with no parameter, right?

Promise chain breaks if then() returns a value

If you have something like this:

$client = new Client();
$client->requestAsync('GET', 'http://example.com')->then(function($result) {
  // This executes.
  return 'Hello';
})->then(function($value) {
  // This executes as well.
  return $value . ' World!';
})->then(function($value) {
  // This never executes.
  return 'Never gonna give you up';
});

The reason for this is (from what I can tell) because Guzzle wraps the first handler in a promise, but it doesn't wrap anything else because this library does not wrap a then() value.

I think it should be consistent either way, it was hard to debug why this was happening. Wrapping the second then() in a new FullfilledPromise() like this:

return new FullfilledPromise($value . ' World!');

fixed my problem.

I expected a value return in then() to be wrapped in a promise all the time, so I think that's what should happen and it should be a trivial change and I don't think it should break anyone's apps (except for that promises that weren't ever executed will now be executed).

Useless library?

I don't get it how is this useful if it doesn't work async? The whole point of promises is to execute code async... so like wtf?

How to unit test a promise?

Disclaimer: This is not a bug, this is just a question.

I have created a web client for a particular API. I also created a Middleware for handling exceptions, the code looks like this:

class ErrorHandlerMiddleware {
    public static function create() {
        return function (callable $handler) {
            return function ($request, array $options) use ($handler) {
                if ($options['http_errors']) {
                    return $handler($request, $options);
                }

                return $handler($request, $options)->then(
                    function (ResponseInterface $response) use ($request, $handler) {
                        $code = $response->getStatusCode();
                        if ($code < 400) {
                            return $response;
                        }

                        switch ($code) {
                            // ....
                            case '401':
                                throw new AccessDeniedException('Access Denied', $request, $response);
                            //...
                        }

                    },
                    function (RequestException $e) use ($request, $handler) {
                        throw new ConnectException('Service not working', $request);
                    }
                );
            };
        };
    }
}

My testing strategy is to create a dummy response object and set the status code to say 401 and then assert that an AccessDeniedException is thrown.

This is what I've done in my unit test

public function testFoo() {
    $options = [
        'http_errors' => false
    ];
    $request = new \GuzzleHttp\Psr7\Request('get', 'http://foo.com');
    $handler = function($request, $options) {
        return new GuzzleHttp\Promise\Promise();
    };

    $middleware = ErrorHandlerMiddleware::create();
    $request_processor = $middleware($handler);

    $promise = $request_processor($request, $options);
}

According to the documentation I need to "resolve" the promise so that I get my callback.

Question 1: How do I use my dummy $response object with the callback defined in the promise?

I tried something like this

$resolved = $promise->resolve('return new \GuzzleHttp\Psr7\Response(200)');
var_dump($resolved());

But I get this error message: "Function name must be a string"

Question 2: It seems like "resolve" only accepts strings, if that's the case, How Am I supposed to use my $response object inside the promise?

Changeable TaskQueue for better event loop integration

I would like to propose a change for better event loop integration.

The issues

Current way for event loop integration (using addPeriodicTimer or futureTick) leads to 100% CPU load, because it kills the whole idea. There is always something to do (run the promises queue), so the timeout before the next tick is always 0 or near it.

It also leads to an endless event loop, because, again, there is always something to do.

The proposed solution

Make the TaskQueue changeable in the code, to be able to set a proper implementation in case of an event loop. A simple event loop implementation could do $loop->nextTick([$queue, 'run']) inside the add() method, so only when it's needed.

I think it can be done with

  1. introducing TaskQueueInterface that will contain all methods from the current TaskQueue,
  2. adding TaskQueue::getInstance() and TaskQueue::setInstance(TaskQueueInterface $q) methods,
  3. changing calls to queue() inside the code to TaskQueue::getInstance().

This way doesn't require BC breaks.

What do you think about it?

Example

Provide some good example working one...

How promise is async

It runs promise callbacks on php script shutdown.. So all the promises will be executed one by one..

i want to know if it really works parallel

Uncaught Promises should throw Exception

I've noticed (#73) that uncaught errors do not throw exceptions, this can make debugging really difficult because there isn't an obvious reason of why a promise chain is breaking.

I believe in JavaScript, an uncaught promise error throws a fatal error, so I think throwing an exception (outside of the promise chain) for uncaught promises would be a good idea here.

Allow throwing an exception to actually propagate

Hey there,

I've got the following code:

$promise->then(
                // $onFulfilled
                function ($user) {
                   // bla bla
                },
                // $onRejected
                function ($reason) {
                    throw new Error('reset_password_token_expired');
                }
            );

but the error is being caught.

What can I do to allow it to propagate up the error chain and crash the app?

Thank you

Coroutine issue with 1.3.0 release

Latest 1.3.0 release includes coroutine changes that seems break aws-sdk-php behaviors.
coroutine is used in our instanceCredentialProvider, which is chained in our defaultCredentialProvider

This problem is related to guzzle because there has been no related code changes in our repo, and when downgrade to 1.2.0 fix the problem.

Personally, I'm having trouble in reproducing the issue locally or with a new ubuntu ec2 instance, and I'm still woking on reproducing it since many customer have reported seeing this issue happening.

Related issue:
aws/aws-sdk-php#1125
guzzle/guzzle#1668

Possible reproduce repo: https://github.com/reproduce/guzzle/tree/issue1668

Open this issue here as well just to have more attention, background and thoughts here. :)

Promise resolution and reference

Hi everyone,

TL; DR

Make promise resolution simpler:

$promise = new \GuzzleHttp\Promise\Promise(function () use (&$promise) {
    $promise->resolve('The referenced promise has been resolved');
});

to:

$promise = new \GuzzleHttp\Promise\Promise(function (PromiseInterface $promise) {
    $promise->resolve('The promise has been resolved');
});

Example

Actually, when you create a promise from scratch you must use a reference to that promise within the wait function, i.e.:

$promise = new \GuzzleHttp\Promise\Promise(function () use (&$promise) {
    $promise->resolve('The referenced promise has been resolved');
});

From a DX point of view, this can lead to headaches when it comes to generate promises within a loop. Let's take this as an example:

use GuzzleHttp\Promise\Promise;
use function GuzzleHttp\Promise\all;

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

$translationMap = [
    'banana' => 'banane',
    'apple'  => 'pomme',
];

$fruits = [
    'banana',
];

$promises = [];

foreach ($fruits AS $fruit) {
    $promises[$fruit] = $promise = new Promise(function () use (&$promise, $translationMap, $fruit) {
        $promise->resolve($translationMap[$fruit]);
    });
}

var_dump(all($promises)->wait()); // Expected ['banana' => 'banane'] and indeed got ['banana' => 'banane']

In this example, there's only 1 instance of $promise, and everything's fine. Now add an apple into the $fruits array:

use GuzzleHttp\Promise\Promise;
use function GuzzleHttp\Promise\all;

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

$translationMap = [
    'banana' => 'banane',
    'apple'  => 'pomme',
];

$fruits = [
    'banana',
    'apple',
];

$promises = [];

foreach ($fruits AS $fruit) {
    $promises[$fruit] = $promise = new Promise(function () use (&$promise, $translationMap, $fruit) {
        $promise->resolve($translationMap[$fruit]);
    });
}

var_dump(all($promises)->wait()); // Expected ['banana' => 'banane', 'apple' => 'pomme'] 
// But got GuzzleHttp\Promise\RejectionException: Invoking the wait callback did not resolve the promise...

Of course, this RejectionException would not happen if I chose a different variable name for each $promise. But honestly this is ugly.

A simpler solution would be to pass the promise itself directly as an argument into the wait function, instead of relying on a reference, i.e:

$promise = new \GuzzleHttp\Promise\Promise(function (PromiseInterface $promise) {
    $promise->resolve('The promise has been resolved');
});

This would require a change into the Promise::invokeWaitFn() method:

$wfn($this);
// instead of
$wfn(true);

This would simplify a lot the promise resolution.
What's your opinion ?

Inject TaskQueue and remove global TaskQueue?

If we're brainstorming on 2.0 ideas... then what about removing the concept of a global dependency on a TaskQueue? In the current implementation, promises are added to a global TaskQueue. That queue can also be swapped out for other queues if needed (like React), but doing so might have implications on other aspects of an application that were relying on a different global TaskQueue's behavior. The bigger problem is that because there's a global TaskQueue, Guzzle can't be used in multiple threads.

A better approach would be to require that a promise is created with a reference to the TaskQueue so that chaining off of a promise would add the tasks to the right queue.

This kind of change would probably require another abstraction and a lot of work to ensure it is ergonomic. I'd also be interested to see if we could ensure that there is not a circular reference between a promise and a TaskQueue.

Throwing Exception from within promises handler doesn't stop script execution

I'm doing some integration testing with a library and use guzzle to make an async http request and noticed that my tests were always passing even when that is not expected. Through debugging narrowed down the issue to being caused by the fact that an exception thrown from a then in a promise won't stop the system from running.

In the snippet bellow for instance, I'm able to get the log in the error.log but PHPUnit never gets the exception when running the unit test. I also tried running this in isolation outside of the unit testing context and same behavior is exhibited

$promise = $httpClient->requestAsync('GET', 'http://some-api-url');

$promise->then(
    function ($response) {
        error_log('Exceptions thrown in promise do not bubble-up');
        throw new Exception('Invalid hit sent. got response: ' . $response->getBody());
    },
    function ($exception) {
        // handle HTTP request exception
    }
);

Is this behavior expected? Am I doing something wrong?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.