Client::pullAllAndSave()   B
last analyzed

Complexity

Conditions 8
Paths 16

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 8
eloc 21
c 2
b 1
f 0
nc 16
nop 2
dl 0
loc 29
rs 8.4444
1
<?php
2
0 ignored issues
show
Coding Style introduced by
Missing file doc comment
Loading history...
3
namespace Hhxsv5\LaravelS\Components\Apollo;
4
5
use Hhxsv5\LaravelS\Components\HttpClient\SimpleHttpTrait;
6
use Hhxsv5\LaravelS\Swoole\Coroutine\Context;
7
use Swoole\Coroutine;
8
use Symfony\Component\Console\Command\Command;
9
use Symfony\Component\Console\Input\InputOption;
10
11
class Client
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class Client
Loading history...
12
{
13
    use SimpleHttpTrait;
0 ignored issues
show
introduced by
The trait Hhxsv5\LaravelS\Componen...pClient\SimpleHttpTrait requires some properties which are not provided by Hhxsv5\LaravelS\Components\Apollo\Client: $statusCode, $body, $errMsg, $errCode, $headers
Loading history...
14
15
    protected $server;
16
    protected $appId;
17
    protected $cluster      = 'default';
18
    protected $namespaces   = ['application'];
19
    protected $clientIp;
20
    protected $pullTimeout  = 5;
21
    protected $backupOldEnv = false;
22
23
    protected $releaseKeys   = [];
24
    protected $notifications = [];
25
26
    protected $watching = true;
27
28
    public function __construct(array $settings)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
29
    {
30
        $this->server = $settings['server'];
31
        $this->appId = $settings['app_id'];
32
        if (isset($settings['cluster'])) {
33
            $this->cluster = $settings['cluster'];
34
        }
35
        if (isset($settings['namespaces'])) {
36
            $this->namespaces = $settings['namespaces'];
37
        }
38
        if (isset($settings['client_ip'])) {
39
            $this->clientIp = $settings['client_ip'];
40
        } else {
41
            $this->clientIp = current(swoole_get_local_ip()) ?: gethostname();
42
        }
43
        if (isset($settings['pull_timeout'])) {
44
            $this->pullTimeout = (int)$settings['pull_timeout'];
45
        }
46
        if (isset($settings['backup_old_env'])) {
47
            $this->backupOldEnv = (bool)$settings['backup_old_env'];
48
        }
49
    }
50
51
    public static function putCommandOptionsToEnv(array $options)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function putCommandOptionsToEnv()
Loading history...
52
    {
53
        $envs = [
54
            'ENABLE_APOLLO'         => !empty($options['enable-apollo']),
55
            'APOLLO_SERVER'         => $options['apollo-server'],
56
            'APOLLO_APP_ID'         => $options['apollo-app-id'],
57
            'APOLLO_CLUSTER'        => $options['apollo-cluster'],
58
            'APOLLO_NAMESPACES'     => implode(',', $options['apollo-namespaces']),
59
            'APOLLO_CLIENT_IP'      => $options['apollo-client-ip'],
60
            'APOLLO_PULL_TIMEOUT'   => $options['apollo-pull-timeout'],
61
            'APOLLO_BACKUP_OLD_ENV' => $options['apollo-backup-old-env'],
62
        ];
63
        foreach ($envs as $key => $value) {
64
            putenv("{$key}={$value}");
65
        }
66
    }
67
68
    public static function createFromEnv()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function createFromEnv()
Loading history...
69
    {
70
        if (!getenv('APOLLO_SERVER') || !getenv('APOLLO_APP_ID')) {
71
            throw new \InvalidArgumentException('Missing environment variable APOLLO_SERVER or APOLLO_APP_ID');
72
        }
73
        $settings = [
74
            'server'         => getenv('APOLLO_SERVER'),
75
            'app_id'         => getenv('APOLLO_APP_ID'),
76
            'cluster'        => ($cluster = (string)getenv('APOLLO_CLUSTER')) !== '' ? $cluster : null,
77
            'namespaces'     => ($namespaces = (string)getenv('APOLLO_NAMESPACES')) !== '' ? explode(',', $namespaces) : null,
78
            'client_ip'      => ($clientIp = (string)getenv('APOLLO_CLIENT_IP')) !== '' ? $clientIp : null,
79
            'pull_timeout'   => ($pullTimeout = (int)getenv('APOLLO_PULL_TIMEOUT')) > 0 ? $pullTimeout : null,
80
            'backup_old_env' => ($backupOldEnv = (bool)getenv('APOLLO_BACKUP_OLD_ENV')) ? $backupOldEnv : null,
81
        ];
82
        return new static($settings);
83
    }
84
85
    public static function createFromCommandOptions(array $options)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function createFromCommandOptions()
Loading history...
86
    {
87
        if (!isset($options['apollo-server'], $options['apollo-app-id'])) {
88
            throw new \InvalidArgumentException('Missing command option apollo-server or apollo-app-id');
89
        }
90
        $settings = [
91
            'server'         => $options['apollo-server'],
92
            'app_id'         => $options['apollo-app-id'],
93
            'cluster'        => isset($options['apollo-cluster']) && $options['apollo-cluster'] !== '' ? $options['apollo-cluster'] : null,
94
            'namespaces'     => !empty($options['apollo-namespaces']) ? $options['apollo-namespaces'] : null,
95
            'client_ip'      => isset($options['apollo-client-ip']) && $options['apollo-client-ip'] !== '' ? $options['apollo-client-ip'] : null,
96
            'pull_timeout'   => isset($options['apollo-pull-timeout']) ? (int)$options['apollo-pull-timeout'] : null,
97
            'backup_old_env' => isset($options['apollo-backup-old-env']) ? (bool)$options['apollo-backup-old-env'] : null,
98
        ];
99
        return new static($settings);
100
    }
101
102
    public static function attachCommandOptions(Command $command)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function attachCommandOptions()
Loading history...
103
    {
104
        $command->addOption('enable-apollo', null, InputOption::VALUE_NONE, 'Whether to enable Apollo component');
105
        $command->addOption('apollo-server', null, InputOption::VALUE_OPTIONAL, 'Apollo server URL');
106
        $command->addOption('apollo-app-id', null, InputOption::VALUE_OPTIONAL, 'Apollo APP ID');
107
        $command->addOption('apollo-namespaces', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The namespace to which the APP belongs');
108
        $command->addOption('apollo-cluster', null, InputOption::VALUE_OPTIONAL, 'The cluster to which the APP belongs');
109
        $command->addOption('apollo-client-ip', null, InputOption::VALUE_OPTIONAL, 'IP of current instance');
110
        $command->addOption('apollo-pull-timeout', null, InputOption::VALUE_OPTIONAL, 'Timeout time(seconds) when pulling configuration');
111
        $command->addOption('apollo-backup-old-env', null, InputOption::VALUE_NONE, 'Whether to backup the old configuration file when updating the configuration .env file');
112
    }
113
114
    public function pullBatch(array $namespaces, $withReleaseKey = false, array $options = [])
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function pullBatch()
Loading history...
115
    {
116
        $configs = [];
117
        $uri = sprintf('%s/configs/%s/%s/', $this->server, $this->appId, $this->cluster);
118
        foreach ($namespaces as $namespace) {
119
            $url = $uri . $namespace . '?' . http_build_query([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
120
                    'releaseKey' => $withReleaseKey && isset($this->releaseKeys[$namespace]) ? $this->releaseKeys[$namespace] : null,
121
                    'ip'         => $this->clientIp,
122
                ]);
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 12 spaces, but found 16.
Loading history...
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
123
            $timeout = isset($options['timeout']) ? $options['timeout'] : $this->pullTimeout;
124
            $response = $this->httpGet($url, compact('timeout'));
125
            if ($response['statusCode'] === 200) {
126
                $json = json_decode($response['body'], true);
127
                if (is_array($json)) {
128
                    $configs[$namespace] = $json;
129
                    $this->releaseKeys[$namespace] = $configs[$namespace]['releaseKey'];
130
                }
131
            } elseif ($response['statusCode'] === 304) {
132
                // ignore 304
133
            }
134
135
        }
136
        return $configs;
137
    }
138
139
    public function pullAll($withReleaseKey = false, array $options = [])
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function pullAll()
Loading history...
140
    {
141
        return $this->pullBatch($this->namespaces, $withReleaseKey, $options);
142
    }
143
144
    public function pullAllAndSave($filepath, array $options = [])
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function pullAllAndSave()
Loading history...
145
    {
146
        $all = $this->pullAll(false, $options);
147
        if (count($all) !== count($this->namespaces)) {
148
            $lackNamespaces = array_diff($this->namespaces, array_keys($all));
149
            throw new \RuntimeException('Missing Apollo configurations for namespaces ' . implode(',', $lackNamespaces));
150
        }
151
        $configs = [];
152
        foreach ($all as $namespace => $config) {
153
            $configs[] = '# Namespace: ' . $config['namespaceName'];
154
            ksort($config['configurations']);
155
            foreach ($config['configurations'] as $key => $value) {
156
                $key = preg_replace('/[^a-zA-Z0-9_.]/', '_', $key);
157
                $configs[] = sprintf('%s=%s', $key, $value);
158
            }
159
        }
160
        if (empty($configs)) {
161
            throw new \RuntimeException('Empty Apollo configuration list');
162
        }
163
        if ($this->backupOldEnv && file_exists($filepath)) {
164
            rename($filepath, $filepath . '.' . date('YmdHis'));
165
        }
166
        $fileContent = implode(PHP_EOL, $configs);
167
        if (Context::inCoroutine()) {
168
            Coroutine::writeFile($filepath, $fileContent);
169
        } else {
170
            file_put_contents($filepath, $fileContent);
171
        }
172
        return $configs;
173
    }
174
175
    public function startWatchNotification(callable $callback, array $options = [])
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function startWatchNotification()
Loading history...
176
    {
177
        if (!isset($options['timeout']) || $options['timeout'] < 60) {
178
            $options['timeout'] = 70;
179
        }
180
        $this->watching = true;
181
        $this->notifications = [];
182
        foreach ($this->namespaces as $namespace) {
183
            $this->notifications[$namespace] = ['namespaceName' => $namespace, 'notificationId' => -1];
184
        }
185
        while ($this->watching) {
186
            $url = sprintf('%s/notifications/v2?%s',
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
187
                $this->server,
188
                http_build_query([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
189
                    'appId'         => $this->appId,
190
                    'cluster'       => $this->cluster,
191
                    'notifications' => json_encode(array_values($this->notifications)),
192
                ])
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
193
            );
194
            $response = $this->httpGet($url, $options);
195
196
            if ($response['statusCode'] === 200) {
197
                $notifications = json_decode($response['body'], true);
198
                if (empty($notifications)) {
199
                    continue;
200
                }
201
                if (!empty($this->notifications) && current($this->notifications)['notificationId'] !== -1) { // Ignore the first pull
202
                    $callback($notifications);
203
                }
204
                array_walk($notifications, function (&$notification) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
205
                    unset($notification['messages']);
206
                });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
207
                $this->notifications = array_merge($this->notifications, array_column($notifications, null, 'namespaceName'));
208
            } elseif ($response['statusCode'] === 304) {
209
                // ignore 304
210
            }
211
        }
212
    }
213
214
    public function stopWatchNotification()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function stopWatchNotification()
Loading history...
215
    {
216
        $this->watching = false;
217
    }
218
}
219