guzzle-swoole_使用PHP-HTTP和HTTPlug从Guzzle5中脱颖而出

guzzle-swoole

This article was peer reviewed by Márk Sági-Kazár and David Buchmann. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

本文由MárkSági-KazárDavid Buchmann进行了同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!



In a previous series, we built a PHP client for Diffbot. The client works well and is in relatively widespread use – we even tested it on a live app to make sure it’s up to par – but it depends heavily on Guzzle 5.

在上一个系列中,我们为Diffbot构建了一个PHP客户端 。 该客户端运行良好并且得到了相对广泛的使用-我们甚至在实时应用中对其进行了测试,以确保其达到同等水平-但它在很大程度上取决于Guzzle 5。

There are two problems with this:

这有两个问题:

  1. Guzzle 6 is out, and supports PSR 7. While the author of Guzzle claims Guzzle 5 will be supported for the foreseeable future, it’s safer to be skeptical of its longevity. Besides, while PSR 7 might have its quirks, it’s good to follow PSRs if only for the compatibility with other projects

    食人鱼6已退出,并支持PSR 7 。 尽管Guzzle的作者声称Guzzle 5将在可预见的将来得到支持,但对其寿命的怀疑仍是较为安全的。 此外,虽然PSR 7可能有其古怪之处 ,但如果仅出于与其他项目的兼容性的考虑,最好遵循PSR。

  2. Someone implementing our client in their app might already have a preferred HTTP client in use, and would like to use theirs rather than Guzzle. We should allow for easy injection of any HTTP client into our SDK.

    在他们的应用中实现我们的客户端的人可能已经在使用首选的HTTP客户端,并且想要使用他们的而不是Guzzle。 我们应该允许将任何 HTTP客户端轻松注入到我们的SDK中。

Coincidentally, there is a new project allowing us to do just that: HTTPlug.

巧合的是,有一个新项目使我们能够做到这一点: HTTPlug

Travel power adapter

Note: you don’t need to be familiar with the internal logic of the Diffbot SDK to follow along. The process in this article is applicable to any package with a concrete HTTP Client implementation and is easy to follow.

注意:您无需先熟悉Diffbot SDK的内部逻辑。 本文中的过程适用于具有特定HTTP客户端实现的任何程序包,并且易于遵循。

PHP-HTTP和HTTPlug (PHP-HTTP and HTTPlug)

PHP-HTTP is a Github organization for HTTP related tools in PHP. It provides HTTPlug, a collection of interfaces and exceptions to define a minimal HTTP client contract on top of PSR-7 request and response. Implementations of this contract ​provide​ the virtual package php-http/client-implementation.

PHP-HTTP是Github组织,用于PHP中的HTTP相关工具。 它提供HTTPlug ,接口和异常的集合,以在PSR-7请求和响应之上定义最小的HTTP客户端协定。 本合同的实现提供虚拟包 php-http/client-implementation

This means someone who uses Guzzle 6 can composer require php-http/guzzle6-adapter to pull in the adapter, the HTTPlug interface package, and Guzzle 6 itself as a dependency of the adapter.

这意味着使用Guzzle 6的人可以composer require php-http/guzzle6-adaptercomposer require php-http/guzzle6-adapter引入适配器,HTTPlug接口包以及Guzzle 6本身作为适配器的依赖项。

HTTPlug is the entry point for a reusable package. It is the client abstraction all clients (like the Guzzle6 Adapter) are based on. These clients then further make use of their underlying packages / dependencies – Guzzle 6 in this case.

HTTPlug是可重用软件包的入口点。 它是所有客户端(例如Guzzle6适配器)所基于的客户端抽象。 然后,这些客户端进一步利用其基础包/依赖项-在这种情况下为Guzzle 6。

So, bottom to top:

因此,从下到上:

  • an HTTP Client exists (Guzzle 6)

    HTTP客户端存在(第6部分)
  • a Guzzle 6 adapter is built with HTTPlug as the interface for it, wraps Guzzle 6

    使用HTTPlug作为其接口构建的Guzzle 6适配器,包装了Guzzle 6
  • an app needing to be able to make HTTP calls needs a client, requires HTTPlug’s HttpClient interface rather than Guzzle 6 directly

    需要进行HTTP调用的应用需要一个客户端,需要HTTPlug的HttpClient接口,而不是直接使用Guzzle 6
  • the app can then use Guzzle 6, or any other adapter implementing HTTPlug’s HttpClient interface and wrapping another third party HTTP Client

    然后,该应用可以使用Guzzle 6或任何其他实现HTTPlug的HttpClient接口并包装另一个第三方HTTP Client的适配器

