• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

leeqvip / php-apollo / 21220519821

21 Jan 2026 06:06PM UTC coverage: 52.632%. First build
21220519821

push

github

leeqvip
feat: first commit

110 of 209 new or added lines in 9 files covered. (52.63%)

110 of 209 relevant lines covered (52.63%)

6.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.77
/src/Client.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Leeqvip\Apollo;
6

7
use GuzzleHttp\Client as GuzzleClient;
8
use GuzzleHttp\Exception\GuzzleException;
9
use Leeqvip\Apollo\Auths\Auth;
10
use Leeqvip\Apollo\Exceptions\ApolloException;
11
use Leeqvip\Apollo\Traits\FakeClient;
12
use Psr\Http\Message\ResponseInterface;
13

14
/**
15
 * Apollo server communication class
16
 */
17
class Client
18
{
19
    use FakeClient;
20

21
    /**
22
     * Guzzle client
23
     * @var GuzzleClient
24
     */
25
    protected GuzzleClient $httpClient;
26

27
    /**
28
     * Authentication object
29
     * @var Auth
30
     */
31
    protected Auth $auth;
32

33
    /**
34
     * Apollo server URL
35
     * @var string
36
     */
37
    protected string $serverUrl;
38

39
    /**
40
     * Application ID
41
     * @var string
42
     */
43
    protected string $appId;
44

45
    /**
46
     * Cluster
47
     * @var string
48
     */
49
    protected string $cluster;
50

51
    /**
52
     * Secret key for authentication
53
     * @var string
54
     */
55
    protected string $secret = '';
56

57
    /**
58
     * Configuration version
59
     * @var array<string, string>
60
     */
61
    protected array $releaseKeys = [];
62

63
    /**
64
     * @var array<string, int>
65
     */
66
    protected array $notificationId = [];
67

68
    /**
69
     * Constructor
70
     *
71
     * @param array<string, mixed> $config Configuration parameters
72
     */
73
    public function __construct(array $config)
74
    {
75
        $this->serverUrl = $config['server_url'] ?? 'http://localhost:8080';
18✔
76
        $this->appId = $config['app_id'] ?? '';
18✔
77
        $this->cluster = $config['cluster'] ?? 'default';
18✔
78
        $this->secret = $config['secret'] ?? '';
18✔
79

80
        $this->httpClient = new GuzzleClient([
18✔
81
            'base_uri' => $this->serverUrl,
18✔
82
            'timeout' => 60,
18✔
83
            'connect_timeout' => 10,
18✔
84
            'headers' => $this->getHeaders(),
18✔
85
        ]);
18✔
86

87
        $this->auth = new Auth();
18✔
88
    }
89

90
    /**
91
     * Get configuration immediately
92
     *
93
     * @param string $namespace Namespace
94
     * @return array<string, mixed>
95
     * @throws ApolloException
96
     * @throws GuzzleException
97
     */
98
    public function getConfigImmediately(string $namespace): array
99
    {
100
        $uri = '/configs/' . $this->appId . '/' . $this->cluster . '/' . $namespace;
6✔
101
        $query = http_build_query([
6✔
102
            'releaseKey' => $this->releaseKeys[$namespace] ?? '',
6✔
103
            'ip' => $this->getClientIp(),
6✔
104
        ]);
6✔
105
        $uri .= '?' . $query;
6✔
106

107
        $body = $this->get($uri);
6✔
108

109
        $data = json_decode($body, true);
6✔
110

111
        if (!isset($data['configurations'])) {
6✔
NEW
112
            throw new ApolloException('Not found configurations key', 400, null, ['body' => $body]);
×
113
        }
114

115
        $this->releaseKeys[$namespace] = $data['releaseKey'];
6✔
116
        return $data['configurations'];
6✔
117
    }
118

119
    /**
120
     * @throws GuzzleException
121
     * @throws ApolloException
122
     */
123
    public function getConfig(string $namespace): string
124
    {
125
        $uri = '/configfiles/json/' . $this->appId . '/' . $this->cluster . '/' . $namespace;
6✔
126
        $query = http_build_query([
6✔
127
            'ip' => $this->getClientIp(),
6✔
128
        ]);
6✔
129
        $uri .= '?' . $query;
6✔
130

131
        return $this->get($uri);
6✔
132
    }
133

134
    /**
135
     * Listen for configuration changes
136
     *
137
     * @param array<string> $namespaces Namespace list
138
     * @param int $timeout Timeout in seconds
139
     * @return array<array<string, mixed>>
140
     * @throws ApolloException
141
     * @throws GuzzleException
142
     */
143
    public function listenConfig(array $namespaces, int $timeout = 60): ?array
144
    {
NEW
145
        $query = [
×
NEW
146
            'appId' => $this->appId,
×
NEW
147
            'cluster' => $this->cluster,
×
NEW
148
            'notifications' => json_encode(array_map(function (string $namespace) {
×
NEW
149
                return [
×
NEW
150
                    'namespaceName' => $namespace,
×
NEW
151
                    'notificationId' => $this->getNotificationId($namespace),
×
NEW
152
                ];
×
NEW
153
            }, $namespaces))
×
NEW
154
        ];
×
NEW
155
        $url = '/notifications/v2?' . http_build_query($query);
×
NEW
156
        $body = $this->get($url, [
×
NEW
157
            'timeout' => $timeout + 10,
×
NEW
158
        ]);
×
159

NEW
160
        return json_decode($body, true);
×
161
    }
162

163
    /**
164
     * Get client IP
165
     *
166
     * @return string
167
     */
168
    protected function getClientIp(): string
169
    {
170
        $ipKeys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'];
12✔
171

172
        foreach ($ipKeys as $key) {
12✔
173
            if (!empty($_SERVER[$key])) {
12✔
NEW
174
                $ip = trim($_SERVER[$key]);
×
NEW
175
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
×
NEW
176
                    return $ip;
×
177
                }
178
            }
179
        }
180

181
        return '127.0.0.1';
12✔
182
    }
