1

我们正面临一个使用 Zend View 并在 swoole http 服务器中运行的 Zend Expressive 应用程序的奇怪行为。此行为与单例模式有关。

我们已将 Zend Expressive 配置为在 swoole http 服务器中运行 - https://docs.zendframework.com/zend-expressive-swoole/ - 并且所有 api 编程工作都没有问题(swoole 是火箭!)

但是我们已经尝试进行下一步,我们尝试通过 swoole http 服务器执行一个 web 界面。

在那里,我们发现了奇怪的行为。为了简化我的问题(我们使用了很多视图助手),我们的 Web 界面与 Zend View 一起使用并使用了 headTitle 助手。当您第一次加载 Web 界面时,一切正常,但是当您重新加载页面时,您会看到元标题重复。

我们的处理程序是下一个

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 你可以看到

ServerRequestInterface::class         => ServerRequestSwooleFactory::class,

然后我们改变观点并检查 zend 视图助手,我们发现视图助手 HeadTitle 扩展自 Container\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 容器。

你遇到过这个问题吗?你是怎么解决这个问题的?

我们正在考虑创建一个自定义助手来删除所有视图助手容器,但如果这样做,我们必须更改很多视图文件以添加这个新的自定义助手。

提前致谢

4

1 回答 1

0

我在https://github.com/zendframework/zend-expressive-swoole/issues/34向 zend-expressive-swoole 库开发团队提出了这个问题,他们向我展示了一种方法:

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();
}
于 2018-09-26T13:27:30.547 回答