The team’s plan is to eventually have maximum support for all the various HTTP clients in PHP land: Guzzle 6, Guzzle 5, Zend2, Zend1, etc. That way, a user of a framework or app will have no conflicts with installed client versions, and will simply plug the appropriate adapter into the mix.

团队的计划是最终为PHP领域中的所有各种HTTP客户端(如Guzzle 6,Guzzle 5,Zend2,Zend1等)提供最大的支持。这样一来,框架或应用程序的用户将不会与已安装的客户端版本发生冲突,只需将适当的适配器插入混音中即可。

Note that we use the terms adapter and client here almost interchangeably – the adapters based on HTTPlug are both. They are wrappers around existing clients, but used directly as clients themselves.

请注意,我们在这里几乎可以互换地使用术语适配器客户端 –基于HTTPlug的适配器都是两者。 它们是现有客户的包装,但直接用作客户本身。

Our plan in this post is to replace the concrete Guzzle 5 dependency of Diffbot’s PHP client with the HTTPlug version.

我们在这篇文章中的计划是用HTTPlug版本替换DiffbotPHP客户端的具体Guzzle 5依赖关系。

Note: HTTPlug and related packages are alpha software, and as such are subject to change. Converting anything to use them is a risky endeavor.

注意:HTTPlug和相关软件包是alpha软件,因此可能会发生更改。 转换任何东西以使用它们是冒险的。

自举 (Bootstrapping)

As usual, it’s recommended we use Homestead Improved to bootstrap our environment. Once we’re ready, we can clone and test the current stable version of the SDK:

与往常一样,建议我们使用Homestead Improvementd引导环境。 准备好之后,我们可以克隆和测试SDK的当前稳定版本:

git clone https://github.com/swader/diffbot-php-client
cd diffbot-php-client 
git checkout tags/0.4.5 
composer install
phpunit

The last command assumes PHPUnit is globally installed on the development environment.

最后一个命令假定PHPUnit已全局安装在开发环境中。

All tests should pass (except for a skipped one that’s bugged and unfixable due to some nonsense), so we’re ready to begin the conversion.

所有测试都应通过(除了由于某些废话而导致错误和不可修复的跳过测试),因此我们准备开始转换。

入门 (Getting Started)

First, we’ll need to create a new branch on which to develop this upgrade.

首先,我们需要创建一个新分支来开发此升级。

git checkout -b feature-httplug

Then, we add two dependencies into our composer.json file:

然后,我们在composer.json文件中添加两个依赖项:

"require": {
        ...
        "php-http/client-implementation": "^1.0"
    },
    "require-dev": {
        ...
        "php-http/guzzle6-adapter": "~0.2@dev"
    },

What this does is tell the client that from now on, it depends on a virtual packagethis one. This means that in order to be used, the application using our Diffbot client (like this one) must select an implementation of this package (one of those listed at the link on Packagist). Of course, during development of the package, it would be impossible to test and see if everything’s working without an actual implementation, so we specify an additional require-dev dependency. In the specific case above, we use "php-http/guzzle6-adapter": "~0.2@dev". We chose that particular version simply because it’s the newest one and there’s no stable release.

这样做是告诉客户,从现在开始,它取决于一个虚拟软件包 - 这个 。 这意味着,要使用该应用程序,使用我们的Diffbot客户端的应用程序(如客户端程序) 必须选择该程序包的实现(Packagist链接上列出的程序包之一)。 当然,在开发包的过程中,如果没有实际的实现就不可能测试并查看一切是否正常,因此我们指定了一个额外的require-dev依赖项。 在上述特定情况下,我们使用"php-http/guzzle6-adapter": "~0.2@dev" 。 我们之所以选择该特定版本,是因为它是最新版本,并且没有稳定的版本。

Note: You may be wondering why we used the approach of adding values into composer.json rather than declaring dependencies interactively in the terminal like we usually do. This is because doing a composer require on a virtual package will throw an error – the package doesn’t really exist, it’s just its virtual name, a placeholder, so Composer will get confused not knowing what to install. There is an issue suggesting a change to this, but it’s not likely to happen soon.

