symplely / http
一个简单的PSR-7请求/响应实现,具有Cookie和会话管理/中间件。
Requires
- php: >7.0
- fig/http-message-util: ^1.1
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0
- psr/http-server-handler: ^1.0
Requires (Dev)
- phpunit/phpunit: ^6 | ^7 | ^8
Provides
README
一个完整的 PSR-7 请求/响应实现,包括 Cookie 和 Session 管理/中间件。
安装
最好使用 Composer 进行安装。
composer require symplely/http
概述
消息摘要
所有 Request
和 Response
类共享一个基类 MessageAbstract
,该类提供与消息的头部和主体交互的方法。
以下方法在所有 Request
和 Response
对象上可用
getProtocolVersion()
获取HTTP协议版本作为字符串(例如,“1.0”或“1.1”)。
withProtocolVersion($version)
返回一个新的消息实例,其HTTP协议版本指定为字符串(例如,“1.0”或“1.1”)。
getHeaders()
返回与消息相关联的头部的数组。数组的键是头名称,每个值是该头的字符串数组。
hasHeader($name)
对指定的头名称在消息的头部中进行不区分大小写的比较,以查看是否存在。如果找到,则返回 true
,如果没有找到,则返回 false
。
getHeader($name)
返回给定不区分大小写头值的字符串数组。如果头不存在,它将返回一个空数组。
getHeaderLine($name)
返回给定不区分大小写头的所有值的逗号分隔字符串。如果头不存在,它将返回一个空字符串。
withHeader($name, $value)
返回一个新的消息实例,同时替换给定的头与指定的值或值。
<?php use Async\Http\ServerRequest; $request = new ServerRequest(...); $newRequest = $request->withHeader( 'Content-Type', 'text/html' ); $newRequest = $request->withHeader( 'Accept', ['application/json', 'application/xml'] );
withAddedHeader($name, $value)
返回一个新的消息实例,同时添加给定的头与指定的值或值。与 withHeader()
非常相似,但它维护所有现有头。
withoutHeader($name)
返回一个新的消息实例,同时完全删除给定的头。
getBody()
以 Psr\Http\Message\StreamInterface
格式获取消息的主体。
withBody($body)
返回使用给定主体的新消息实例。主体必须是 Psr\Http\Message\StreamInterface
的实例。
请求
有两种类型的请求:Request
和 ServerRequest
。Request
类用于出站请求,例如,您向另一个服务器发送请求。ServerRequest
类用于入站请求,例如,有人为您发送请求以便您处理并响应。
除非您正在构建HTTP客户端,否则您可能只会使用 ServerRequest
。两者都包含,因为此库是一个完整的PSR-7实现。
- Request(出站)
- ServerRequest(入站)
请求
Request
类用于构建出站、客户端请求。请求被认为是不可变的;所有更改请求状态的方法都返回一个包含更改的新实例。原始请求始终保持不变。
构建一个 Request
静态的 Request::create
是构建请求最一致的方法。
<?php use Async\Http\Request; $request = Request::create('GET', '/some/path?foo=bar');
或者,您也可以手动构建请求。
<?php use Async\Http\Request; $method = 'GET'; $uri = 'http://example.com/'; $headers = ['Content-Type' => 'application/json']; $body = '{"ping": "pong"}'; $protocolVersion = '1.1'; // All of the parameters are optional. $request = new Request( $method, $uri, $headers, $body, $protocolVersion );
除了从 MessageAbstract
继承的所有方法外,还提供了以下方法
getRequestTarget()
获取请求的目标,客户端将看到它。在大多数情况下,这将是对应的 URI 的原始形式,除非提供了特定的值。例如,如果您请求 "http://example.com/search?q=test",那么这将包含 "/search?q=test")。
withRequestTarget($requestTarget)
返回一个新的实例,该实例的消息请求目标如所给。
getMethod()
获取请求的 HTTP 方法。
withMethod($method)
返回一个新的实例,该实例的消息 HTTP 方法设置为所给。方法名称应该是大写,然而它不会为您纠正大小写。
getUri()
以 Psr\Http\Message\UriInterface
的形式获取请求的 URI。
withUri($uri, $preserveHost = false)
返回一个新的实例,该实例的消息 URI 设置为所给。必须给出 Psr\Http\Message\UriInterface
。如果将 preserve host 设置为 true
,则不会更改请求的主机名,除非已经设置了主机名。
ServerRequest
ServerRequest
类扩展了 Request
,用于构建传入的服务器端请求。请求被认为是不可变的;所有更改请求状态的方法都会返回一个新的实例,该实例包含更改。原始请求始终保持不变。
构建 ServerRequest
ServerRequestFactory
是构建请求最一致的方法,无论使用的框架是什么。所有 PSR-17 实现都共享此方法签名。
<?php use Async\Http\ServerRequestFactory; use Psr\Http\Message\ServerRequestInterface; $factory = new ServerRequestFactory(); /** @var ServerRequestInterface */ $request = $factory->createServerRequest('GET', '/some/path?foo=bar'); $request = $factory->createServerRequest('GET', '/some/path?foo=bar', $serverParams);
除了从 Request
继承的所有方法外,还提供了以下方法
getServerParams()
获取请求的服务器参数。通常这是 $_SERVER
变量的内容,但不必如此。
getCookieParams()
获取请求的 cookie 参数。返回的结构与 $_COOKIE
提供的格式匹配。
withCookieParams($cookies)
返回一个新的请求实例,该实例的 cookie 参数已更新。$cookies
参数必须与 $_COOKIE
提供的结构匹配。
getQueryParams()
获取请求的查询字符串参数。通常这是 $_GET
变量的内容,但不必如此。查询参数也可能与 URI 查询参数不同步,因为设置一个不会自动设置另一个。
withQueryParams($query)
返回一个新的请求实例,该实例的查询参数已更新。更新查询参数不会自动更新请求的 URI。
getUploadedFiles()
获取一个规范化文件上传的数组,其中数组的每个节点都是一个 Psr\Http\Message\UploadedFileInterface
。
withUploadedFiles($uploadedFiles)
返回一个新的请求实例,该实例具有给定的文件树。数组的每个节点必须是一个 Psr\Http\Message\UploadedFileInterface
。
<?php use Async\Http\ServerRequest; $request = new ServerRequest(...); // A simple list. $newRequest = $request->withUploadedFiles( [ 'fileA' => $fileA, 'fileB' => $fileB, ] ); // A nested list. $newRequest = $request->withUploadedFiles( [ 'images' => [ 'small' => $fileA, 'large' => $fileB, ], 'foo' => [ 'bar' => [ 'baz' => $fileC, ], ], ] );
getParsedBody()
获取请求体的参数。如果请求的 Content-Type 是 application/x-www-form-urlencoded
或 multipart/form-data
,并且请求方法是 POST
,则此方法将返回一个类似于 $_POST
的数组。对于其他方法,如 PUT
或 PATCH
,只有当 Content-Type 是 application/x-www-form-urlencoded
或 application/json
时,它才会解析主体,然后返回结果数组。
withParsedBody($body)
返回一个新的带有指定解析体的请求实例。它只接受 array
、object
或 null
值。
getAttributes()
获取与请求关联的所有自定义属性。属性是添加到请求中的应用程序特定数据,可以是任何东西,例如路由数据或认证标志。
getAttribute($name, $default = null)
获取请求的给定属性。如果属性未设置,则返回默认值。
withAttribute($name, $value)
返回一个新的请求实例,其中设置了给定的属性。
<?php use Async\Http\ServerRequest; $request = new ServerRequest(...); // If you have a route such as /product/{id} // And a request for /product/123 // You can set the 'id' attribute to the product ID $newRequest = $request->withAttribute('id', 123); // Some controller for the route $controller = function ($request) { // Look up product data $productId = $request->getAttribute('id'); $product = $someRepository->find($productId); // Do something with $product }; $controller($newRequest);
withoutAttribute($name)
返回一个新的请求实例,其中移除了给定的属性。
响应
有三种响应类可用,主要是为了方便,但它们都扩展了 Response
。
Response
Response
类用于向客户端返回数据,通常是 HTML 的形式。
构建一个 Response
静态的 Response::create
是构建响应的最一致方法。
<?php use Async\Http\Response; $response = new Response(); /** @var ResponseInterface */ $response = Response::create(); $response = Response::create(404); $response = Response::create(404, 'Not Found');
或者您可以手动构建一个。
<?php use Async\Http\Response; // Defaults to a 200 OK response. $response = new Response('Hello, world!'); // Use a given status code. $response = new Response(204); // Send custom headers. $response = new Response( 302, 'Goodbye, world!', ['Location' => '/bye-bye'] );
除了从 MessageAbstract
继承的所有方法外,还提供了以下方法
getStatusCode()
可以用来获取响应的 HTTP 状态码(例如,200
或 404
)。
getReasonPhrase()
可以用来获取与状态码相关的文本(例如,OK
或 Not Found
)。
withStatus()
允许您设置状态以及可选的理由短语,并返回一个新响应对象中的更改。
<?php use Async\Http\Response; $response = new Response(...); $newResponse = $response->withStatus(204); $newResponse = $response->withStatus(204, 'No Content');
JsonResponse
JsonResponse
是 Response
类的一个便利扩展,用于简化返回 JSON 数据。它自动将传递给它的任何数据编码为 JSON,并将 Content-Type
头设置为 application/json
。
<?php use Async\Http\JsonResponse; // Defaults to a 200 OK response. $response = new JsonResponse(['message' => 'Hello, world!']); // Custom 404 response. $response = new JsonResponse( ['error' => 'Page not found'], 404 ); // Include additional headers. $response = new JsonResponse( ['error' => 'Invalid credentials'], 401, ['X-Auth' => 'Failed'] );
RedirectResponse
RedirectResponse
是 Response
类的一个便利扩展,用于简化重定向。它自动设置 Location
头,并在正文中包含一个指向要重定向到的 URI 的链接。
<?php use Async\Http\RedirectResponse; // Defaults to a 302 redirect. $redirect = new RedirectResponse('/some/path'); // Use a given status code. $redirect = new RedirectResponse('/some/path', 301); // Send custom headers. $redirect = new RedirectResponse( '/some/path', 302, ['X-Message' => 'Bye-bye'] );
文件上传
UploadedFile
类试图修复 PHP 结构 $_FILES
全局时的问题。
构建一个 UploadedFile
静态的 UploadedFile::create
是构建 UploadedFile
的最一致方法。
<?php use Async\Http\UploadedFile; $stream = ...; $file = UploadedFile::create($stream); $file = UploadedFile::create($stream, $size, $error, $clientFilename, $clientMediaType);
以下方法可用
getStream()
获取一个表示文件上传的 Psr\Http\Message\StreamInterface
。
moveTo($targetPath)
将文件移动到目标路径。内部使用 move_uploaded_file()
或 rename()
,具体取决于是否在 SAPI 或非 SAPI 环境中调用。
getSize()
获取文件的大小。
getError()
获取与文件相关的任何错误代码。这将返回 UPLOAD_ERR_*
常量 之一。
getClientFilename()
获取客户端发送的文件名。此值的真实性不应被信任,因为它很容易被伪造。
getClientMediaType()
获取客户端发送的媒体类型。此值的真实性不应被信任,因为它很容易被伪造。
流
流提供了一种标准化的方式来访问可流式传输的数据,例如请求/响应体和文件上传。然而,它们可能在代码的任何其他部分都很有用。
构建一个 Stream
静态的 Stream::create
、Stream::createFromFile
、Stream::createFromResource
是构建 Stream
的最一致方法。
<?php use Async\Http\Stream; $stream = Stream::create('string of data'); $stream = Stream::createFromFile('/path/to/file', 'r'); $resource = fopen('/path/to/file', 'wb+'); $stream = Stream::createFromResource($resource);
或者您也可以手动构建一个 Stream
。
<?php use Async\Http\Stream; $stream = new Stream('string of data'); $stream = new Stream($resource);
以下方法可用
close()
关闭流及其任何底层资源。
detach()
将底层资源从流中分离出来并返回它。
getSize()
获取流的尺寸,如果已知。
tell()
返回文件指针的当前位置。
eof()
如果流在流的末尾,则返回 true。
isSeekable()
返回流是否可寻址。
seek($offset, $whence = SEEK_SET)
在流中定位到指定位置。 $whence
应该是 PHP 的 SEEK_*
常量 之一。
rewind()
将流定位到开始位置。
isWritable()
返回流是否可写。
write($string)
将数据写入流。
isReadable()
返回流是否可读。
read($length)
从流中读取数据。
getContents()
返回流中剩余的内容。
getMetadata($key = null)
获取流元数据作为关联数组或检索特定的键。返回的键与 PHP 的 stream_get_meta_data()
函数返回的键相同。
URI
Uri
类使得处理 URI 值变得更加容易,因为你可以轻松地获取或设置 URI 的某些部分。
构建 Uri
静态的 Uri::create
是构建 Uri
最一致的方式。
<?php use Async\Http\Uri; $uri = Uri::create('/some/path?foo=bar'); $uri = Uri::create('https://example.com/search?q=test');
或者,你可以手动构建一个 Uri
<?php use Async\Http\Uri; $uri = new Uri('/some/path?foo=bar'); $uri = new Uri('https://example.com/search?q=test');
以下方法可用
getScheme()
检索 URI 的方案部分。
getAuthority()
检索 URI 的权限组件。URI 的权限语法为 [用户信息@]主机[:端口]
。
getUserInfo()
检索 URI 的用户信息组件。语法是 用户名[:密码]
。
getHost()
检索 URI 的主机部分。
getPort()
检索 URI 的端口部分。如果端口是标准端口(例如,HTTP 的 80 或 HTTPS 的 443),则返回 null
。
getPath()
检索 URI 的路径部分。
getQuery()
检索 URI 的查询字符串。
getFragment()
检索 URI 的片段部分。
withScheme($scheme)
返回具有指定方案的新的实例。
withUserInfo($user, $password = null)
返回具有指定用户信息的新的实例。
withHost($host)
返回具有指定主机的新的实例。
withPort($port)
返回具有指定端口的新的实例。
withPath($path)
返回具有指定路径的新的实例。
withQuery($query)
返回具有指定查询的新的实例。
withFragment($fragment)
返回具有指定片段的新的实例。
Cookie
Cookies 处理两个问题,管理 Cookie
请求头和管理 Set-Cookie
响应头。它是通过引入一个用于管理 Cookie 实例集合的 Cookies 类和一个用于管理 SetCookie 实例集合的 SetCookies 类来实现的。
这些类是 repo dflydev-fig-cookies 的合并和重构。
实例化这些集合看起来像这样
use Async\Http\Cookies; use Async\Http\SetCookies; // Get a collection representing the cookies in the `Cookie` headers // of a PSR-7 Request. $cookies = Cookies::fromRequest($request); // Get a collection representing the cookies in the Set-Cookie headers // of a PSR-7 Response $setCookies = SetCookies::fromResponse($response);
在这些集合中以某种方式修改后,它们将被渲染成 PSR-7 请求或 PSR-7 响应,如下所示
// Put the `Cookie` headers and add them to the headers of a // PSR-7 Request. $request = $cookies->intoHeader($request); // Put the `Set-Cookie` headers and add them to the headers of a // PSR-7 Response. $response = $setCookies->intoHeader($response);
对于简单的 Cookie
实例,创建。
use Async\Http\Cookie; // Parse Set-Cookie header(s) and create an instance of CookieInterface. $cookie = (new Cookie()) ->create('PHPSESS=1234567890; Domain=domain.tld; Expires=Wed, 21 Oct 2015 07:28:00 GMT; HttpOnly; Max-Age=86400; Path=/admin; Secure'); // After making changes you can just cast it to a RFC-6265 valid string as show below. $header = (string) $cookie;
类似于 PSR-7 消息,Cookie
、Cookies
、SetCookie
和 SetCookies
都表示为不可变值对象,所有更改都将返回原始对象的具有所需更改的新实例。
虽然这种设计风格有很多好处,但它可以变得非常冗长。为了解决这个问题,以下提供了两个外观,以尝试简化整个流程并使其更加简洁。
基本用法
开始使用 Cookies 的最简单方法是使用 RequestCookies
和 ResponseCookies
类。它们是原始 Cookies 类的外观。它们的任务是使常见的与 cookie 相关的任务比直接使用原始类更加容易和简洁。
创建 Cookies
和 SetCookies
以及重建 请求 和 响应 都会产生开销。这些方法中的每一个都会经过这个过程,所以在同一代码段中使用过多的这些调用时要小心。在某些情况下,直接使用原始类可能比使用外观类更好。
请求 Cookies
请求包含在 Cookie 请求头中的 cookie 信息。这些头中的 cookie 由 Cookie
类表示。
use Async\Http\Cookie; $cookie = Cookie::make('theme', 'blue');
为了轻松处理请求 cookie,请使用 RequestCookies
外观。
获取请求 Cookie
get
方法将返回一个 Cookie
实例。如果不存在指定名称的 cookie,则返回的 Cookie
实例将具有 null
值。
get
的可选第三个参数设置当不存在 cookie 时应使用的值。
use Async\Http\RequestCookies; $cookie = RequestCookies::get($request, 'theme'); $cookie = RequestCookies::get($request, 'theme', 'default-theme');
设置请求 Cookie
set
方法将添加一个 cookie 或替换现有的 cookie。
作为第二个参数使用 Cookie
原始值。
use Async\Http\RequestCookies; $request = RequestCookies::set($request, Cookie::make('theme', 'blue'));
修改请求 Cookie
modify
方法允许根据当前具有指定名称的 cookie 替换 cookie 的内容。第三个参数是一个接受 Cookie
实例作为第一个参数的 callable
,并期望返回一个 Cookie
实例。
如果不存在指定名称的 cookie,则将具有 null
值的新 Cookie
实例传递给 callable。
use Async\Http\RequestCookies; $modify = function (Cookie $cookie) { $value = $cookie->getValue(); // ... inspect current $value and determine if $value should // change or if it can stay the same. in all cases, a cookie // should be returned from this callback... return $cookie->withValue($value); } $request = RequestCookies::modify($request, 'theme', $modify);
删除请求 Cookie
remove
方法如果存在 cookie,则将其删除。
use Async\Http\RequestCookies; $request = RequestCookies::remove($request, 'theme');
请注意,这不会导致客户端删除 cookie。请参阅 ResponseCookies::expire
来执行此操作。
响应 Cookies
响应包含在 Set-Cookie 响应头中的 cookie 信息。这些头中的 cookie 由 SetCookie
类表示。
use Async\Http\SetCookie; $setCookie = SetCookie::create('lu') ->withValue('Rg3vHJZnehYLjVg7qi3bZjzg') ->withExpires('Tue, 15-Jan-2013 21:47:38 GMT') ->withMaxAge(500) ->rememberForever() ->withPath('/') ->withDomain('.example.com') ->withSecure(true) ->withHttpOnly(true) ;
为了轻松处理响应 cookie,请使用 ResponseCookies
外观。
获取响应 Cookie
get
方法将返回一个 SetCookie
实例。如果不存在指定名称的 cookie,则返回的 SetCookie
实例将具有 null
值。
get
的可选第三个参数设置当不存在 cookie 时应使用的值。
use Async\Http\ResponseCookies; $setCookie = ResponseCookies::get($response, 'theme'); $setCookie = ResponseCookies::get($response, 'theme', 'simple');
设置响应 Cookie
set
方法将添加一个 cookie 或替换现有的 cookie。
作为第二个参数使用 SetCookie
原始值。
use Async\Http\ResponseCookies; $response = ResponseCookies::set($response, SetCookie::create('token') ->withValue('a9s87dfz978a9') ->withDomain('example.com') ->withPath('/firewall') );
修改响应 Cookie
modify
方法允许根据当前具有指定名称的 cookie 替换 cookie 的内容。第三个参数是一个接受 SetCookie
实例作为第一个参数的 callable
,并期望返回一个 SetCookie
实例。
如果不存在指定名称的 cookie,则将具有 null
值的新 SetCookie
实例传递给 callable。
use Async\Http\ResponseCookies; $modify = function (SetCookie $setCookie) { $value = $setCookie->getValue(); // ... inspect current $value and determine if $value should // change or if it can stay the same. in all cases, a cookie // should be returned from this callback... return $setCookie ->withValue($newValue) ->withExpires($newExpires) ; } $response = ResponseCookies::modify($response, 'theme', $modify);
删除响应 Cookie
remove
方法如果存在 cookie,则从响应中删除 cookie。
use Async\Http\ResponseCookies; $response = ResponseCookies::remove($response, 'theme');
使响应 Cookie 过期
expire
方法设置一个具有远过去期日期的 cookie。这会导致客户端删除 cookie。
use Async\Http\ResponseCookies; $response = ResponseCookies::expire($response, 'session_cookie');
会话
通常,PHP 会在调用 session_start()
时自动为您发送头信息。然而,这意味着头信息不是作为 PSR-7 响应对象的一部分发送,因此不在您的控制之下。Sessions
通过将相关头信息放入 PSR-7 响应中来将这些头信息重新置于您的控制之下。
此类是 Session
和 SessionHeadersHandler
类在仓库中的合并和重写,位于 Relay.Middleware 和 sessionware 。
此管理器提供了一个优雅的 OOP API 来访问与会话相关的操作
$session = new \Async\Http\Sessions($id, $cacheLimiter, $cacheExpire);
在实例化时,您可以将缓存限制值作为第二个构造函数参数传入。允许的值有 'nocache'、'public'、'private_no_cache' 或 'private'。如果您完全不需要缓存限制头,请传入一个空字符串 ''。默认值为 'nocache'。
您还可以将缓存过期时间(以分钟为单位)作为第四个构造函数参数传入。默认值为 180 分钟。
Sessions::getSession($serverRequest)
从请求中获取会话实例Sessions::start($id)
使用指定 ID 重新启动会话Sessions::toArray()
获取所有会话数据Sessions::getId()
获取会话标识符Sessions::regenerate()
使用加密方法生成会话标识符Sessions::has($item)
验证变量是否已保存到会话中Sessions::set($item, $val)
将变量保存到会话中Sessions::get($item, $default)
从会话中获取变量Sessions::unset($item)
从会话中移除变量Sessions::clear()
移除所有会话变量Sessions::close()
关闭会话并保存其内容,在脚本关闭或 __destruct 时也会自动更新 $_SESSIONSessions::destroy()
销毁会话及其所有内容
会话中间件
相同的实例还附带一个中间件处理器,您可以使用它来自动初始化会话并将会话 cookie 写入响应。
$session = new Sessions(); /** * Session is started, populated with default parameters and the response has session cookie header. * * @param ServerRequestInterface $request * @param ResponseInterface $response * @param RequestHandlerInterface|callable|null $next * * The callable should have something similar to this signature: * function (ServerRequestInterface $request, ResponseInterface $response) : Response { * // your code * } */ $response = $session($request, $response, $next);
永远不要 使用 PHP 内置的
session_*
函数(会话对象最终将无法同步)或$_SESSION
全局变量(更改将被忽略并覆盖)。请使用 Sessions 对象 API
Sessions
实现 IteratorAggregate
、ArrayAccess
、Countable
,因此它看起来非常像 $_SESSION
。因此,只需将您的应用程序中所有 $_SESSION
出现的位置替换为对象的实例。
写入会话
$session->abcd = 'efgh'; //or $session['abcd'] = 'efgh'; //or $session->set('abcd', 'efgh');
从会话中读取
$abcd = $session->abc; //or $abcd = $session['abcd']; //or $abcd = $session->get('abcd');
从会话中移除
unset($session->abc); //or unset($session['abcd']); //or $session->unset('abcd');
清除会话数据
$session->clear();