modethirteen/hyperplug

一个用于连接HTTP套接字的PHP库

dev-main 2021-09-12 03:35 UTC

This package is auto-updated.

Last update: 2024-09-14 10:13:42 UTC


README

一个用于连接HTTP套接字的PHP库。

github.com codecov.io Latest Stable Version Latest Unstable Version

  • PHP 7.4 (主要,2.x)

安装

使用 Composer。有两种方法可以将此库添加到您的项目中。

从Composer CLI

./composer.phar require modethirteen/hyperplug

或在项目的composer.json中添加modethirteen/hyperplug

{
    "require": {
        "modethirteen/hyperplug": "dev-main"
    }
}

dev-main 是主要开发分支。如果您在生产环境中使用此库,建议您使用稳定版本。

假设您已设置Composer的自动加载器,库可以在 modethirteen\Http\ 命名空间中找到。

入门

快速示例

$plug = new Plug(XUri::newFromString('https://api.example.com/v2'))
    ->withResultParser(new JsonParser());
$result = $plug->at('users', 'bob')
    ->get();
if($result->isSuccess()) {

    // great job!
    echo $result->getVal('body/name');
}

用法

// the library allows for programmatic URL construction and parsing
$uri = XUri::newFromString('http://api.example.com/v3')

    // every step in a URL builder returns an immutable XUri object
    ->withScheme('https')
    ->at('widgets')
    ->withQueryParam('xyzzy', 'plugh')
    ->withQueryParams(QueryParams::newFromArray([
        'bar' => 'qux',
        'baz' => 'fred'
    ]))
    ->withoutQueryParam('bar');

// QueryParams objects are normally immutable
$params = $uri->getQueryParams();

// we can change the data structure of a QueryParams object if we must
$params = $params->toMutableQueryParams();
$params->set('baz', 'abc');

// QueryParams are also iterable
foreach($params as $param => $value) {
    $uri = $uri->withReplacedQueryParam($param, $value);
}

// what does our URL look like now?
$result = $uri->toString(); // https://api.example.com/v3/widgets?xyzzy=plugh&baz=abc

// we can give our XUri object to a Plug to create a client
$plug = new Plug($uri);

// like every object in this library, attaching new values or behaviors to plugs is by default immutable
// ...and returns a new object reference

// add credentials for authorization
$plug->withCredentials('franz', 'beckenbauer');

// or a bearer token
$plug->withHeader('Authorization', 'Bearer 12345');

// we can add some additional URL path segments and query parameters that weren't part of the constructing URL
$plug = $plug->at('another', 'additional', 'endpoint', 'segment')->with('more', 'params');

// how many redirects will we follow?
$plug = $plug->withAutoRedirects(2);

// HTTP requests often need HTTP headers
$plug = $plug->withHeader('X-FcStPauli', 'hells')
    ->withAddedHeader('X-FcStPauli', 'bells')
    ->withHeader('X-HSV', 'you\'ll never walk again');

// ...or not
$plug = $plug->withoutHeader('X-HSV');

// the Headers object, like XUri and QueryParams, is normally immutable
$headers = $plug->getHeaders();
$result = $headers->getHeader('X-FcStPauli'); // ['hells', 'bells']
$result = $headers->getHeaderLine('X-FcStPauli'); // X-HSV: hells, bells

// but if you really want to...
$mutableHeaders = $headers->toMutableHeaders();
$mutableHeaders->set('X-HSV', 'keiner mag den hsv');

// a Headers object is iterable
foreach($mutableHeaders as $header => $values) {
    foreach($values as $value) {

        // HTTP headers can have multiple stored values
        // ...though normally sent via an HTTP client as comma separated on a single HTTP header line
        echo "{$header}: {$value}";
    }
}

// also we can merge the two sets of Headers (the original and the mutated one)
// ...to create a brand new object containing the values of both
$mergedHeaders = $headers->toMergedHeaders($mutableHeaders);

// we've built out a pretty complex HTTP client now
// ...but what if we want a client with a different URL but everything else the same?
$alternateApiPlug = $plug->withUri(XUri::newFromString('https://db.example.com/graph'));

// we are going to invoke an HTTP request
// ...pre and post invocation callbacks can attach special logic and handlers
// ...intended to be executed whenever or wherever this HTTP client is used
// ...maybe there is some logic we want to always perform at the moment the HTTP request is about to be sent?
$plug = $plug->withPreInvokeCallback(function(XUri $uri, IHeaders $headers) {

    // last chance to change the URL or HTTP headers before the request is made
    // ...URL and HTTP headers for the single request invocation can be mutated
    // ...this will not affect the URL or HTTP headers configured in the plug
    $headers->toMutableHeaders()->addHeader('something', 'contextual');
});

// multiple callbacks can be attached (they are executed in the order they are attached)
$plug = $plug->withPreInvokeCallback(function(XUri $uri, IHeaders $headers) {
});

// maybe we want to attach some special handlin that always executes when we receive an HTTP response?
$plug = $plug->withPostInvokeCallback(function(Result $result) {

    // perhaps there is special behavior to always trigger based on the HTTP response status code?
    if($result->is(403)) {
    }
});

// HTTP responses can be parsed from text into traversable data structures by attaching one or more ResultParser objects
// ...parsing can be possibly memory intensive, so limits can be put on the allowed size of a response to parse
$plug = $plug->withResultParser((new JsonParser())->withMaxContentLength(640000));

// fetching resources is handled via HTTP GET
$result = $plug->get();

// deleting resources is handled via HTTP
$result = $plug->delete();

// POST or PUT can optionally send data, in a several different content types as needed
$result = $plug->post(
    (new MultiPartFormDataContent([
        'a' => 'b',
        'c' => 'd'
    ]))
    ->withFileContent(new FileContent('/path/to/file'))
);
$result = $plug->put(new FileContent('/path/to/file'));
$result = $plug->post(new UrlEncodedFormDataContent([
    'e' => 'f',
    'g' => 'h'
]));
$result = $plug->post(JsonContent::newFromArray([
    'a' => [
        'multi-dimensional' => [
            'data',
            'structure'
        ]
    ]
]));
$result = $plug->post(XmlContent::newFromArray([
    'another' => [
        'multi-dimensional' => [
            'data',
            'structure'
        ],
        'formatted' => 'as xml'
    ]
]));
$result = $plug->put(new TextContent('good old text!'));

我们鼓励您探索库的 测试,以了解此处未列出的功能。

开发和测试

社区贡献始终受到欢迎(存在需要解决的问题和改进)。

该库通过结合 PHPUnitMockPlug(一个匹配 HyperPlug 调用并返回模拟响应的拦截器)以及实际的 cURL-驱动的HTTP请求到本地托管的服务器 httpbin 进行测试。进一步使用 PHPStan(PHP静态分析工具)检查代码质量。

# fork and clone the HyperPlug repository
git clone git@github.com:{username}/HyperPlug.git

# install dependencies
composer install

# start the httpbin container
docker-compose up -d

# run static analysis checks
vendor/bin/phpstan analyse

# run tests
export HTTPBIN_BASEURI=https://:8080
vendor/bin/phpunit --configuration phpunit.xml.dist