注意:您可能想知道为什么我们使用在composer.json中添加值的方法,而不是像通常那样在终端中以交互方式声明依赖项。 这是因为在虚拟软件包上执行composer require会引发错误-软件包实际上并不存在,只是其虚拟名称(占位符),因此Composer不知道要安装什么会感到困惑。 一个问题建议对此进行更改,但不太可能很快发生。

Since the php-http packages are still under heavy development, we should add the following two values to our composer.json file:

由于php-http包仍在大量开发中,因此我们应该在composer.json文件中添加以下两个值:

"prefer-stable": true,
"minimum-stability": "dev"

This is to allow the installation of dev packages (non-stable), but will prefer stable versions if they exist. So rather than fetch, say, PHPUnit 5.2.x which is highly unstable, it will fetch 5.0.8 (most up to date at the time of writing), but it will also succeed if we ask it for packages that don’t have stable releases (like the guzzle6-adapter).

这是为了允许安装dev软件包(不稳定),但是如果存在稳定版本,则更喜欢稳定版本。 因此,与其获取(例如,高度不稳定PHPUnit 5.2.x),不如获取5.0.8(在编写本文时,它是最新的),但是如果我们要求它提供不包含以下内容的软件包,它也将成功。稳定的版本(例如guzzle6-adapter )。

We also need to remove the dependency on Guzzle5, if we intend to install Guzzle6. The final require blocks look like this:

如果要安装Guzzle6,还需要删除对Guzzle5的依赖。 最终的require块如下所示:

"require": {
        "php" : ">=5.4.0",
        "php-http/client-implementation": "^1.0"
    },
    "require-dev": {
        "symfony/var-dumper": "~2",
        "phpunit/phpunit": "^5.0",
        "php-http/guzzle6-adapter": "~0.2@dev"
    },

计划 (The Plan)

The way the SDK currently works is as follows: in the main Diffbot class, we optionally set an HTTPClient. This is currently bound to Guzzle’s implementation at version 5. If no custom client instance is set, the Diffbot class automatically uses a default client.

SDK当前的工作方式如下:在Diffbot类中 ,我们可以选择设置HTTPClient。 当前,这已绑定到版本5的Guzzle的实现。如果未设置自定义客户端实例,则Diffbot类将自动使用默认客户端。

This client is then used by the Api Abstract’s call method to issue a get request to the given URL. Additionally, there is a custom call method in the Crawl API class and the Search API class.

然后, Api Abstract的call方法使用此客户端向指定的URL发出get请求。 此外,在Crawl API类Search API类中还有一个自定义call方法。

The result of the call is saved as a $response, which is a Guzzle5 Response. That response is then additionally processed by the Entity Factory which checks its validity and builds entities from it, pushing them into Entity Iterator.

通话结果将保存为$response ,即Guzzle5响应。 然后,该响应由Entity Factory进行附加处理,该实体工厂检查其有效性并从中构建实体 ,并将其推入Entity Iterator

The plan is, thus, to:

因此,该计划是:

  1. Replace Diffbot::setHttpClient with a method accepting an HTTPlug implementation

    Diffbot::setHttpClient替换为接受HTTPlug实现的方法

  2. Modify the API abstract’s, Crawl’s, and Search class’ call methods so that they can issue a GET request with any HTTP client implementation provided to them.

    修改API摘要,Crawl和Search类的call方法,以便它们可以使用提供给他们的任何HTTP客户端实现来发出GET请求。

  3. Modify the Entity Factory and Entity Iterator so that they no longer depend on the Guzzle5 version of Response, but rather the PSR-7 counterpart.

    修改Entity Factory和Entity Iterator,使其不再依赖于Response的Guzzle5版本,而不再依赖于PSR-7。

The PHP-HTTP project has an additional package, Utils, which contains HttpMethodsClient. That class wraps a message factory and the HTTP client into one whole, making it easier to send requests with commonly used verbs like GET, POST, etc – thus translating into something similar to what we had so far: $client->get( ... ). What’s more, it also returns the PSR-7 ResponseInterface, which means the getBody method will be available to us – that’ll leave only the toJson method unimplemented, something we can easily do ourselves.

PHP-HTTP项目还有一个附加程序包Utils ,其中包含HttpMethodsClient 。 该类将消息工厂和HTTP客户端包装为一个整体,从而更容易发送带有GET,POST等常用动词的请求-从而翻译成类似于我们到目前为止的东西: $client->get( ... ) 。 而且,它还会返回PSR-7 ResponseInterface ,这意味着getBody方法将对我们可用–仅保留toJson方法未实现,而我们可以轻松地做到这一点。

