Статьи

От запроса к ответу: путешествие в Drupal 8 Internals

В первой статье о разработке модуля Drupal 8 мы немного рассмотрели аспект маршрутизации этого процесса. Мы видели, что создание страниц с путями теперь связано с объявлением маршрутов, соответствующих контроллерам. Последний, как мы видели, может возвращать массив визуализации, который интерпретируется в разметку и отображается в основной области содержимого этой страницы. Однако знаете ли вы, что на самом деле Drupal на самом деле преобразует этот массив в объект Response в соответствии с требованиями HTTPKernelInterface Symfony?

drupal8wide

В этой статье я хотел бы, чтобы мы углубились во внутренности Drupal 8 (и Symfony2) и посмотрели на то, что на самом деле происходит (и может случиться) с момента запроса пользователя к тому, в котором они видят что-то вернулся в ответ. Пример, который я упомянул выше, является лишь одним из направлений, в котором может идти этот процесс, и сегодня мы также увидим другие возможности. Цель состоит в том, чтобы понять гибкость системы, которая, в свою очередь, может помочь нам создавать великолепные приложения.

Прежде чем углубляться в это, я настоятельно рекомендую вам ознакомиться с этой диаграммой, которая прекрасно справляется с синтезом того, что часто называют конвейером рендеринга . Хотя, по моему мнению, это представляет больше, чем подразумевает название, потому что система рендеринга является лишь частью того, что изображено, хотя и является большой.

Фронт-контроллер (index.php)

Не секрет, что Symfony2 сейчас является важной частью Drupal. Последний использует многие компоненты Symfony, наиболее важные для этой статьи компоненты HTTPKernel и HTTPFoundation . Вместе они отвечают за инкапсуляцию пользовательского запроса, передачу его приложению, а затем возвращение того, что возвращается пользователю согласованным и OO-способом.

HTTPKernelInterface (о чем вы, вероятно, слышали и в других контекстах ) — это то, что склеивает все это вместе, принимая объект Request и всегда возвращая объект Response . Очень простая, но мощная концепция.

Этот процесс инициируется внутри файла index.php который начинается с генерации указанного объекта Request и передачи его в метод HTTPKernel::handle() . Последний отвечает за возврат объекта Response . На высоком уровне это то, что происходит как в приложении Drupal, так и в приложении Symfony (или любом другом, использующем компонент HTTPKernel).

HTTPKernel и события

HTTPKernel — это сердце любого приложения на базе Symfony. Его метод handle() , как мы видели, несет большую ответственность за подготовку ответа и делает это в рабочем процессе, управляемом событиями . Это создает очень гибкое приложение, в котором тяжелая работа всегда делегируется слушателям этих событий.

Если вы посмотрите на диаграмму, представленную ранее, вы увидите, что этот рабочий процесс изображен во втором столбце, и он, по сути, представляет собой связь между Symfony и стороной Drupal.

Он начинается с первого события, называемого kernel.request . Подписчики на это событие решают различные задачи. Но два очень важных в Drupal 8 — согласование формата и маршрутизация. Первый определяет тип ответа, который должен быть возвращен (html, json, image, pdf и т. Д.), А второй определяет, какой код отвечает за его обработку (ключ _controller определения маршрута в файле routing.yml ) , Но, как и в большинстве шагов в этом kernel.response процессе события, если слушатель возвращает объект ответа, процесс пропускает большинство дальнейших шагов (останавливает распространение) и переходит прямо к kernel.response .

Второе событие — kernel.controller которое вызывается после того, как приложение узнает, какой контроллер отвечает за обработку запроса. На этом этапе слушатели все еще могут выполнять некоторые переопределяющие операции над ним. Внимательно следуя этому шагу, Ядро отвечает за разрешение аргументов, передаваемых контроллеру. Одной из таких операций в Drupal является загрузка объектов на основе идентификаторов, найденных в запросе (например, узлов), и непосредственное предоставление им контроллера. Затем, наконец, вызывается контроллер с соответствующими параметрами.

Контроллер отвечает за возврат какого-либо ответа. Если он возвращает объект Response , процесс переходит к событию kernel.response . Слушатели последнего могут выполнить последние изменения объекта, такие как изменение заголовков или самого содержимого. И после получения его от метода handle() фронт-контроллер использует метод send() объекта Response чтобы отправить его обратно пользователю и завершает процесс.

symfony event workflow

Идем глубже с массивами рендеринга

Если контроллер не возвращает объект Response , ядро ​​запускает одно последнее событие: kernel.view . Его подписчики отвечают за превращение результата контроллера в реальный объект Response . Таким образом, это означает, что у вас есть возможность вернуть из вашего контроллера любой объект, если вы связываете его с подписчиком события VIEW, который превращает это в правильный Response .

Однако, как мы видели в примере , большинство контроллеров времени будут возвращать массив визуализации. Обычно это основное содержание страницы (аналогично обратным вызовам страницы в Drupal 7).

Для этого в Drupal 8 имеется MainContentViewSubscriber, отвечающий за преобразование этого массива в надлежащие объекты Response . Это достигается с помощью определенного MainContentRenderer, выбранного на этапе согласования формата, о котором мы говорили ранее. И хотя таких рендеров уже несколько , по умолчанию используется HtmlRenderer .

HTMLRenderer

Так как это наиболее часто используемый тип рендерера основного контента, давайте немного углубимся и посмотрим, как это создает страницу.

Одним из интересных моментов этого этапа является концепция вариантов страницы.
Это означает, что HTMLRenderer отправляет событие, ответственное за выяснение, какой тип страницы должен использоваться для RenderEvents::SELECT_PAGE_DISPLAY_VARIANT массива рендеринга основного содержимого: RenderEvents::SELECT_PAGE_DISPLAY_VARIANT . По умолчанию SimplePageVariant используется, если не включен модуль блокировки. В этом случае включается BlockPageVariant и позволяет размещать блоки в областях вокруг основного содержимого. Если вы хотите, вы можете подписаться на это событие в своем собственном модуле и предоставить свой собственный вариант.

Все это происходит в методе prepare() HTMLRenderer который предоставляет метод renderResponse() с #type => 'page' который обертывает основной контент. Последние два, в свою очередь, #type => 'html' массив визуализации #type => 'html' который в конечном итоге визуализируется с использованием класса Renderer (эквивалент drupal_render() в Drupal 7). Результирующая строка HTML добавляется к объекту Response и возвращается на фронт-контроллер.

Хотя это очень общий обзор процесса, это в основном то, что происходит. Теперь у нас есть объект Response который означает, что ядро ​​может отправлять свое событие kernel.response . И сразу после этого фронт-контроллер может отправить Response обратно пользователю и завершить процесс.

Вывод

В этой статье мы познакомились с внутренностями Drupal 8 (и Symfony2), следуя конвейеру от запроса пользователя к ответу, который возвращает сервер. Мы видели, как Drupal 8 использует компоненты HTTPKernel и HTTPFoundation Symfony2 и как он в основном живет поверх них. Кроме того, мы видели, как склеиваются между ними события, отправляемые ядром, на которые Drupal подписывается для всей своей функциональности. Наконец, мы увидели, как HTML-страницы создаются и возвращаются пользователю с помощью конвейера рендеринга.

Я полагаю, что понимание того, что происходит под капотом в приложении Drupal 8, позволит вам создавать классные приложения, точно зная, какие у вас точки входа в этот поток. И я считаю, что если вы уберете только одну вещь из этой статьи, это должно быть слово гибкость . Потому что гибкость создания именно того, что нам нужно в Drupal 8, намного превосходит все, что есть в Drupal 7. Он действительно стал современным.