nette/http

🌐 Nette Http: HTTP 请求、响应和会话的抽象。提供数据清理和 URL、cookie 操作的实用工具。

维护者

详细信息

github.com/nette/http

主页

源代码

问题

安装数: 12,654,332

依赖者: 579

建议者: 9

安全性: 0

星标: 451

关注者: 37

分支: 89

开放问题: 36

v3.3.0 2024-01-30 18:16 UTC

README

Downloads this Month Tests Build Status Windows Coverage Status Latest Stable Version License

简介

HTTP 请求和响应被封装在 Nette\Http\RequestNette\Http\Response 对象中,这些对象提供了便捷的 API,并充当清理过滤器。

文档可以在 网站上找到

支持我

你喜欢 Nette DI 吗?你期待新功能吗?

Buy me a coffee

谢谢!

安装

composer require nette/http

它需要 PHP 版本 8.1,并支持 PHP 8.4。

HTTP 请求

HTTP 请求是一个 Nette\Http\Request 对象。重要的是,Nette 在 创建 此对象时,清除所有 GET、POST 和 COOKIE 输入参数以及控制字符和无效的 UTF-8 序列。因此,您可以安全地继续处理数据。清理后的数据用于演示者和表单。

Request 是不可变的。它没有设置器,只有一个所谓的 withUrl() 方法,该方法不会更改对象,但会返回一个带有修改值的新的实例。

withUrl(Nette\Http\UrlScript $url): Nette\Http\Request

返回具有不同 URL 的克隆。

getUrl(): Nette\Http\UrlScript

