3

I am trying to build a Symfony 4.3.3 command where I want to add the ability for callback functions to be overwritten at any time. Look at the following code snippet:

namespace App\Command;

use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\WebSocket\Server;

final class SwooleWsCommand extends Command
{
    // ...

    protected function execute(InputInterface $input, OutputInterface $output): bool
    {
        $port   = $input->getArgument('port');
        $server = new SwooleServer("127.0.0.1", (int)$port);
        $server->on('request', static function (Request $request, Response $response) {
            $response->end('Hello '.$request->rawcontent());
        });
        $server->start();
        return true;
    }
}

I want to convert this:

$server->on('request', static function (Request $request, Response $response) {
    $response->end('Hello '.$request->rawcontent());
});

Into this (if it possible and it's not one crazy thing or impossible to achieve):

$server->on('request', <function>);

My idea, and I might be completely wrong, is to create an interface allowing the methods to be overwritten at any time. Below is a code snippet of it:

namespace App\Service;

use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;

interface SwooleWsCallbackInterface
{
    public function request(Request $request, Response $response): void;
    public function message(Server $server, Frame $frame): void;
}

From there I should be able to inject the interface in the command, something like:

/** @var SwooleWsCallbackInterface */
private $callback;

public function __construct(SwooleWsCallbackInterface $callback)
{
    $this->callback = $callback;
    parent::__construct();
}

As you may notice the function request() receives two parameters. For such reason once I invoke the method from the command as $server->on('request', $this->callback->request($request, $response)); it tries to access both parameters.

I have read Symfony Docs for How to Autowire Interfaces but still not clear to me how to properly set the parameters needed by the method to work.

If I am understanding the docs properly here:

 App\Util\TransformerInterface: '@App\Util\Rot13Transformer'

They've already overwritten the methods at App\Util\Rot13Transformer and should not have any problems. However the example does not show the method transform from the interface TransformerInterface being invoked.

Not sure if the proper autowiring on my scenario would be:

  App\Command\SwooleWsCommand: ~
  sdk.command.swoolews:
    alias: App\Command\SwooleWsCommand

  App\Service\SwooleWsCallbackInterface: ~
  sdk.swoolews:
    alias: App\Service\SwooleWsCallbackInterface

Can I get some ideas here for how to achieve this? And some explanation in how this works? I might be getting it wrong :|

Note: if something is not clear or it's confusing just ask me

ReynierPM
  • 17,594
  • 53
  • 193
  • 363

2 Answers2

1

You still want to create a closure to pass to $server->on. You can do as such with:

$onRequest = Closure::fromCallable([$this->callback, 'request']);

and then pass it:

$server->on('request', $onRequest);
Federkun
  • 36,084
  • 8
  • 78
  • 90
  • Can you extend that example a little bit? I am not following you here – ReynierPM Aug 14 '19 at 19:48
  • `$onRequest` will create a callback that you can pass to `$server->on`, which will then invoke with the req and res arguments. You should be able, with your `SwooleWsCallbackInterface $callback`, to just copy and paste the example above. Or did I misread your question? – Federkun Aug 14 '19 at 19:50
  • then I do not need to add any services to `service.yaml` nor autowire anything? – ReynierPM Aug 14 '19 at 19:52
  • you _do_ need to provide an implementation of SwooleWsCallbackInterface to your command, you can choose how. autowire should work fine here. – Federkun Aug 14 '19 at 19:53
  • by implementation you mean a class that implements the interface? Check the part I added for autowiring in the OP not sure if that is how it should be or if I should follow the same pattern as in Symfony doc – ReynierPM Aug 14 '19 at 19:59
  • have you followed https://symfony.com/doc/current/console/commands_as_services.html ? – Federkun Aug 14 '19 at 20:01
  • Yes that doc is what I've used for the command but none of the examples at docs uses function with parameters. They are just simple examples to show you the how to. – ReynierPM Aug 14 '19 at 20:04
  • I may have lost you there; what do you mean with "uses function with parameters"? You inject a class, the example in the documentation should cover your use case – Federkun Aug 14 '19 at 20:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/197966/discussion-between-reynierpm-and-federkun). – ReynierPM Aug 14 '19 at 20:09
  • Be aware that using closure::fromCallable May cause some problems with some tools like IDEs And static analysers. As they May think that the method Is never used when it actualy Is. Write the closure directly (like in my answer) And these problems Will be gone... – slepic Aug 15 '19 at 04:56
1

You are invoking the function where you want to pass reference to the function instead. Try this:

$server->on('request', function ($request, $response) {
$this->callback->request($request, $response);
});
slepic
  • 641
  • 4
  • 11