Поведение Zend View на http-сервере Swoole - PullRequest
0 голосов
/ 21 сентября 2018

мы сталкиваемся со странным поведением приложения Zend Expressive, использующего Zend View и работающего на http-сервере swoole.Это поведение связано с шаблоном Singleton.

Мы настроили Zend Expressive для запуска на http-сервере swoole - https://docs.zendframework.com/zend-expressive-swoole/ - и все запрограммированные API-интерфейсы работают без проблем (swoole - это ракета!)

Но мы попытались перейти к следующему шагу и попытались запустить веб-интерфейс через http-сервер swoole.

Там мы нашли странное поведение.Чтобы упростить мой вопрос (мы используем много помощников вида), наши веб-интерфейсы работают с Zend View и используют помощник headTitle.Когда вы загружаете веб-интерфейс в первый раз, все работает хорошо, но при перезагрузке страницы вы видите, что мета-заголовок дублирован.

Наш следующий обработчик

public function handle(ServerRequestInterface $request) : ResponseInterface
{
    $data = [
        "rand" => rand(1000, 9999),
        "time" => date("Y-m-d H:i:s")
    ];
    return new HtmlResponse($this->template->render('app::my-view', $data));
}

Наше представлениеследующий

<?php $this->headTitle('My Page'); ?>
<b>Rand</b>: <?php echo $rand; ?><br>
<b>Time</b>: <?php echo $time; ?><br>
<hr>

При поиске в Google мы обнаружили в https://framework.zend.com/blog/2018-03-21-expressive-swoole.html, что команда Zend столкнулась с подобной проблемой:

Однако с асинхронным сервером то же самоеЭкземпляр будет использоваться при каждом запросе.Обычно манипуляции с экземплярами сообщений PSR-7 создают новые экземпляры, поскольку реализуемые ими интерфейсы указываются как неизменяемые.К сожалению, из-за технических ограничений языка PHP мы не смогли сделать тело ответных сообщений неизменным.Это означает, что если один процесс пишет в это тело, то последующий процесс - или даже те, которые выполняются параллельно!- получит те же изменения.Это может привести, в лучшем случае, к дублированию контента, а в худшем - к предоставлению неверного контента или утечке информации!

И они решили, что

Для борьбы с этими ситуациями мы изменили службу Psr \ Http \ Message \ ResponseInterface, которую мы зарегистрировали в контейнере внедрения зависимостей: теперь он возвращает не экземпляр интерфейса, а фабрику, способную создать экземпляр

Но, осмотрев классы Zend Framework, мы увидели, что у нас возникла другая проблема, потому что решение было реализовано в коде, и все запросы к apis были правильными

в zend-expressive-swoole / src / ConfigProvider.php youможет видеть

ServerRequestInterface::class         => ServerRequestSwooleFactory::class,

Затем мы меняем нашу точку зрения и осматриваем помощники Zend View, и мы обнаружили, что HeadTitle представления помощника простирается от контейнера \ AbstractStandalone

file: zend-view/src/Helper/HeadTitle
class HeadTitle extends Placeholder\Container\AbstractStandalone

Глядя в этоAbstractStandalone мы обнаружили, что в методе конструктора загружается контейнергрубый синглтон

file: zend-view/src/Helper/Placeholder\Container\AbstractStandalone
public function getContainer()
{
    if (! $this->container instanceof AbstractContainer) {
        $this->container = new $this->containerClass();
    }
    return $this->container;
}

Вот проблема.С модулем Zend Expressive Swoole каждый запрос и каждый ответ независимы, но один и тот же контейнер Singleton используется снова и снова.

Сталкивались ли вы с этой проблемой?Как вы решили это?

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

Заранее спасибо

1 Ответ

0 голосов
/ 26 сентября 2018

Я задал этот вопрос команде разработчиков библиотеки zend-expressive-swoole в https://github.com/zendframework/zend-expressive-swoole/issues/34, и они показали мне способ сделать это:

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
    $layout  = clone $this->layout;
    $this->phpRenderer->viewModel()->setRoot($layout);
    $this->phpRenderer->headTitle()->deleteContainer();
    $this->phpRenderer->headMeta()->deleteContainer();
    $this->phpRenderer->seo()->reset();
}
...