Компонент HttpKernel: HttpKernelInterface

Дата оновлення перекладу 2022-11-09

Компонент 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,
        $type = self::MASTER_REQUEST,
        $catch = true
    );
}

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,
        $type = HttpKernelInterface::MASTER_REQUEST,
        $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, $year)
{
    $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;
}

Tip

Якщо ви, як і я, запускаєте ваш фреймворк з командного рядку, симулюючи запити (Request::create('/is_leap_year/2012')), то ви можете з легкістю налагодити екземпляри відповіді, скинувши їх представлення рядку (echo $response;), так як воно відображає всі заголовки, а також зміст відповіді.

Щоб переконатися, що все працює правильно, додайте довільне число до змісту відповіді та перевірте, щоб він змінювався лише кожні 10 секунд:

1
$response = new Response('Yep, this is a leap year! '.rand());

Note

При розгортуванні у вашому середовищі виробництва, продовжуйте використовувати зворотний проксі Symfony (чудово для спільного хостингу) або навіьт краще, переключіться на дієвіший звороний проксі, на кшталт Varnish.

Використання HTTP-заголовків кешування для управління кешем вашого додатку - дуже потужний інструмент, який дозволяє вам точно налаштувати вашу стратегію кешування, так як ви можете використовувати як модель закінчення дії, так і валідації в HTTP специфікації. Якщо вам не комфортно з цими концептами, прочитайте главу про HTTP-кешування у документації Symfony.

Клас відповіді містить багато інших методів, які довзволяють вам сконфігурувати 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'
]);

// еквівалентно наступному коду
$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-кешування - це чудово, але що, якщо ви не можете кешувати цілу сторінку? Що, якщо ви можете кешувати все, окрім бічної панелі, яка динамічніша, ніж решта змісту? Включення крайніх сторі (Edge Side Includes)(ESI) йдуть на допомогу! Замість генерування всього змісту за один раз, ESI дозволяють вам відмитити область сторінки, як зміст виклику підзапиту:

1
2
3
4
5
Це зміст вашої сторінки

2012 - это високосный год? 

Інший зміст

Для того, щоб ESI-теги підтримувалися HttpCache, вам потрібно передати йому екземпляр класуESI. Клас ESI автоматично аналізує ESI-теги та змушує підзапити конвертувати їх у правильний зміст:

1
2
3
4
5
$framework = new HttpKernel\HttpCache\HttpCache(
    $framework,
    new HttpKernel\HttpCache\Store(__DIR__.'/../cache'),
    new HttpKernel\HttpCache\Esi()
);

Note

Для того, щоб 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 має багато функцій, на кшталт підтримки розширень HTTP Cache-Control stale-while-revalidate і stale-if-error, як визначено в RFC 5861.

З додаванням єдиного інтерфейсу, наш фреймворк тепер може користуватися перевагами багатьох вбудованих у компонент HttpKernel функцій; HTTP-кешування - це лише одна з них, але вона важлива, так як вона може змусити ваші додатки літати!