183

184
    /**
185
     * Get HTTP headers
186
     *
187
     * @return array<string, string>
188
     */
189
    protected function getHeaders(): array
190
    {
191
        return [
18✔
192
            'Content-Type' => 'application/json',
18✔
193
            'Accept' => 'application/json',
18✔
194
        ];
18✔
195
    }
196

197
    /**
198
     * @param string $uri
199
     * @param array $options
200
     * @return string
201
     * @throws ApolloException
202
     * @throws GuzzleException
203
     */
204
    public function get(string $uri, array $options = []): string
205
    {
206
        $authHeaders = $this->auth->headers($uri, $this->appId, $this->secret);
12✔
207

208
        $options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? [], $authHeaders);
12✔
209

210
        $response = $this->httpGet($uri, $options);
12✔
211

212
        if ($response->getStatusCode() >= 400) {
12✔
NEW
213
            throw new ApolloException('Request failed with status code ' . $response->getStatusCode(), 400, null, ['response' => $response->getBody()]);
×
214
        }
215

216
        return $response->getBody()->getContents();
12✔
217
    }
218

219
    /**
220
     * @throws GuzzleException
221
     */
222
    protected function httpGet(string $uri, array $options = []): ResponseInterface
223
    {
224
        $fakeResponse = $this->fakeGet($uri, $options);
12✔
225
        if ($fakeResponse) {
12✔
226
            return $fakeResponse;
12✔
227
        }
228

NEW
229
        return $this->httpClient->get($uri, $options);
×
230
    }
231

232
    /**
233
     * Get current release key
234
     *
235
     * @param string $namespace Namespace
236
     * @return string|null
237
     */
238
    public function getReleaseKey(string $namespace): ?string
239
    {
NEW
240
        return $this->releaseKeys[$namespace] ?? null;
×
241
    }
242

243
    /**
244
     * Set release key
245
     *
246
     * @param string $namespace Namespace
247
     * @param string $releaseKey Release key
248
     * @return void
249
     */
250
    public function setReleaseKey(string $namespace, string $releaseKey): void
251
    {
NEW
252
        $this->releaseKeys[$namespace] = $releaseKey;
×
253
    }
254

255
    public function getNotificationId(string $namespace): int
256
    {
NEW
257
        return $this->notificationId[$namespace] ?? -1;
×
258
    }
259

260

261
    public function setNotificationId(string $namespace, int $notificationId): void
262
    {
NEW
263
        $this->notificationId[$namespace] = $notificationId;
×
264
    }
265
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc