I've been working on, what should be for most users, an almost entirely backwards compatible update to Guzzle which adds the following enhancements:

1. Asynchronous future responses and makes it easier to create new adapters (e.g., curl, stream wrapper, sockets, etc). The async support works in a completely backwards compatible way to the existing synchronous workflow of Guzzle, but now provides the ability to use Guzzle with non-blocking libraries (e.g., React).
2. Compatible with the latest PSR-7 updates. If the PSR-7 proposal remains mostly unchanged, then Guzzle can later extend from the PSR-7 message interfaces in a backwards compatible way.
3. Added a new terminal "end" event to requests that is only emitted once per request, making it easier to work with only the end-result of a message (complete and error can be emitted N number of times).

This updated version of Guzzle uses a new project to manage the adapter layer to make it easier to implement custom adapters without having to know anything about the internals of Guzzle (see: http://mtdowling.com/blog/2014/09/28/guzzle-ring/).

A full list of changes can be found here: https://github.com/guzzle/guzzle/blob/ring/CHANGELOG.md#500-tbd. As you can see, for most users, this update should be a no-op.

I was hoping that if I tagged version 5 in the next few days that Drupal 8 could update to use it. The reason I think it's important that Drupal updates to this new version is that we are working on a new version of the AWS SDK for PHP that will rely on Guzzle ~5. This new version is something Drupal users are going to want to use over the previous version because it provides significant improvements is also mostly backwards compatible to its previous version. I can submit a patch to Goutte to update to version 5 (it should just be a matter of updating the composer.json file).

Please let me know what I can do to help make this happen.

Comments

mtdowling’s picture

Issue summary: View changes
mtdowling’s picture

Issue summary: View changes

Added mention of "end" event.

berdir’s picture

Wow, I have a feeling that you will be at Guzzle 17 before we can release Drupal 8 ;)

mtdowling’s picture

I know, my bad :). This is a pretty minor update in terms of breaking interfaces, but provides a lot of features. The good news is that we're shipping an updated AWS SDK soon, so that will effectively prevent me from shipping any more breaking changes in Guzzle :)

andypost’s picture

Is there a way to provide guzzle as versionable library?
Why core depends on interfaces from guzzle internals?

andypost’s picture

Is there a way to provide guzzle as versionable library?
Why core depends on interfaces from guzzle internals?

catch’s picture

Category: Feature request » Task

Depending on the bc breaks we might not be able to do this, but switching to task since we need to sort it out before release.

mtdowling’s picture

I ran the unit tests under core/ and didn't get any Guzzle related failures. I did get a Pecl UUID failure, but it's unrelated:

1) Drupal\Tests\Component\Uuid\UuidTest::testGenerateUuid with data set #1 (Drupal\Component\Uuid\Pecl)
UUID generation for Drupal\Component\Uuid\Pecl works.
Failed asserting that false is true.

/Users/dowling/projects/drupal/core/tests/Drupal/Tests/Component/Uuid/UuidTest.php:31

I grepped for all instances of Guzzle in Core. I've gone through each usage, line by line, to see if they require any changes. They don't, but I found a couple things that I wanted to mention. The things I'm mentioning don't require changes, but I wanted to point them out. Here are the ue

.//core/includes/install.core.inc
> no need to change

.//core/lib/Drupal/Core/Http/Client.php
- No change needed.
- Note: it's still backwards compatible, but could be updated considering the following: Guzzle 5 now uses the CA bundle of the system by default, so defining "verify" as true is now the default.

.//core/lib/Drupal/Core/Test/EventSubscriber/HttpRequestSubscriber.php
- No change

.//core/lib/Drupal.php: * @return \GuzzleHttp\ClientInterface
- No change

.//core/modules/aggregator/src/Form/OpmlFeedAdd.php
- No change

.//core/modules/aggregator/src/Plugin/aggregator/fetcher/DefaultFetcher.php
- No change

.//core/modules/hal/src/Normalizer/FileEntityNormalizer.php
- No change needed.
- Note: (line 67) uses "$file_data = $this->httpClient->get($data['uri'][0]['value'])->getBody(TRUE);". This is a change from Guzzle 3 to 4 that was missed. You need to cast the body as a string instead of passing (true) to the getBody() method. It works fine because the body, when used as as string, is cast to a string.

.//core/modules/locale/locale.batch.inc
- No change

.//core/modules/statistics/src/Tests/StatisticsAdminTest.php
- No change

.//core/modules/statistics/src/Tests/StatisticsLoggingTest.php
- No change

.//core/modules/system/core.api.php
- No change

.//core/modules/system/system.module
- No change

.//core/modules/update/src/UpdateFetcher.php
- No change

----------

Are there other places I should look to figure out the impact of this update? How do I run the simpletest tests?

larowlan’s picture

@mtdowling you can use core/scripts/run-tests.sh to run simpletest, but simplest way is to upload a patch here.

