Passed
Push — master ( c66900...e3cbf5 )
by Rafael
06:44
created

MercureHubCommand   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 88
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 14
eloc 41
c 1
b 0
f 0
dl 0
loc 88
ccs 0
cts 51
cp 0
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 5 1
A __construct() 0 5 1
C execute() 0 51 12
1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Command;
12
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Input\InputArgument;
15
use Symfony\Component\Console\Input\InputInterface;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Process\Process;
18
use Ynlo\GraphQLBundle\Subscription\SubscriptionManager;
19
20
/**
21
 * This command act as a middleware to execute the mercure hub process in order to
22
 * listen conected/disconected subscribers to remove subscriptions without any active subscriber
23
 */
24
class MercureHubCommand extends Command
25
{
26
    /**
27
     * @var string
28
     */
29
    protected $secret;
30
31
    /**
32
     * @var SubscriptionManager
33
     */
34
    protected $subscriptionManager;
35
36
    /**
37
     * MercureHubCommand constructor.
38
     *
39
     * @param SubscriptionManager $subscriptionManager
40
     */
41
    public function __construct(SubscriptionManager $subscriptionManager)
42
    {
43
        $this->subscriptionManager = $subscriptionManager;
44
45
        parent::__construct();
46
    }
47
48
    /**
49
     * {@inheritDoc}
50
     */
51
    protected function configure()
52
    {
53
        $this->setName('graphql:mercure:start')
54
             ->setDescription('Start mercure HUB server. Define mercure settings using env variables with MERCURE_* prefix in your .env file')
55
             ->addArgument('mercure', InputArgument::REQUIRED, 'Mercure binary');
56
    }
57
58
    /**
59
     * {@inheritDoc}
60
     */
61
    protected function execute(InputInterface $input, OutputInterface $output)
62
    {
63
        $env = [
64
            'ALLOW_ANONYMOUS' => 1,
65
            'CORS_ALLOWED_ORIGINS' => '*',
66
        ];
67
68
        foreach ($_ENV as $name => $value) {
69
            if (preg_match('/^MERCURE_(\w+)$/', $name, $matches)) {
70
                $env[$matches[1]] = $value;
71
            }
72
        }
73
74
        $process = new Process([$input->getArgument('mercure')], null, $env, null, null);
75
76
        $subscriptionManager = $this->subscriptionManager;
77
        $subscriptionManager->handler()->clear();
78
79
        $subscribersByTopics = [];
80
81
        $process->run(
82
            static function ($type, $msg) use ($output, &$subscribersByTopics, $subscriptionManager) {
83
                $output->writeln($msg);
84
85
                $connected = strpos($msg, '"New subscriber"') !== false;
86
                $disconnected = strpos($msg, '"Subscriber disconnected"') !== false;
87
88
                if ($connected || $disconnected) {
89
                    preg_match('/remote_addr="([^"]+)"/', $msg, $matches);
90
                    $remoteAddr = $matches[1] ?? null;
91
92
                    preg_match('/subscriber_topics="\[([^]]+)/', $msg, $matches);
93
                    $subscription = $matches[1] ?? null;
94
95
                    if ($subscription && $remoteAddr) {
96
                        if ($connected) {
97
                            if (!isset($subscribersByTopics[$subscription])) {
98
                                $subscribersByTopics[$subscription] = [];
99
                            }
100
101
                            $subscriptionManager->handler()->touch($subscription);
102
                            $subscribersByTopics[$subscription][$remoteAddr] = true;
103
                        } elseif ($disconnected) {
104
                            if (isset($subscribersByTopics[$subscription][$remoteAddr])) {
105
                                unset($subscribersByTopics[$subscription][$remoteAddr]);
106
                            }
107
108
                            if (empty($subscribersByTopics[$subscription])) {
109
                                unset($subscribersByTopics[$subscription]);
110
                                echo $subscription;
111
                                $subscriptionManager->handler()->del($subscription);
112
                            }
113
                        }
114
                    }
115
                }
116
            }
117
        );
118
    }
119
}
120