- 后端
- 电商
- 前端
【免费下载链接】CRMEB
🔥CRMEB开源商城免费开源多语言商城系统,Tp6框架商城,系统可商用;包含小程序商城、H5商城、公众号商城、PC商城、App,支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面DIY,前后端分离,方便二开,更有详细使用文档、接口文档、数据字典、二开文档/视频教程,欢迎大家提出宝贵意见和建议
本指南以 Guzzle v3(
guzzle/guzzle 3.9.3)官方文档为骨架,完整讲解 HTTP 客户端的三大核心组成(Client / Request / Response)、Base URL 合并规则、全部请求选项(Request Options)以及并行请求与插件事件机制,并结合 CRMEB 商城源码展示它在对象存储上传、第三方云 SDK 依赖中的真实应用场景。读完本文,你将能熟练使用 Guzzle v3 构造任意 HTTP 请求、配置认证与代理、控制超时与 SSL 校验,并理解它在 CRMEB 项目中的落地位置。
一、Guzzle HTTP Client 是什么:三大核心部件
Guzzle 让 PHP 开发者能够完全掌控 HTTP 请求,同时遵循 HTTP/1.1 的最佳实践。它的 HTTP 功能是构建在 PHP libcurl 绑定之上的健壮框架。其核心由三部分组成(对应文档 crmeb/vendor/guzzle/guzzle/docs/http-client/client.rst):
| 组成 | 类 | 职责 |
|---|---|---|
| Clients | Guzzle\Http\Client | 创建并发送请求,将响应关联到请求上 |
| Requests | Guzzle\Http\Message\Request | 无请求体的请求 |
| Requests | Guzzle\Http\Message\EntityEnclosingRequest | 携带请求体的请求 |
| Responses | Guzzle\Http\Message\Response | 服务端返回的响应对象 |
Client 对象负责三件事:创建请求、发送请求、把响应设置到请求对象上。所有请求都由请求工厂(Guzzle\Http\Message\RequestFactoryInterface)创建,默认工厂通常够用,你也可以通过$client->setRequestFactory()注入自定义工厂。
二、创建客户端与 Base URL 合并规则
2.1 基本用法
实例化 Client 时,可以传入一个可选的 base URL(本质是一个 URI 模板)和一个可选配置数组。使用相对 URL 创建请求时,base URL 会自动与请求 URL 合并:
use Guzzle\Http\Client; // 创建客户端并提供 base URL $client = new Client('https://api.github.com'); $request = $client->get('/user'); $request->setAuth('user', 'pass'); echo $request->getUrl(); // >>> https://api.github.com/user // 必须显式发送请求才会真正发起传输 $response = $request->send(); echo $response->getBody(); // >>> {"type":"User", ... echo $response->getHeader('Content-Length'); // >>> 792 $data = $response->json(); echo $data['type']; // >>> User2.2 Base URL 与相对 URL 的合并规则
Guzzle 遵循 RFC 3986 的 5.2 节来合并 base URL 与相对 URL。注意上文例子中传给get()的是/user(带前导斜杠),它是"绝对路径"形式的相对 URL,因此合并后实际得到https://api.github.com/user。具体规则如下:
- 绝对路径(带前导斜杠,如
/path/to/something):会覆盖 base URL 中的既有路径,同时相对 URL 携带的查询字符串会替换 base URL 的查询字符串。 - 相对路径(不带前导斜杠):会追加到 base URL 路径之后。例如 base URL 为
https://api.twitter.com/v1.1,用statuses/user_timeline.json创建 GET 请求,得到https://api.twitter.com/v1.1/statuses/user_timeline.json。如果同时提供了查询字符串,则相对路径追加到 base URL 路径,查询参数合并进 base URL 的查询字符串。 - 绝对 URL(如
http://httpbin.org/ip):请求完全按该 URL 原样发送,base URL 的任何组成部分都不会参与合并。
从源码看,Client 内部将 baseUrl 解析为Guzzle\Http\Url对象,在 Client.php 的createRequest()中完成合并逻辑,你可以直接在源码中验证上述三种合并分支。
三、客户端配置选项(Configuration Options)
构造函数第二个参数是配置数组,可以包含 URI 模板变量或改变客户端行为的特殊选项:
| 配置项 | 说明 |
|---|---|
request.options | 关联数组,作为每个请求的默认 Request options,作用于客户端创建的所有请求 |
redirect.disable | 禁用客户端创建的每个请求的 HTTP 重定向跟随 |
curl.options | 关联数组,作为每个请求的 cURL 选项;若某项的键或值是字符串,Guzzle 会自动匹配对应的 cURL 常量(如字符串"CURLOPT_PROXY"会被转换为常量CURLOPT_PROXY) |
ssl.certificate_authority | 值为true时使用 Guzzle 内置的 SSL 证书包(默认行为);'system'使用系统证书包;字符串指向某个证书文件;字符串指向某目录则使用其中多个证书;false则关闭 SSL 校验(不推荐)。当 Guzzle 运行在 phar 中时,内置证书会被解压到系统临时目录,每次创建客户端都会做 MD5 完整性校验 |
command.params | 使用Guzzle\Service\Client时,作为每个命令默认选项的关联数组 |
3.1 综合配置示例
下面的示例设置了默认请求头、默认查询参数、默认认证方案和默认代理。配置数组中的变量还可以注入到 base URL 中(即 URI 模板):
use Guzzle\Http\Client; $client = new Client('https://api.twitter.com/{version}', array( 'version' => 'v1.1', 'request.options' => array( 'headers' => array('Foo' => 'Bar'), 'query' => array('testing' => '123'), 'auth' => array('username', 'password', 'Basic|Digest|NTLM|Any'), 'proxy' => 'tcp://localhost:80' ) ));3.2 自定义 User-Agent
默认 User-Agent 格式为Guzzle/<Guzzle_Version> curl/<curl_version> PHP/<PHP_VERSION>,可通过setUserAgent()定制:
// 完全覆盖默认 User-Agent $client->setUserAgent('Test/123'); // 在默认 User-Agent 前追加字符串 $client->setUserAgent('Test/123', true);该方法的签名在 Client.php 中为setUserAgent($userAgent, $includeDefault = false),第二个参数控制是否保留默认串。
四、使用客户端创建各类请求
Client 暴露了多种创建 Request 对象的方法:
$client->createRequest($method, $uri, array $headers, $body, $options)— 创建任意自定义 HTTP 请求$client->get($uri, array $headers, $options)— 创建 GET 请求$client->head($uri, array $headers, $options)— 创建 HEAD 请求$client->delete($uri, array $headers, $body, $options)— 创建 DELETE 请求$client->post($uri, array $headers, $postBody, $options)— 创建 POST 请求$client->put($uri, array $headers, $body, $options)— 创建 PUT 请求$client->patch($uri, array $headers, $body, $options)— 创建 PATCH 请求
use Guzzle\Http\Client; $client = new Client('http://baseurl.com/api/v1'); // 相对 URL(相对路径追加到 base URL 路径) // 最终 URL: http://baseurl.com/api/v1/path?query=123&value=abc $request = $client->get('path?query=123&value=abc'); $response = $request->send(); // 带绝对路径的相对 URL(覆盖 base URL 的路径部分) // 最终 URL: http://baseurl.com/path?query=123&value=abc $request = $client->head('/path?query=123&value=abc'); $response = $request->send(); // 绝对 URL,完全按原样发送 $request = $client->delete('http://www.example.com/path?query=123&value=abc'); $response = $request->send(); // PUT:以 PHP 流的内容作为请求体,并指定自定义请求头 $request = $client->put('http://www.example.com/upload', array( 'X-Header' => 'My Header' ), fopen('http://www.test.com/', 'r')); $response = $request->send(); // POST:手动追加文件 $request = $client->post('http://localhost:8983/solr/update') ->addPostFiles(array('file' => '/path/to/documents.xml')); $response = $request->send(); // 探测资源是否允许 DELETE 方法 $supportsDelete = $client->options('/path')->send()->isMethodAllowed('DELETE');这些便捷方法在 Client.php 中均有对应实现,内部统一调用createRequest()。
五、静态客户端(StaticClient)
Guzzle 还提供了静态门面,可以更方便地发送简单 HTTP 请求,无需实例化客户端:
// 挂载静态客户端,之后即可通过 \Guzzle 访问 Guzzle\Http\StaticClient::mount(); $response = Guzzle::get('http://guzzlephp.org');静态客户端的每个请求方法(get()、post()、put()等)都接受一个请求选项关联数组:
$response = Guzzle::post('http://test.com', array( 'headers' => array('X-Foo' => 'Bar'), 'body' => array('Test' => '123'), 'timeout' => 10 ));从 StaticClient.php 源码看,mount($className = 'Guzzle', ClientInterface $client = null)通过class_alias注册别名,request()内部惰性创建new Client()并调用createRequest($method, $url, null, null, $options)。
六、请求选项(Request Options)全解析
请求选项可以在创建请求时指定,也可以在客户端的request.options中配置为默认值。它可以控制请求头、查询字符串、响应下载位置、代理、认证等各个方面。
6.1 headers
应用到请求的请求头关联数组。当在创建方法的$options参数中指定时,$options中的 headers 会覆盖$headers参数中同名的头部:
$request = $client->get($url, array(), array( 'headers' => array('X-Foo' => 'Bar') ));也可以在客户端上配置默认请求头:
$client = new Guzzle\Http\Client(); // 用路径语法设置单个请求头 $client->setDefaultOption('headers/X-Foo', 'Bar'); // 设置全部请求头 $client->setDefaultOption('headers', array('X-Foo' => 'Bar'));此外,任何客户端默认请求选项都可以通过配置对象设置:
$client->getConfig()->setPath('request.options/headers/X-Foo', 'Bar');6.2 query
请求的查询字符串参数关联数组。在$options中指定时,会覆盖$url中的同名查询参数:
$request = $client->get($url, array(), array( 'query' => array('abc' => '123') ));也可在客户端上配置默认查询参数:
$client = new Guzzle\Http\Client(); // 路径语法设置单个查询参数 $client->setDefaultOption('query/abc', '123'); // 设置一组默认查询参数 $client->setDefaultOption('query', array('abc' => '123'));6.3 body
设置请求体。body选项的值可以是Guzzle\Http\EntityBodyInterface、字符串、fopen资源,或发送 POST 时的数组。一旦指定body选项,它会覆盖创建方法中的$body参数。
6.4 auth
指定 HTTP 认证参数的数组。数组第 0 位是用户名,第 1 位是密码,第 2 位可选认证类型。可选类型为:"Basic"(默认)、"Digest"、"NTLM"、"Any":
$request = $client->get($url, array(), array( 'auth' => array('username', 'password', 'Digest') )); // 让客户端每个请求都携带认证头 $client->setDefaultOption('auth', array('username', 'password', 'Digest'));6.5 cookies
指定要添加到请求中的 cookie 关联数组。
6.6 allow_redirects
指定请求是否跟随重定向。默认跟随;设为false则禁用(等价于配置项redirect.disable,内部对应Guzzle\Http\RedirectPlugin::DISABLE常量)。
6.7 save_to
指定响应体下载到何处,可以传文件路径、fopen资源或Guzzle\Http\EntityBodyInterface对象。
6.8 events
events选项让挂载事件监听器变得简单,它必须是"事件名 → Closure(或包含 Closure 与优先级的数组)"的关联数组:
$request = $client->get($url, array(), array( 'events' => array( 'request.before_send' => function (\Guzzle\Common\Event $e) { echo 'About to send ' . $e['request']; } ) )); // 静态客户端用法 Guzzle::get($url, array( 'events' => array( 'request.before_send' => function (\Guzzle\Common\Event $e) { echo 'About to send ' . $e['request']; } ) ));6.9 plugins
plugins选项让你为请求挂载一组插件:
Guzzle::get($url, array( 'plugins' => array( new Guzzle\Plugin\Cache\CachePlugin(), new Guzzle\Plugin\Cookie\CookiePlugin() ) ));6.10 exceptions
exceptions选项用于在非成功的 HTTP 响应码(如 404、500 等)下禁用异常抛出。设为false则不抛异常。
6.11 params
params选项用于向请求添加数据参数关联数组。注意:这不是查询字符串参数。
6.12 timeout / connect_timeout
timeout:整个传输允许的最长秒数,超时即中断。connect_timeout:连接等待的最长秒数。- 两者设为
0表示无限期等待。
$request = $client->get('http://www.example.com', array(), array( 'timeout' => 20, 'connect_timeout' => 1.5 ));6.13 verify
true启用 SSL 证书校验(默认);false禁用;或者传入 CA bundle 路径,使用自定义证书进行校验。
6.14 cert
cert指定 PEM 格式的 SSL 客户端证书(用于要求双向认证的服务器)。如果证书需要密码,用数组且密码作为第二个元素。通常与ssl_key配合使用:
$request = $client->get('https://www.example.com', array(), array( 'cert' => '/etc/pki/client_certificate.pem' )); $request = $client->get('https://www.example.com', array(), array( 'cert' => array('/etc/pki/client_certificate.pem', 's3cr3tp455w0rd') ));6.15 ssl_key
ssl_key指定 PEM 格式私钥文件,可带密码保护。密码属于敏感信息,请妥善保管包含它的 PHP 脚本。通常与cert配合使用:
$request = $client->get('https://www.example.com', array(), array( 'ssl_key' => '/etc/pki/private_key.pem' )); $request = $client->get('https://www.example.com', array(), array( 'ssl_key' => array('/etc/pki/private_key.pem', 's3cr3tp455w0rd') ));6.16 proxy
proxy用于指定 HTTP 代理,格式如http://username:password@192.168.16.1:10。
6.17 debug
debug用于显示一次传输的详细 cURL 输出。
6.18 stream
使用静态客户端时,stream设为true会返回一个Guzzle\Stream\Stream对象,可以按需从流中拉取数据(而不是让 cURL 一次性把整个响应下载到内存):
$stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); while (!$stream->feof()) { echo $stream->readLine(); }七、发送请求:串行与并行
7.1 send() 发送单个请求
请求可以通过 Request 对象的send()发送,也可以通过 Client 的send()发送:
$request = $client->get('http://www.amazon.com'); $response = $client->send($request);7.2 并行发送(Parallel Requests)
Client 的send()既可以接受单个Guzzle\Http\Message\RequestInterface对象,也可以接受一个 RequestInterface 对象数组——传入数组时,请求将被并行发送。
串行发送大量 HTTP 请求会因"前一个请求必须完成后才能发起下一个"而造成脚本无谓的延迟;并行发送则让整个请求池以其中最慢请求的速度完成,大幅缩短多个 HTTP 请求的总耗时。Guzzle 对 PHP 的 curl_multi 函数提供了封装。
并行发送三个请求的示例:
use Guzzle\Common\Exception\MultiTransferException; try { $responses = $client->send(array( $client->get('http://www.google.com/'), $client->head('http://www.google.com/'), $client->get('https://www.github.com/') )); } catch (MultiTransferException $e) { echo "The following exceptions were encountered:\n"; foreach ($e as $exception) { echo $exception->getMessage() . "\n"; } echo "The following requests failed:\n"; foreach ($e->getFailedRequests() as $request) { echo $request . "\n\n"; } echo "The following requests succeeded:\n"; foreach ($e->getSuccessfulRequests() as $request) { echo $request . "\n\n"; } }成功时返回一组Guzzle\Http\Message\Response对象。单个请求失败不会导致整个请求池失败——传输过程中抛出的所有异常会被聚合成Guzzle\Common\Exception\MultiTransferException。对应实现为 Client.php 的sendMultiple(),其内部使用CurlMultiProxy(默认 MAX_HANDLES = 3,即最多同时打开 3 个 cURL 句柄)。
八、插件与事件系统
Guzzle 提供了易用的请求插件,通过基于 Symfony2 Event Dispatcher 的"信号槽"事件通知为请求添加行为。任何挂载到 Client 对象上的事件监听器或订阅者,都会自动挂载到该客户端创建的每一个请求上。
8.1 让每个请求共享同一 cookie 会话
将Guzzle\Plugin\Cookie\CookiePlugin挂到客户端,即可让该客户端创建的每个请求都支持 cookie,且共用同一个 cookie 会话:
use Guzzle\Plugin\Cookie\CookiePlugin; use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; // 创建 cookie 插件 $cookiePlugin = new CookiePlugin(new ArrayCookieJar()); // 将插件作为订阅者挂到客户端 $client->addSubscriber($cookiePlugin);8.2 客户端发出的事件
Guzzle\Http\Client对象发出如下事件:
| 事件名 | 说明 | 事件数据 |
|---|---|---|
client.create_request | 客户端创建请求时触发 | client:客户端对象;request:已创建的请求 |
use Guzzle\Common\Event; use Guzzle\Http\Client; $client = new Client(); // 监听请求创建,打印出来 $client->getEventDispatcher()->addListener('client.create_request', function (Event $e) { echo 'Client object: ' . spl_object_hash($e['client']) . "\n"; echo "Request object: {$e['request']}\n"; });九、CRMEB 项目中的真实落地
9.1 Guzzle v3 的引入来源
CRMEB 的 crmeb/composer.lock 中锁定了guzzle/guzzlev3.9.3,它并非由 CRMEB 直接声明,而是由腾讯云对象存储 SDKqcloud/cos-sdk-v5间接依赖(require"guzzle/guzzle": "~3.7")带入的。此外 crmeb/composer.json 还直接声明了guzzlehttp/guzzle: 6.5.5与guzzlehttp/psr7: ^1.7,因此 CRMEB 中同时存在 v3 与 v6 两代 Guzzle 代码路径,属于依赖生态的典型叠加。
9.2 EntityBody 在文件流上传中的使用
文档提到的Guzzle\Http\EntityBodyInterface在 CRMEB 上传组件中有直接应用。以阿里云 OSS 存储实现 crmeb/crmeb/services/upload/storage/Oss.php 为例,stream()方法在上传前把文件内容包装为 Guzzle 的 EntityBody:
use Guzzle\Http\EntityBody; // ... $fileContent = (string)EntityBody::factory($fileContent); $uploadInfo = $this->app()->putObject($this->storageName, $key, $fileContent);同样地,京东云存储 crmeb/crmeb/services/upload/storage/Jdoss.php 也通过EntityBody::factory()将上传内容转换为统一实体(对应文档中body选项支持Guzzle\Http\EntityBodyInterface值的说明)。
9.3 与 Guzzle v6 / PSR-7 的共存
CRMEB 的云存储模块在另一些实现中则使用新一代 PSR-7 工具类:腾讯云 COS(Cos.php)、华为云 OBS(Obs.php)、火山引擎 TOS(Tyoss.php)都use GuzzleHttp\Psr7\Utils;,微信 OAuth 服务(WechatOauth.php)也使用GuzzleHttp\Psr7\Uri处理 URI。这两套命名空间可以共存,正是 Guzzle 三代架构设计(v3 基于Guzzle\Http\*自定义消息体系、v6 起全面拥抱 PSR-7)在真实项目中的体现。
9.4 对照:CRMEB 自研的 curl 封装
值得注意的是,CRMEB 核心业务代码并未直接依赖 Guzzle 发送常规 HTTP 请求,而是封装了自己的 crmeb/crmeb/services/HttpService.php(基于原生 curl),提供getRequest()、postRequest()、request()等静态方法,并统一处理超时、HTTPS 证书校验、header 输出与http_code判定。这也解释了 Guzzle 在 CRMEB 中主要作为"第三方云 SDK 的传输层依赖"而非"项目自有 HTTP 客户端"的定位——你可以对照本文的timeout/verify/proxy等选项,理解这些概念在 curl 参数(CURLOPT_TIMEOUT、CURLOPT_SSL_VERIFYPEER等)中的等价映射。
十、小结与注意事项
- Guzzle v3 的核心工作流是"创建 Client → 用便捷方法创建 Request →
send()发送 → 解析 Response",Base URL 合并遵循 RFC 3986,三类 URL 形态(绝对路径 / 相对路径 / 绝对 URL)的合并行为截然不同,务必区分。 - 客户端配置(
request.options、curl.options、ssl.certificate_authority等)与单次请求选项(headers、query、auth、timeout、verify、proxy等)是两套机制,前者作用于所有请求,后者单次覆盖。 - 需要同时请求多个资源时,优先使用
send(array(...))并行模式;失败请求通过MultiTransferException::getFailedRequests()与getSuccessfulRequests()分别处理。 - 在 CRMEB 仓库中,Guzzle v3 由
qcloud/cos-sdk-v5间接引入(guzzle/guzzle ~3.7),锁版本为 v3.9.3,其EntityBody被用于 OSS / 京东云文件流上传;若需在项目内直接使用 Guzzle 发送请求,请确认引入路径与版本命名空间(Guzzle\Http\*为 v3,GuzzleHttp\*为 v6+),避免混用导致类加载错误。
- 后端
- 电商
- 前端
【免费下载链接】CRMEB
🔥CRMEB开源商城免费开源多语言商城系统,Tp6框架商城,系统可商用;包含小程序商城、H5商城、公众号商城、PC商城、App,支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面DIY,前后端分离,方便二开,更有详细使用文档、接口文档、数据字典、二开文档/视频教程,欢迎大家提出宝贵意见和建议
相关推荐
Guzzle HTTP 客户端请求选项详解
Guzzle HTTP 客户端请求选项详解 概述 Guzzle 是一个功能强大的 PHP HTTP 客户端,它提供了丰富的请求选项 Request Option
后端vue-resource与GraphQL:使用HTTP客户端发送GraphQL请求
vue resource与GraphQL:使用HTTP客户端发送GraphQL请求 在现代前端开发中,高效的数据获取是构建优秀用户体验的关键。你是否还在为RES
前端Guzzle HTTP客户端:PHP开发者必备的HTTP请求工具
Guzzle HTTP客户端:PHP开发者必备的HTTP请求工具 什么是Guzzle? Guzzle是一个功能强大的PHP HTTP客户端库,它极大地简化了发送
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考