attached patch to see what breaks

larowlan’s picture

Status: Active » Needs review
larowlan’s picture

note this patch is NOT FOR COMMIT, just for review.
Obviously we don't want dev-ring and minimum stability dev.

Status: Needs review » Needs work

The last submitted patch, 9: guzzle-5.with-vendor.patch, failed testing.

larowlan’s picture

so 75 fails

mtdowling’s picture

Did any of those failures fail before applying the patch? I guess I'm not sure how this change is related to some of the failures. In fact, it looks like SimpleTest is using curl directly in some places that look like they failed in this update.

larowlan’s picture

@mtdowling no - HEAD is green (https://qa.drupal.org/8.0.x-status)

The fails relate to the update status module, which uses Guzzle to fetch json or xml from drupal.org and report security updates (not sure that the test actually does this, but that is what the module does).

The fails are 'cannot serialize closure'. So I'm guessing that is something to do with the Queue entries that are created to fetch each module update in the background. These are objects that are serialized into the {queue} table with the default queue backend.

larowlan’s picture

Status: Needs work » Needs review
StatusFileSize
new673 bytes
new1.05 MB
larowlan’s picture

StatusFileSize
new6.67 KB
larowlan’s picture

+++ b/core/modules/update/src/UpdateFetcher.php
@@ -8,6 +8,7 @@
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;

@@ -16,6 +17,8 @@
+  use DependencySerializationTrait;
+

So this is the only core change required to support the current state of the ring branch of Guzzle (which I assume to be Guzzle 5 once merged)

Status: Needs review » Needs work

The last submitted patch, 16: guzzle-5.2.patch, failed testing.

larowlan’s picture

Status: Needs work » Needs review
StatusFileSize
new7.34 KB
new683 bytes
new1.05 MB

And we need to cast the getBody from a stream to a string

andypost’s picture

+++ b/core/modules/update/src/UpdateProcessor.php
@@ -161,7 +161,7 @@ public function processFetchTask($project) {
-      $data = $this->updateFetcher->fetchProjectData($project, $site_key);
+      $data = (string) $this->updateFetcher->fetchProjectData($project, $site_key);

That's strange... I think there should be a clean way (reusable) - fetchProjectData() should care about data and its type

larowlan’s picture

The difference is upstream, getBody returns a guzzle stream object which can be cast to a string.
In the current version it returns differently for empty - you need to cast it to a string before checking if empty.

mtdowling’s picture

Guzzle 5 has now been tagged.

larowlan’s picture

StatusFileSize
new12.09 KB
new1.4 MB

our head composer.json is invalid :(

composer self-update
composer validate
./composer.json is invalid, the following errors/warnings were found:
require.php : invalid version constraint (Could not parse version constraint >5.4.4-13: Invalid version string "5.4.4-13")
require.symfony/yaml : unbound version constraints (dev-master#089129be9f9828f41f0f345e09ff322ecf92cfb2) should be avoided
require.doctrine/common : unbound version constraints (dev-master#a45d110f71c323e29f41eb0696fa230e3fa1b1b5) should be avoided
require.phpunit/phpunit-mock-objects : unbound version constraints (dev-master#e60bb929c50ae4237aaf680a4f6773f4ee17f0a2) should be avoided

So assume we meant 5.4.13?

andypost’s picture

Status: Needs review » Needs work

The last submitted patch, 24: guzzle-5.tagged.patch, failed testing.

larowlan’s picture

Status: Needs work » Needs review
StatusFileSize
new11.19 KB
new1.19 MB

Missed updates to guzzlehttp/stream too
This should be pretty close

dawehner’s picture

  1. +++ b/composer.json
    @@ -4,7 +4,7 @@
    -    "php": ">5.4.4-13",
    +    "php": ">5.4.13",
    

    haha, nope :)

  2. +++ b/composer.json
    @@ -30,6 +30,7 @@
       },
    +  "minimum-stability": "dev",
       "autoload": {
    

    Do we really need this? Won't that bite us in the future when people do other updates in other packages?

  3. +++ b/composer.lock
    @@ -2498,9 +2578,7 @@
    diff --git a/core/includes/theme.inc b/core/includes/theme.inc
    
    diff --git a/core/includes/theme.inc b/core/includes/theme.inc
    index 1015f67..e8b9bde 100644
    
    index 1015f67..e8b9bde 100644
    --- a/core/includes/theme.inc
    
    --- a/core/includes/theme.inc
    +++ b/core/includes/theme.inc
    
    +++ b/core/includes/theme.inc
    +++ b/core/includes/theme.inc
    @@ -1492,6 +1492,7 @@ function template_preprocess_feed_icon(&$variables) {
    
    @@ -1492,6 +1492,7 @@ function template_preprocess_feed_icon(&$variables) {
         '#height' => 16,
         '#alt' => $text,
       );
    +  $variables['attributes']['class'] = array('feed-icon');
       // Stripping tags because that's what l() used to do.
       $variables['attributes']['title'] = strip_tags($text);
     }
    diff --git a/core/modules/update/src/UpdateFetcher.php b/core/modules/update/src/UpdateFetcher.php
    
    diff --git a/core/modules/update/src/UpdateFetcher.php b/core/modules/update/src/UpdateFetcher.php
    index 14bdb29..5f22265 100644
    
    index 14bdb29..5f22265 100644
    --- a/core/modules/update/src/UpdateFetcher.php
    
    --- a/core/modules/update/src/UpdateFetcher.php
    +++ b/core/modules/update/src/UpdateFetcher.php
    
    +++ b/core/modules/update/src/UpdateFetcher.php
    +++ b/core/modules/update/src/UpdateFetcher.php
    @@ -8,6 +8,7 @@
    
    @@ -8,6 +8,7 @@
     namespace Drupal\update;
     
     use Drupal\Core\Config\ConfigFactoryInterface;
    +use Drupal\Core\DependencyInjection\DependencySerializationTrait;
     use GuzzleHttp\ClientInterface;
     use GuzzleHttp\Exception\RequestException;
     
    @@ -16,6 +17,8 @@
    
    @@ -16,6 +17,8 @@
      */
     class UpdateFetcher implements UpdateFetcherInterface {
     
    +  use DependencySerializationTrait;
    +
       /**
        * URL to check for updates, if a given project doesn't define its own.
        */
    diff --git a/core/modules/update/src/UpdateProcessor.php b/core/modules/update/src/UpdateProcessor.php
    
    diff --git a/core/modules/update/src/UpdateProcessor.php b/core/modules/update/src/UpdateProcessor.php
    index d37f643..f129845 100644
    
    index d37f643..f129845 100644
    --- a/core/modules/update/src/UpdateProcessor.php
    
    --- a/core/modules/update/src/UpdateProcessor.php
    +++ b/core/modules/update/src/UpdateProcessor.php
    
    +++ b/core/modules/update/src/UpdateProcessor.php
    +++ b/core/modules/update/src/UpdateProcessor.php
    @@ -161,7 +161,7 @@ public function processFetchTask($project) {
    
    @@ -161,7 +161,7 @@ public function processFetchTask($project) {
         $project_name = $project['name'];
     
         if (empty($this->failed[$fetch_url_base]) || $this->failed[$fetch_url_base] < $max_fetch_attempts) {
    -      $data = $this->updateFetcher->fetchProjectData($project, $site_key);
    +      $data = (string) $this->updateFetcher->fetchProjectData($project, $site_key);
         }
         if (!empty($data)) {
           $available = $this->parseXml($data);
    

    Seems totally unrelated :)

tstoeckler’s picture

We can do "prefer-stable": true then the "minimum-stability" shouldn't hurt.

larowlan’s picture

1 and 2 will fix. 3 some are needed, some aren't

larowlan’s picture

StatusFileSize
new6.02 KB
new1.21 MB
new10.33 KB

Fixes #28 for explanation of changes to core/modules/update/src/UpdateFetcher.php core/modules/update/src/UpdateProcessor.php see fails/comments above.

I think this is ready

Berdir queued 31: guzzle-5.31.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 31: guzzle-5.31.patch, failed testing.

larowlan’s picture

Status: Needs work » Needs review
StatusFileSize
new1.21 MB

re-roll

berdir’s picture

Status: Needs review » Needs work
+++ b/core/modules/update/src/UpdateProcessor.php
@@ -161,7 +161,7 @@ public function processFetchTask($project) {
 
     if (empty($this->failed[$fetch_url_base]) || $this->failed[$fetch_url_base] < $max_fetch_attempts) {
-      $data = $this->updateFetcher->fetchProjectData($project, $site_key);
+      $data = (string) $this->updateFetcher->fetchProjectData($project, $site_key);

I think this should be casted to a string inside UpdateFetcher::fetchProjectData(). The documentation says it returns a string and the API there is clearly wrong (there is no TRUE argument anymore to getBody()).

larowlan’s picture

good catch

larowlan’s picture

Status: Needs work » Needs review
StatusFileSize
new1.34 KB
new1.23 MB
jibran’s picture

Status: Needs review » Reviewed & tested by the community

Everything is addressed so RTBC.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.0.x, thanks!

  • catch committed fa9b7ac on 8.0.x
    Issue #2348365 by larowlan | mtdowling: Update to Guzzle 5.
    

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

kim.pepper’s picture

Added follow up #2401159: Convert instances of Guzzle getBody(TRUE) for converting instances of getBody(TRUE)

jhedstrom’s picture

Added follow-up #2442411: Update react/promise to 2.2.0 (related only in that the library was introduced by Guzzle 5).