Additionally, the project has a Discovery component, which features some static classes for discovering installed factories and clients – this allows us to provide our end user with a zero-configuration experience in some cases (see docs).

此外,该项目还有一个发现组件,该组件具有一些用于发现已安装的工厂和客户端的静态类-这使我们在某些情况下为最终用户提供零配置的体验(请参阅docs )。

With the battle plan laid out, we can get started with the refactoring.

制定好战斗计划后,我们就可以开始进行重构了。

先决条件 (Prerequisites)

Let’s require the additional packages:

我们需要其他软件包:

composer require "php-http/utils" "php-http/discovery"

Diffbot类 (Diffbot Class)

The Diffbot class has this line at the top:

Diffbot类的顶部具有以下行:

use GuzzleHttp\Client;

We can just change it to:

我们可以将其更改为:

use Http\Client\Utils\HttpMethodsClient as Client;

The setHttpClient method should flare up in the IDE now, saying it’s missing some required parameters, namely the client to use, and the message factory with which to build Request instances.

setHttpClient方法现在应该在IDE中出现,表示缺少一些必需的参数,即要使用的客户端以及用于构建Request实例的消息工厂

The method should be refactored into:

该方法应重构为:

/**
 * Sets the client to be used for querying the API endpoints
 *
 * @param Client $client
 * @see http://php-http.readthedocs.org/en/latest/utils/#httpmethodsclient
 * @return $this
 */
public function setHttpClient(Client $client = null)
{
    if ($client === null) {
		$client = new Client(
		   \Http\Discovery\HttpClientDiscovery::find(),
		   \Http\Discovery\MessageFactoryDiscovery::find()
		);
    }
    $this->client = $client;
    return $this;
}

Alternatively, the Discovery classes can be imported with use statements at the top of the class.

或者,可以use类顶部的use语句导入发现类。

This change has now allowed the end user of the Diffbot SDK to either:

现在,此更改使Diffbot SDK的最终用户可以:

  • have their own client installed and let the Discovery components in tandem with HttpMethodsClient take care of things automatically, or

    拥有自己的客户端,并让与HttpMethodsClient一起使用的Discovery组件自动处理事务,或者
  • configure their own HttpMethodsClient instance by injecting a custom instance of a PSR 7 Client and Message Factory into a new instance of it, and inject that into the setHttpClient method for full flexibility

    通过注入PSR 7客户端和邮件厂的自定义实例到它的一个新的实例配置自己HttpMethodsClient实例,并注入到这一点setHttpClient全灵活性的方法

Most users will use this on autopilot.

大多数用户会在自动驾驶仪上使用它。

Next up, the call methods.

接下来, call方法。

Response instance flagged as erroneous

Because the HttpMethodsClient instance we implemented before has a get method, there are no changes needed in that regard. The $response instance, however, shows a mistmatch, and with good reason. The original $response expected by the EntityFactory is a Guzzle5 Response.

因为我们之前实现的HttpMethodsClient实例具有get方法,所以在这方面不需要更改。 但是, $response实例显示出不匹配,并且有充分的理由。 EntityFactory期望的原始$response是Guzzle5响应。

Due to the complaint being issued by EntityFactory, we don’t really need to edit the API Abstract – it’ll take care of things on its own. The Crawl class’ call counterpart is a bit different:

由于是由EntityFactory发出的投诉,因此我们实际上不需要编辑API抽象-它会自行处理。 Crawl类的call对象有点不同:

public function call()
{
    $response = $this->diffbot->getHttpClient()->get($this->buildUrl());

    $array = $response->json();

    if (isset($array['jobs'])) {
        $jobs = [];
        foreach ($array['jobs'] as $job) {
            $jobs[] = new JobCrawl($job);
        }

        return new EntityIterator($jobs, $response);
    } elseif (!isset($array['jobs']) && isset($array['response'])) {
        return $array['response'];
    } else {
        throw new DiffbotException('It appears something went wrong.');
    }
}

Two warnings here – the second line of the method which uses the json method of $response, and the EntityIterator instantiation which expects a Guzzle5 Response. The only line we can affect from here is the former, so let’s change it to:

这里有两个警告-使用$responsejson方法的方法的第二行,以及需要Guzzle5响应的EntityIterator实例化。 我们唯一可以影响的是前者,因此我们将其更改为:

$array = json_decode($response->getBody(), true);

