如何将参数传入 Websocket 处理程序?

问题描述

我正在开发一个记分卡应用程序,其中某些成员正在玩,并且可以在图表中更新他们的分数,这也需要反映在团队成员屏幕中。

为此,我使用了 cboden/ratchet

每个团队都有一个通用的团队代码,我将使用 URL localhost:8000/{token} 传递该代码,该代码将从控制器传递到 twig。

我有以下命令:

    $server = IoServer::factory(
        new HttpServer(
            new WsServer(
                new scoreHandler()
            )
        ),8080
    );
    $server->run();

scoreHandler

public function __construct(EntityManagerInterface $entityManager)
{
    $this->connections = new SplObjectStorage;
    $this->entityManager = $entityManager;
}

这会返回 Too few arguments to function App\Websocket\scoreHandler::__construct(),0 passed 错误

我不知道如何在此处修复此错误。因为我打算插入数据库并根据令牌获取记录并返回到某个用户组。

有人可以帮我吗?

解决方法

注意事项

强烈建议不要将 PHP 与 Symfony 和/或 Doctrine 一起用于任何长时间运行的后台进程(守护进程),它侦听 WebSocket(或其他)连接,在任何生产/现实世界中使用 Ratchet/ReactPHP 风格的功能环境。 PHP 并非设计为作为守护程序运行。因此,如果没有适当的计划,该过程将因 Doctrine Connection 异常和 MySQL Server Has Gone Away 错误或由维护实体管理器、Symfony 服务定义和记录器溢出而导致的内存泄漏而崩溃。

使用 PHP 作为守护进程需要实施不直观的解决方法,例如 Messenger Queue(导致响应之间的长时间延迟)或 Lazy Proxy objects(导致代码级可维护性问题)和额外的后台进程,如主管和/或 cron 作业来规避固有问题并从崩溃中恢复。


如果您对 config/services.yaml 使用默认的 autowire 配置并且 ScoreHandler 不在排除路径之一中,则以下选项可以使用依赖项注入。

# config/services.yaml
services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands,event subscribers,etc.

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'

    # ...

注入 ScoreHandler 即服务

推荐的方法是将 ScoreHandler 服务注入到命令中,这也会自动将 EntityManagerInterface 注入到 ScoreHandler 中。

class YourCommand extends Command
{

    private $handler;

    public function __construct(ScoreHandler $handler)
    {
        $this->handler = $handler;
        parent::__construct();
    }

    //...

    public function execute(InputInterface $input,OutputInterface $outpu)
    {

        //...

        $server = IoServer::factory(
            new HttpServer(
                new WsServer($this->handler)
            ),8080
        );
        $server->run();
    }
}

注入EntityManagerInterface并传递给ScoreHandler

由于您要手动创建 ScoreHandler 的新实例,因此需要将 EntityManagerInterface 作为参数提供给构造函数。

不推荐这样做,因为已经存在一个服务,并且如前一个示例中那样已经自动装配。

class YourCommand extends Command
{

    private $em;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
        parent::__construct();
    }

    //...

    public function execute(InputInterface $input,OutputInterface $outpu)
    {

        //...

        $server = IoServer::factory(
            new HttpServer(
                new WsServer(
                    new ScoreHandler($this->em)
                )
            ),8080
        );
        $server->run();
    }
}

NodeJS 替代方案

虽然 PHP 不是作为守护进程设计的,但 nodejs 和其他几个平台能够促进监听连接。它们可用于将请求转发到您的 Symfony Web 应用程序并将响应发送回客户端,作为请求代理。

有关 WebSocket 代理服务的示例,请参阅 https://stackoverflow.com/a/68027150/1144627