返回请求的 URL 作为对象 [UrlScript|urls#UrlScript]。

$url = $httpRequest->getUrl();
echo $url; // https://nette.org/en/documentation?action=edit
echo $url->getHost(); // nette.org

浏览器不会向服务器发送片段,因此 $url->getFragment() 将返回空字符串。

getQuery(string $key = null): string|array|null

返回 GET 请求参数

$all = $httpRequest->getQuery();    // array of all URL parameters
$id = $httpRequest->getQuery('id'); // returns GET parameter 'id' (or null)

getPost(string $key = null): string|array|null

返回 POST 请求参数

$all = $httpRequest->getPost();     // array of all POST parameters
$id = $httpRequest->getPost('id');  // returns POST parameter 'id' (or null)

getFile(string $key): Nette\Http\FileUpload|array|null

返回 上传 作为对象 Nette\Http\FileUpload

$file = $httpRequest->getFile('avatar');
if ($file->hasFile()) { // was any file uploaded?
	$file->getName(); // name of the file sent by user
	$file->getSanitizedName(); // the name without dangerous characters
}

getFiles(): array

返回结构化的上传文件树,每个叶节点都是一个 Nette\Http\FileUpload 实例

$files = $httpRequest->getFiles();

getCookie(string $key): string|array|null

返回 cookie 或 null 如果不存在。

$sessId = $httpRequest->getCookie('sess_id');

getCookies(): array

返回所有 cookie

$cookies = $httpRequest->getCookies();

getMethod(): string

返回创建请求时使用的 HTTP 方法。

echo $httpRequest->getMethod(); // GET, POST, HEAD, PUT

isMethod(string $method): bool

检查创建请求时使用的 HTTP 方法。参数不区分大小写。

if ($httpRequest->isMethod('GET')) ...

getHeader(string $header): ?string

返回 HTTP 标头或 null 如果不存在。参数不区分大小写。

$userAgent = $httpRequest->getHeader('User-Agent');

getHeaders(): array

以关联数组的形式返回所有 HTTP 标头

$headers = $httpRequest->getHeaders();
echo $headers['Content-Type'];

getReferer(): ?Nette\Http\UrlImmutable

用户从哪个 URL 来的?请注意,这完全不可靠。

isSecured(): bool

连接是否加密(HTTPS)?您可能需要设置代理以实现正常功能。

isSameSite(): bool

请求是否来自相同的(子)域,并且是通过点击链接发起的?

isAjax(): bool

这是一个 AJAX 请求吗?

getRemoteAddress(): ?string

返回用户的 IP 地址。您可能需要设置代理以实现正常功能。

getRemoteHost(): ?string

返回用户 IP 地址的 DNS 转换。您可能需要设置代理以实现正常功能。

getRawBody(): ?string

返回HTTP请求的主体

$body = $httpRequest->getRawBody();

detectLanguage(array $langs): ?string

检测语言。作为参数 $lang,我们传入应用程序支持的语言数组,并返回浏览器首选的语言。这并不是魔法,该方法只是使用了 Accept-Language 头。如果没有匹配到,则返回 null

// Header sent by browser: Accept-Language: cs,en-us;q=0.8,en;q=0.5,sl;q=0.3

$langs = ['hu', 'pl', 'en']; // languages supported in application
echo $httpRequest->detectLanguage($langs); // en

RequestFactory

当前HTTP请求的对象是由 Nette\Http\RequestFactory 创建的。如果您正在编写不使用DI容器的应用程序,可以按照以下方式创建请求

$factory = new Nette\Http\RequestFactory;
$httpRequest = $factory->fromGlobals();

在调用 fromGlobals() 之前可以配置RequestFactory。我们可以通过使用 $factory->setBinary() 禁用从无效UTF-8序列中对输入参数的所有清理。还可以设置代理服务器,这对于正确检测用户的IP地址非常重要,使用 $factory->setProxy(...)

可以使用过滤器清理掉可能因为各种网站的实现不当的评论系统而进入URL中的字符

// remove spaces from path
$requestFactory->urlFilters['path']['%20'] = '';

// remove dot, comma or right parenthesis form the end of the URL
$requestFactory->urlFilters['url']['[.,)]$'] = '';

// clean the path from duplicated slashes (default filter)
$requestFactory->urlFilters['path']['/{2,}'] = '/';

HTTP响应

HTTP响应是一个 Nette\Http\Response 对象。与请求不同,该对象是可变的,因此可以使用setter来更改状态,即发送头信息。请记住,所有setter 必须在实际输出发送之前调用。 isSent() 方法会告诉是否已经发送了输出。如果它返回 true,则尝试发送头信息将抛出 Nette\InvalidStateException 异常。

setCode(int $code, string $reason = null)

更改状态 响应代码。为了提高源代码的可读性,建议使用 预定义常量 而不是实际数字。

$httpResponse->setCode(Nette\Http\Response::S404_NotFound);

getCode(): int

返回响应的状态码。

isSent(): bool

返回是否已经从服务器发送了头信息到浏览器,因此不再可能发送头信息或更改状态码。

setHeader(string $name, string $value)

发送一个HTTP头信息,并 覆盖 同名之前发送的头信息。

$httpResponse->setHeader('Pragma', 'no-cache');

addHeader(string $name, string $value)

发送一个HTTP头信息,并 不覆盖 同名之前发送的头信息。

$httpResponse->addHeader('Accept', 'application/json');
$httpResponse->addHeader('Accept', 'application/xml');

deleteHeader(string $name)

删除之前发送的HTTP头信息。

getHeader(string $header): ?string

返回已发送的HTTP头信息,或如果不存在则返回 null。该参数不区分大小写。

$pragma = $httpResponse->getHeader('Pragma');

getHeaders(): array

以关联数组的形式返回所有已发送的HTTP头信息。

$headers = $httpResponse->getHeaders();
echo $headers['Pragma'];

setContentType(string $type, string $charset = null)

发送 Content-Type 头。

$httpResponse->setContentType('text/plain', 'UTF-8');

redirect(string $url, int $code = self::S302_FOUND): void

重定向到另一个URL。别忘了退出脚本。

$httpResponse->redirect('http://example.com');
exit;

setExpiration(?string $time)

使用 Cache-ControlExpires 头设置HTTP文档的过期时间。参数可以是时间间隔(作为文本)或 null,这将禁用缓存。

// browser cache expires in one hour
$httpResponse->setExpiration('1 hour');

setCookie(string $name, string $value, $time, string $path = null, string $domain = null, bool $secure = null, bool $httpOnly = null, string $sameSite = null)

发送cookie。参数的默认值是

  • $path 涵盖所有目录('/'
  • $domain 涵盖当前(子)域名,但不包括其子域名
  • $secure 默认为false
  • $httpOnly 为true,所以cookie对JavaScript不可访问
  • $sameSite 为null,所以不指定标志

时间可以指定为字符串或秒数。

$httpResponse->setCookie('lang', 'en', '100 days');

deleteCookie(string $name, string $path = null, string $domain = null, bool $secure = null): void

删除cookie。参数的默认值是

  • $path 涵盖所有目录('/'
  • $domain 涵盖当前(子)域名,但不包括其子域名
  • $secure 默认为false
$httpResponse->deleteCookie('lang');

已上传文件

方法 Nette\Http\Request::getFiles() 返回一个规范化结构的上传文件树,每个叶子节点是一个 Nette\Http\FileUpload 实例。这些对象封装了由 <input type=file> 表单元素提交的数据。

结构反映了 HTML 元素的命名。在最简单的例子中,这可能是一个单独的命名表单元素,提交方式如下

<input type="file" name="avatar">

在这种情况下,$request->getFiles() 返回数组

[
	'avatar' => /* FileUpload instance */
]

即使用户没有上传任何文件或上传失败,也会创建 FileUpload 对象。方法 hasFile() 如果已发送文件则返回 true

$request->getFile('avatar')->hasFile();

对于使用数组表示法的名称的输入

<input type="file" name="my-form[details][avatar]">

返回的树最终看起来像这样

[
	'my-form' => [
		'details' => [
			'avatar' => /* FileUpload instance */
		],
	],
]

您还可以创建文件数组

<input type="file" name="my-form[details][avatars][] multiple">

在这种情况下,结构看起来像

[
	'my-form' => [
		'details' => [
			'avatars' => [
				0 => /* FileUpload instance */,
				1 => /* FileUpload instance */,
				2 => /* FileUpload instance */,
			],
		],
	],
]

访问嵌套数组索引 1 的最佳方式如下

$file = Nette\Utils\Arrays::get(
	$request->getFiles(),
	['my-form', 'details', 'avatars', 1],
	null
);
if ($file instanceof FileUpload) {
	...
}

因为您不能信任外部数据,因此不要依赖于文件结构的格式,使用 Arrays::get()$request->getFiles()['my-form']['details']['avatars'][1] 更安全,后者可能会失败。

FileUpload 方法概述 .{toc: FileUpload}

hasFile(): bool

如果用户上传了文件,则返回 true

isOk(): bool

如果文件已成功上传,则返回 true

getError(): int

返回与上传文件关联的错误代码。它是以下之一 UPLOAD_ERR_XXX 常量。如果文件已成功上传,则返回 UPLOAD_ERR_OK

move(string $dest)

将上传的文件移动到新位置。如果目标文件已存在,它将被覆盖。

$file->move('/path/to/files/name.ext');

getContents(): ?string

返回上传文件的全部内容。如果上传未成功,则返回 null

getContentType(): ?string

根据文件签名检测上传文件的 MIME 内容类型。如果上传未成功或检测失败,则返回 null

需要 PHP 扩展 fileinfo

getName(): string

返回浏览器提交的原始文件名。

不要信任此方法返回的值。客户端可以发送一个恶意的文件名,意图破坏或攻击您的应用程序。

getSanitizedName(): string

返回经过清理的文件名。它只包含 ASCII 字符 [a-zA-Z0-9.-]。如果名称不包含此类字符,则返回 'unknown'。如果文件是 JPEG、PNG、GIF 或 WebP 图像,则返回正确的文件扩展名。

getSize(): int

返回上传文件的大小。如果上传未成功,则返回 0

getTemporaryFile(): string

返回上传文件的临时位置路径。如果上传未成功,则返回 ''

isImage(): bool

如果上传的文件是 JPEG、PNG、GIF 或 WebP 图像,则返回 true。检测基于其签名。不检查整个文件的完整性。例如,您可以尝试 加载 图像以确定图像是否未损坏。

需要 PHP 扩展 fileinfo

getImageSize(): ?array

返回一对 [width, height],表示上传图像的尺寸。如果上传未成功或不是有效图像,则返回 null

toImage(): Nette\Utils\Image

将图像加载为 Image 对象。如果上传未成功或不是有效图像,则抛出 Nette\Utils\ImageException 异常。

会话

使用会话时,每个用户都会收到一个唯一的标识符,称为会话ID,该ID通过cookie传递。这作为会话数据的键。与存储在浏览器端的cookie不同,会话数据存储在服务器端。

会话由Nette\Http\Session对象管理。

开始会话

默认情况下,如果HTTP请求包含带有会话ID的cookie,Nette会自动启动会话。当我们开始读取或写入数据时,它也会自动启动。可以通过$session->start()手动启动会话。

PHP在启动会话时发送影响缓存的HTTP头部,请参阅session_cache_limiter,以及可能带有会话ID的cookie。因此,在发送任何输出到浏览器之前始终需要启动会话,否则将抛出异常。因此,如果您知道在页面渲染过程中将使用会话,请在presenter中手动启动会话,例如。

在开发者模式下,Tracy因为使用它来在Tracy Bar中显示重定向和AJAX请求栏而启动会话。

部分

在纯PHP中,会话数据存储是通过一个全局变量$_SESSION访问的数组。问题是应用程序通常由许多独立的部分组成,如果所有部分只有一个相同的数组可用,那么迟早会发生名称冲突。

Nette框架通过将整个空间划分为部分(对象Nette\Http\SessionSection)来解决此问题。然后,每个单元使用自己的部分和唯一的名称,不会发生冲突。

我们从会话管理器中获取部分

$section = $session->getSession('unique name');

在presenter中,只需用参数调用getSession()

// $this is Presenter
$section = $this->getSession('unique name');

可以通过方法$session->hasSection('unique name')检查部分的存在。

然后就可以简单地使用该部分了

// variable writing
$section->userName = 'john'; // nebo $section['userName'] = 'john';

// variable reading
echo $section->userName; // nebo echo $section['userName'];

// variable removing
unset($section->userName);  // unset($section['userName']);

可以使用foreach循环从部分获取所有变量

foreach ($section as $key => $val) {
	echo "$key = $val";
}

访问不存在的变量不会生成任何错误(返回的值是null)。在某些情况下,这种行为可能是不希望的,因此有更改它的可能性

$section->warnOnUndefined = true;

如何设置过期时间

可以为单个部分甚至单个变量设置过期时间。我们可以让用户的登录在20分钟后过期,但仍然记住购物车的内容。

// section will expire after 20 minutes
$section->setExpiration('20 minutes');

// variable $section->flash will expire after 30 seconds
$section->setExpiration('30 seconds', 'flash');

可以通过方法removeExpiration()取消之前设置的过期时间。通过方法remove()确保立即删除整个部分。

会话管理

Nette\Http\Session类会话管理方法的概述

start(): void

启动会话。

isStarted(): bool

会话是否已启动?

close(): void

结束会话。在脚本结束时,会话会自动结束。

destroy(): void

结束并删除会话。

exists(): bool

HTTP请求是否包含带有会话ID的cookie?

regenerateId(): void

生成一个新的随机会话ID。数据保持不变。

getId(): string

返回会话ID。

配置

必须在启动会话之前调用这些方法。

setName(string $name): static

更改会话名称。可以在一个网站上同时运行多个不同名称的会话。

getName(): string

返回会话名称。

setOptions(array $options): static

配置会话。可以设置所有PHP 会话指令(例如,使用savePath而不是session.save_path)和readAndClose

setExpiration(?string $time): 静态

设置会话不活动后的过期时间。

setCookieParameters(string $path, string $domain = null, bool $secure = null, string $samesite = null): 静态

设置cookie参数。

setSavePath(string $path): 静态

设置存储会话文件的目录。

setHandler(\SessionHandlerInterface $handler): 静态

设置自定义处理程序,请参阅PHP文档

安全第一

服务器假设只要请求包含相同的会话ID,就与相同用户进行通信。安全机制的任务是确保这种行为真的有效,且不存在替换或窃取标识符的可能性。

因此,Nette框架正确配置PHP指令,仅通过cookie传输会话ID,以防止从JavaScript访问,并在关键时刻,如用户登录时,生成新的会话ID。

使用ini_set函数来配置PHP,但不幸的是,某些网络托管服务禁止使用此函数。如果这是您的情况,请尝试联系您的托管提供商,要求允许您使用此函数,或者至少正确配置其服务器。[注]

URL

Nette\Http\Url类使得处理URL及其各个组件变得简单,这些组件在本图中概述。

 scheme  user  password  host   port    path        query  fragment
   |      |      |        |      |       |            |       |
 /--\   /--\ /------\ /-------\ /--\/----------\ /--------\ /----\
 http://john:xyz%[email protected]:8080/en/download?name=param#footer
 \______\__________________________/
     |               |
  hostUrl        authority

URL生成直观

use Nette\Http\Url;

$url = new Url;
$url->setScheme('https')
	->setHost('localhost')
	->setPath('/edit')
	->setQueryParameter('foo', 'bar');

echo $url; // 'https://localhost/edit?foo=bar'

您还可以解析URL然后操作它

$url = new Url(
	'http://john:xyz%[email protected]:8080/en/download?name=param#footer'
);

以下方法可用于获取或更改单个URL组件

我们还可以使用以下方式操作单个查询参数

方法getDomain(int $level = 2)返回主机右侧或左侧的部分。如果主机是www.nette.org,则它就是这样工作的

Url类实现了JsonSerializable接口,并具有__toString()方法,以便对象可以被打印或用于传递给json_encode()的数据。

echo $url;
echo json_encode([$url]);

方法isEqual(string|Url $anotherUrl): bool检查两个URL是否相同。

$url->isEqual('https://nette.org');

UrlImmutable

Nette\Http\UrlImmutable类是类Url的不可变替代方案(正如在PHP中,DateTimeImmutableDateTime的不可变替代方案)。它没有设置器,而是有所谓的withers,它们不会改变对象,而是返回具有修改值的新的实例。

use Nette\Http\UrlImmutable;

$url = new UrlImmutable(
	'http://john:xyz%[email protected]:8080/en/download?name=param#footer'
);

$newUrl = $url
	->withUser('')
	->withPassword('')
	->withPath('/cs/');

echo $newUrl; // 'http://nette.org:8080/cs/?name=param#footer'

以下方法可用于获取或更改单个URL组件

我们还可以使用以下方式操作单个查询参数

方法getDomain(int $level = 2)Url中的方法相同。方法withoutUserInfo()移除userpassword

UrlImmutable类实现了JsonSerializable接口,并具有__toString()方法,以便对象可以被打印或用于传递给json_encode()的数据。

echo $url;
echo json_encode([$url]);

方法isEqual(string|Url $anotherUrl): bool检查两个URL是否相同。

如果您喜欢Nette,请现在捐款。谢谢!