HttpKernel 组件:HttpKernelInterface
在本书第二章的结论中,我谈到了使用 Symfony 组件的一个巨大好处:使用它们的**互操作性**在所有框架和应用程序之间。 让我们朝着这个目标迈出一大步,让我们的框架实现 HttpKernelInterface
1 2 3 4 5 6 7 8 9 10 11 12 13 14
namespace Symfony\Component\HttpKernel;
// ...
interface HttpKernelInterface
{
/**
* @return Response A Response instance
*/
public function handle(
Request $request,
int $type = self::MAIN_REQUEST,
bool $catch = true
): Response;
}
HttpKernelInterface
可能是 HttpKernel 组件中最重要的代码,不夸张。 实现此接口的框架和应用程序是完全可互操作的。 此外,许多强大的功能将随之而来,而且是免费的。
更新您的框架,使其实现此接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// example.com/src/Framework.php
// ...
use Symfony\Component\HttpKernel\HttpKernelInterface;
class Framework implements HttpKernelInterface
{
// ...
public function handle(
Request $request,
int $type = HttpKernelInterface::MAIN_REQUEST,
bool $catch = true
) {
// ...
}
}
通过此更改,一小步就能带来巨大的进步! 让我们来谈谈最令人印象深刻的优势之一:透明的 HTTP 缓存 支持。
HttpCache
类实现了一个功能齐全的反向代理,用 PHP 编写; 它实现了 HttpKernelInterface
并包装了另一个 HttpKernelInterface
实例
1 2 3 4 5 6 7 8 9 10 11 12 13
// example.com/web/front.php
// ...
use Symfony\Component\HttpKernel;
$framework = new Simplex\Framework($dispatcher, $matcher, $controllerResolver, $argumentResolver);
$framework = new HttpKernel\HttpCache\HttpCache(
$framework,
new HttpKernel\HttpCache\Store(__DIR__.'/../cache')
);
$response = $framework->handle($request);
$response->send();
这就是为我们的框架添加 HTTP 缓存支持所需的全部操作。 这不是很神奇吗?
缓存的配置需要通过 HTTP 缓存标头完成。 例如,要将响应缓存 10 秒,请使用 Response::setTtl()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// example.com/src/Calendar/Controller/LeapYearController.php
// ...
public function index(Request $request, int $year): Response
{
$leapYear = new LeapYear();
if ($leapYear->isLeapYear($year)) {
$response = new Response('Yep, this is a leap year!');
} else {
$response = new Response('Nope, this is not a leap year.');
}
$response->setTtl(10);
return $response;
}
提示
如果您正在通过模拟请求 (Request::create('/is_leap_year/2012')
) 从命令行运行框架,您可以通过转储其字符串表示形式 (echo $response;
) 来调试 Response 实例,因为它会显示所有标头以及响应内容。
为了验证它是否正常工作,请向响应内容添加一个随机数,并检查该数字是否仅每 10 秒更改一次
1
$response = new Response('Yep, this is a leap year! '.rand());
注意
当部署到生产环境时,请继续使用 Symfony 反向代理(非常适合共享主机),甚至最好切换到更高效的反向代理,例如 Varnish。
使用 HTTP 缓存标头来管理您的应用程序缓存非常强大,并且允许您微调您的缓存策略,因为您可以使用 HTTP 规范的过期和验证模型。 如果您不熟悉这些概念,请阅读 Symfony 文档的 HTTP 缓存 章节。
Response 类包含允许您配置 HTTP 缓存的方法。 其中最强大的一种是 setCache()
,因为它将最常用的缓存策略抽象为一个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
$response->setCache([
'must_revalidate' => false,
'no_cache' => false,
'no_store' => false,
'no_transform' => false,
'public' => true,
'private' => false,
'proxy_revalidate' => false,
'max_age' => 600,
's_maxage' => 600,
'immutable' => true,
'last_modified' => new \DateTime(),
'etag' => 'abcdef'
]);
// it is equivalent to the following code
$response->setPublic();
$response->setMaxAge(600);
$response->setSharedMaxAge(600);
$response->setImmutable();
$response->setLastModified(new \DateTime());
$response->setEtag('abcde');
当使用验证模型时,isNotModified()
方法允许您通过尽早短路响应生成来缩短响应时间
1 2 3 4 5 6 7 8 9
$response->setETag('whatever_you_compute_as_an_etag');
if ($response->isNotModified($request)) {
return $response;
}
$response->setContent('The computed content of the response');
return $response;
使用 HTTP 缓存很棒,但是如果您无法缓存整个页面怎么办? 如果您可以缓存所有内容,但除了某些侧边栏,它比其余内容更动态怎么办? 边缘端包含 (ESI) 来救援! ESI 允许您将页面区域标记为子请求调用的内容,而不是一次性生成整个内容
1 2 3 4 5
This is the content of your page
Is 2012 a leap year? <esi:include src="/leapyear/2012"/>
Some other content
为了使 HttpCache 支持 ESI 标签,您需要向其传递 ESI
类的实例。 ESI
类会自动解析 ESI 标签并发出子请求以将其转换为其正确的内容
1 2 3 4 5
$framework = new HttpKernel\HttpCache\HttpCache(
$framework,
new HttpKernel\HttpCache\Store(__DIR__.'/../cache'),
new HttpKernel\HttpCache\Esi()
);
注意
为了使 ESI 工作,您需要使用支持它的反向代理,例如 Symfony 实现。 Varnish 是最佳替代方案,并且是开源的。
当使用复杂的 HTTP 缓存策略和/或许多 ESI 包含标签时,可能很难理解为什么以及何时应该缓存资源。 为了简化调试,您可以启用调试模式
1 2 3 4 5 6
$framework = new HttpKernel\HttpCache\HttpCache(
$framework,
new HttpKernel\HttpCache\Store(__DIR__.'/../cache'),
new HttpKernel\HttpCache\Esi(),
['debug' => true]
);
调试模式向每个响应添加一个 X-Symfony-Cache
标头,描述缓存层执行的操作
1 2 3
X-Symfony-Cache: GET /is_leap_year/2012: stale, invalid, store
X-Symfony-Cache: GET /is_leap_year/2012: fresh
HttpCache 具有许多功能,例如支持 RFC 5861 中定义的 stale-while-revalidate
和 stale-if-error
HTTP Cache-Control 扩展。
通过添加单个接口,我们的框架现在可以受益于 HttpKernel 组件中构建的许多功能; HTTP 缓存只是其中之一,但它是一个重要的功能,因为它可以使您的应用程序飞速运行!