A similar change needs to be done in the Search class’ call method, where the line:

需要在Search类的call方法中进行类似的更改,其中该行:

$arr = $ei->getResponse()->json(['big_int_strings' => true]);

changes into:

更改为:

$arr = json_decode((string)$ei->getResponse()->getBody(), true, 512, 1);

实体工厂 (Entity Factory)

The EntityFactory class has the following import at the top:

EntityFactory类的顶部具有以下导入:

use GuzzleHttp\Message\ResponseInterface as Response;

We can change this to:

我们可以将其更改为:

use Psr\Http\Message\ResponseInterface as Response;

The same needs to be done in the EntityFactory interface which the EntityFactory class implements.

EntityFactory类实现的EntityFactory 接口中需要完成相同的操作。

The other change is similar to what we did above, in the Crawl class. We change:

另一个变化与我们在Crawl类中所做的类似。 我们改变:

$arr = $response->json(['big_int_strings' => true]);

to

$arr = json_decode($response->getBody(), true, 512, 1);

in both checkResponseFormat and createAppropriateIterator methods.

checkResponseFormatcreateAppropriateIterator方法中。

实体迭代器 (Entity Iterator)

We change:

我们改变:

use GuzzleHttp\Message\ResponseInterface as Response;

to

use Psr\Http\Message\ResponseInterface as Response;

测验 (Tests)

Mocking, the main way of testing HTTP requests and API calls, is different in Guzzle 6, so our tests need a slightly bigger overhaul.

模拟是测试HTTP请求和API调用的主要方式,在Guzzle 6中有所不同,因此我们的测试需要稍大一点的改进。

As this tutorial is already a bit on the long side, please see the relevant feature branch if you’re interested in learning the differences in mocking between Guzzle 5 and Guzzle 6 and, specifically, between the two versions of the Diffbot SDK.

由于本教程已经很长了,如果您想了解Guzzle 5和Guzzle 6之间,特别是Diffbot SDK的两个版本之间在模拟方面的差异,请参阅相关的功能分支

Finally, let’s run the tests:

最后,让我们运行测试:

phpunit

PHPUnit 5.0.8 by Sebastian Bergmann and contributors.

Runtime:       PHP 5.6.10-1+deb.sury.org~trusty+1 with Xdebug 2.3.2
Configuration: /home/vagrant/Code/diffbot-php-client/phpunit.xml.dist

...............................................................  63 / 347 ( 18%)
............................................................... 126 / 347 ( 36%)
............S.................................................. 189 / 347 ( 54%)
............................................................... 252 / 347 ( 72%)
............................................................... 315 / 347 ( 90%)
................................                                347 / 347 (100%)

Time: 55.78 seconds, Memory: 34.25Mb

Success! All passing (except the expected skipped test).

成功! 全部通过(预期的跳过测试除外)。

The Diffbot SDK is now not only PSR-7 compatible, but also receptive of other implementations of HTTP clients. All it needs is an adapter respecting the HTTPlug interface, and everything should work out of the box.

Diffbot SDK现在不仅与PSR-7兼容,而且可以接受HTTP客户端的其他实现。 它所需要的只是一个遵循HTTPlug接口的适配器,所有内容都可以直接使用。

结论 (Conclusion)

HTTPlug is a useful new approach to abstracting the HTTP client implementations in the apps we build. Whether we’re building HTTP clients ourselves or using them in other apps, PHP-HTTP provides a whole new world of extensibility for the reasonable price of one additional layer of abstraction.

HTTPlug是一种有用的新方法,用于在我们构建的应用程序中抽象HTTP客户端实现。 无论是我们自己构建HTTP客户端还是在其他应用程序中使用它们,PHP-HTTP都以一个合理的价格(额外的抽象层)提供了一个全新的可扩展性世界。

If you’d like to help out by adding more adapter implementations, or just by trying the packages out and giving feedback, the team welcomes all contributions. Get in touch, or leave your feedback in the comments section below, and if you found this tutorial interesting, don’t forget to hit that like button!

如果您想通过添加更多适配器实现或仅通过试用软件包并提供反馈来提供帮助,团队欢迎您的贡献。 保持联系,或在下面的评论部分中留下您的反馈,如果您觉得本教程很有趣,请不要忘记单击“赞”按钮!

翻译自: https://www.sitepoint.com/breaking-free-from-guzzle5-with-php-http-and-httplug/

guzzle-swoole

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值