• 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

28.26
/src/Apollo.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Leeqvip\Apollo;
6

7
use GuzzleHttp\Exception\GuzzleException;
8
use Leeqvip\Apollo\Exceptions\ApolloException;
9
use Leeqvip\Apollo\Parsers\Parser;
10

11
/**
12
 * Apollo client core class
13
 */
14
class Apollo
15
{
16
    /**
17
     * Instance
18
     * @var Apollo|null
19
     */
20
    protected static ?Apollo $instance = null;
21

22
    /**
23
     * Configuration management
24
     * @var Config
25
     */
26
    protected Config $config;
27

28
    /**
29
     * Apollo server communication client
30
     * @var Client
31
     */
32
    protected Client $client;
33

34
    /**
35
     * Configuration
36
     * @var array<string, mixed>
37
     */
38
    protected array $options;
39

40
    /**
41
     * Namespace list
42
     * @var array<string>
43
     */
44
    protected array $namespaces = ['application'];
45

46
    protected string $defaultNamespace = 'application';
47

48
    /**
49
     * Cache directory
50
     * @var string
51
     */
52
    protected string $cacheDir;
53

54
    /**
55
     * Constructor
56
     *
57
     * @param array<string, mixed> $options Configuration parameters
58
     */
59
    protected function __construct(array $options)
60
    {
61
        $this->options = $options;
6✔
62
        $this->config = new Config();
6✔
63
        $this->client = new Client($options);
6✔
64
        $this->namespaces = $options['namespaces'] ?? $this->namespaces;
6✔
65
        $this->defaultNamespace = count($this->namespaces) > 0 ? $this->namespaces[0] : $this->defaultNamespace;
6✔
66
        $this->cacheDir = $options['cache_dir'] ?? sys_get_temp_dir() . '/apollo';
6✔
67

68
        // Initialize
69
        $this->init();
6✔
70
    }
71

72
    /**
73
     * Get instance (singleton pattern)
74
     *
75
     * @param array<string, mixed> $options Configuration parameters
76
     * @return Apollo
77
     */
78
    public static function getInstance(array $options = []): Apollo
79
    {
80
        if (!self::$instance) {
12✔
81
            self::$instance = new self($options);
6✔
82
        }
83
        return self::$instance;
12✔
84
    }
85

86
    /**
87
     * Initialize
88
     *
89
     * @return void
90
     */
91
    protected function init(): void
92
    {
93
        // Ensure cache directory exists
94
        if (!is_dir($this->cacheDir)) {
6✔
95
            mkdir($this->cacheDir, 0755, true);
6✔
96
        }
97
    }
98

99
    public function load(): void
100
    {
101
        // Load local cache
NEW
102
        $this->loadCache();
×
103

NEW
104
        $this->pullConfigs();
×
105
    }
106

107
    /**
108
     * Pull configurations
109
     *
110
     * @return void
111
     * @throws ApolloException
112
     * @throws GuzzleException
113
     */
114
    public function pullConfigs(): void
115
    {
NEW
116
        foreach ($this->namespaces as $namespace) {
×
NEW
117
            $configString = $this->client->getConfig($namespace);
×
NEW
118
            $config = $this->parse($configString, $namespace);
×
NEW
119
            $this->config->setBatch($config, $namespace);
×
NEW
120
            $this->saveCache($namespace, $config);
×
121
        }
122
    }
123

124
    protected function parse(string $content, string $namespace): array
125
    {
NEW
126
        $parser = Parser::create($namespace);
×
NEW
127
        return $parser->parse($content);
×
128
    }
129

130
    /**
131
     * Get configuration
132
     *
133
     * @param string $key Configuration key
134
     * @param mixed $default Default value
135
     * @param string|null $namespace Namespace
136
     * @return mixed
137
     */
138
    public function get(string $key, mixed $default = null, ?string $namespace = null): mixed
139
    {
140
        return $this->config->get($key, $default, empty($namespace) ? $this->defaultNamespace : $namespace);
6✔
141
    }
142

143
    /**
144
     * Get all configurations
145
     *
146
     * @param string|null $namespace Namespace
147
     * @return array<string, mixed>
148
     */
149
    public function getAll(?string $namespace = null): array
150
    {
NEW
151
        return $this->config->getAll(empty($namespace) ? $this->defaultNamespace : $namespace);
×
152
    }
153

154
    /**
155
     * Register configuration change callback
156
     *
157
     * @param callable $callback Callback function
158
     * @param string $namespace Namespace
159
     * @return void
160
     */
161
    public function onUpdate(callable $callback, string $namespace = '*'): void
162
    {
NEW
163
        $this->config->registerCallback($callback, $namespace);
×
164
    }
165

166
    /**
167
     * Start listening for configuration changes
168
     *
169
     * @param callable|null $callback Change callback
170
     * @return void
171
     */
172
    public function listen(?callable $callback = null): void
173
    {
NEW
174
        while (true) {
×
NEW
175
            $notifications = $this->client->listenConfig($this->namespaces);
×
NEW
176
            if (!empty($notifications)) {
×
NEW
177
                foreach ($notifications as $notification) {
×
NEW
178
                    ['namespaceName' => $namespace, 'notificationId' => $notificationId] = $notification;
×
NEW
179
                    $config = $this->client->getConfigImmediately($namespace);
×
180

NEW
181
                    $this->config->setBatch($config, $namespace);
×
NEW
182
                    $this->saveCache($namespace, $config);
×
183
                    // notificationId
NEW
184
                    $this->client->setNotificationId($namespace, $notificationId);
×
185

NEW
186
                    if ($callback) {
×
NEW
187
                        call_user_func($callback, $config, $namespace);
×
188
                    }
189
                }
190
            }
191
        }
192
    }
193

194
    /**
195
     * Load local cache
196
     *
197
     * @return void
198
     */
199
    protected function loadCache(): void
200
    {
NEW
201
        foreach ($this->namespaces as $namespace) {
×
NEW
202
            $cacheFile = $this->getCacheFile($namespace);
×
NEW
203
            if (file_exists($cacheFile)) {
×
NEW
204
                $content = file_get_contents($cacheFile);
×
NEW
205
                if ($content) {
×
NEW
206
                    $config = json_decode($content, true);
×
NEW
207
                    if ($config) {
×
NEW
208
                        $this->config->setBatch($config, $namespace);
×
209
                    }
210
                }
211
            }
212
        }
213
    }
214

215
    /**
216
     * Save cache
217
     *
218
     * @param string $namespace Namespace
219
     * @param array<string, mixed> $config Configuration
220
     * @return void
221
     */
222
    protected function saveCache(string $namespace, array $config): void
223
    {
NEW
224
        $cacheFile = $this->getCacheFile($namespace);
×
NEW
225
        file_put_contents($cacheFile, json_encode($config, JSON_UNESCAPED_UNICODE));
×
226
    }
227

228
    /**
229
     * Get cache file path
230
     *
231
     * @param string $namespace Namespace
232
     * @return string
233
     */
234
    protected function getCacheFile(string $namespace): string
235
    {
NEW
236
        return $this->cacheDir . '/' . md5($this->options['app_id'] . '_' . $namespace) . '.json';
×
237
    }
238

239
    /**
240
     * Disable cloning
241
     */
242
    public function __clone(): void {}
243

244
    /**
245
     * Disable serialization
246
     */
247
    public function __wakeup(): void {}
248
}
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