跳到内容

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-revalidatestale-if-error HTTP Cache-Control 扩展。

通过添加单个接口,我们的框架现在可以受益于 HttpKernel 组件中构建的许多功能; HTTP 缓存只是其中之一,但它是一个重要的功能,因为它可以使您的应用程序飞速运行!

本作品,包括代码示例,均根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本