feat(php-client): 实现 PHP 客户端 SDK 及示例
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
FROM php:8.3-cli
|
||||||
|
|
||||||
|
WORKDIR /kenaito-conf-php-client-demo
|
||||||
|
|
||||||
|
# Install required extensions
|
||||||
|
RUN docker-php-ext-install pcntl
|
||||||
|
|
||||||
|
# Copy client library
|
||||||
|
COPY kenaito-conf-php-client/ /kenaito-conf-php-client/
|
||||||
|
|
||||||
|
# Copy demo
|
||||||
|
COPY kenaito-conf-php-client-demo/app.php /kenaito-conf-php-client-demo/app.php
|
||||||
|
|
||||||
|
ENTRYPOINT ["php", "app.php"]
|
||||||
@@ -24,20 +24,12 @@ kenaito-conf/
|
|||||||
├── kenaito-conf-nodejs-client-demo/ # Node.js 客户端示例(端口 8083)
|
├── kenaito-conf-nodejs-client-demo/ # Node.js 客户端示例(端口 8083)
|
||||||
├── kenaito-conf-perl-client/ # Perl 客户端 SDK(Perl 5.16+)
|
├── kenaito-conf-perl-client/ # Perl 客户端 SDK(Perl 5.16+)
|
||||||
├── kenaito-conf-perl-client-demo/ # Perl 客户端示例
|
├── kenaito-conf-perl-client-demo/ # Perl 客户端示例
|
||||||
├── kenaito-conf-php-client/ # (未支持)PHP 客户端 SDK(PHP 8.0+)
|
├── kenaito-conf-php-client/ # PHP 客户端 SDK(PHP 7.4+)
|
||||||
├── kenaito-conf-php-client-demo/ # (未支持)PHP 客户端示例
|
├── kenaito-conf-php-client-demo/ # PHP 客户端示例
|
||||||
├── kenaito-conf-dotnet-client/ # .NET 客户端 SDK(.NET 6.0+)
|
├── kenaito-conf-dotnet-client/ # .NET 客户端 SDK(.NET 6.0+)
|
||||||
├── kenaito-conf-dotnet-client-demo/ # .NET 客户端示例
|
├── kenaito-conf-dotnet-client-demo/ # .NET 客户端示例
|
||||||
├── kenaito-conf-c-client/ # (未支持)C 客户端 SDK(libcurl + OpenSSL)
|
|
||||||
├── kenaito-conf-c-client-demo/ # (未支持)C 客户端示例
|
|
||||||
├── kenaito-conf-cpp-client/ # (未支持)C++ 客户端 SDK(libcurl + OpenSSL)
|
|
||||||
├── kenaito-conf-cpp-client-demo/ # (未支持)C++ 客户端示例
|
|
||||||
├── kenaito-conf-lua-client/ # Lua 客户端 SDK(Lua 5.1+)
|
├── kenaito-conf-lua-client/ # Lua 客户端 SDK(Lua 5.1+)
|
||||||
├── kenaito-conf-lua-client-demo/ # Lua 客户端示例
|
└── kenaito-conf-lua-client-demo/ # Lua 客户端示例
|
||||||
├── kenaito-conf-rust-client/ # (未支持)Rust 客户端 SDK(Rust 1.70+)
|
|
||||||
├── kenaito-conf-rust-client-demo/ # (未支持)Rust 客户端示例
|
|
||||||
├── kenaito-conf-ruby-client/ # (未支持)Ruby 客户端 SDK(Ruby 2.7+)
|
|
||||||
└── kenaito-conf-ruby-client-demo/ # (未支持)Ruby 客户端示例
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
@@ -370,6 +362,61 @@ client:sync_once()
|
|||||||
client:stop()
|
client:stop()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### PHP 客户端
|
||||||
|
|
||||||
|
适用于 PHP 7.4+ 项目。
|
||||||
|
|
||||||
|
**1. 安装**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd kenaito-conf-php-client
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
或在项目中引入:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require kenaito/conf-client
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 使用**
|
||||||
|
|
||||||
|
```php
|
||||||
|
require_once 'vendor/autoload.php';
|
||||||
|
|
||||||
|
use Kenaito\Conf\Config;
|
||||||
|
use Kenaito\Conf\Client;
|
||||||
|
|
||||||
|
$config = new Config([
|
||||||
|
'baseUrl' => 'http://localhost:8080',
|
||||||
|
'appName' => 'my-app',
|
||||||
|
'env' => 'daily',
|
||||||
|
'syncInterval' => 10,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$client = new Client($config);
|
||||||
|
|
||||||
|
// 监听配置变更
|
||||||
|
$client->onChange(function(array $changed) {
|
||||||
|
foreach ($changed as $key => $value) {
|
||||||
|
echo "config changed: $key = $value\n";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动客户端(拉取配置)
|
||||||
|
$client->start();
|
||||||
|
|
||||||
|
// 获取配置
|
||||||
|
$value = $client->get('database.host');
|
||||||
|
$all = $client->getAll();
|
||||||
|
|
||||||
|
// 手动同步一次
|
||||||
|
$client->syncOnce();
|
||||||
|
|
||||||
|
// 停止
|
||||||
|
$client->stop();
|
||||||
|
```
|
||||||
|
|
||||||
## API 概览
|
## API 概览
|
||||||
|
|
||||||
### 管理端 API(需 JWT 认证)
|
### 管理端 API(需 JWT 认证)
|
||||||
|
|||||||
@@ -98,6 +98,18 @@ docker run -it --rm \
|
|||||||
kenaito-conf-lua-demo
|
kenaito-conf-lua-demo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## PHP 客户端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -f Dockerfile.php -t kenaito-conf-php-demo .
|
||||||
|
|
||||||
|
docker run -it --rm \
|
||||||
|
-e KENAITO_BASE_URL=http://host.docker.internal:8080 \
|
||||||
|
-e KENAITO_APP_NAME=kenaito-springboot-demo \
|
||||||
|
-e KENAITO_ENV=daily \
|
||||||
|
kenaito-conf-php-demo
|
||||||
|
```
|
||||||
|
|
||||||
## Spring Boot 2 客户端(测试通过)
|
## Spring Boot 2 客户端(测试通过)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../kenaito-conf-php-client/autoload.php';
|
||||||
|
|
||||||
|
use Kenaito\Conf\Config;
|
||||||
|
use Kenaito\Conf\Client;
|
||||||
|
|
||||||
|
function getEnvVar(string $key, string $default): string
|
||||||
|
{
|
||||||
|
$value = getenv($key);
|
||||||
|
return ($value !== false && $value !== '') ? $value : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置值(可被远程配置覆盖)
|
||||||
|
$configValues = [
|
||||||
|
'database.host' => getEnvVar('DATABASE_HOST', '192.168.1.1'),
|
||||||
|
'database.port' => getEnvVar('DATABASE_PORT', '8888'),
|
||||||
|
'database.name' => getEnvVar('DATABASE_NAME', 'xxxx'),
|
||||||
|
'database.user' => getEnvVar('DATABASE_USER', 'xxxx'),
|
||||||
|
'database.password' => getEnvVar('DATABASE_PASSWORD', 'xxxxxxxx'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$config = new Config([
|
||||||
|
'baseUrl' => getEnvVar('KENAITO_BASE_URL', 'http://localhost:8080'),
|
||||||
|
'appName' => getEnvVar('KENAITO_APP_NAME', 'kenaito-springboot-demo'),
|
||||||
|
'env' => getEnvVar('KENAITO_ENV', 'daily'),
|
||||||
|
'version' => getEnvVar('KENAITO_VERSION', 'latest'),
|
||||||
|
'token' => getEnvVar('KENAITO_TOKEN', ''),
|
||||||
|
'syncInterval' => (int)getEnvVar('KENAITO_SYNC_INTERVAL', '10'),
|
||||||
|
'cacheDir' => getEnvVar('KENAITO_CACHE_DIR', '.'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$client = new Client($config);
|
||||||
|
|
||||||
|
// 监听配置变更
|
||||||
|
$client->onChange(function(array $changed) use (&$configValues) {
|
||||||
|
echo "Config changed:\n";
|
||||||
|
foreach ($changed as $key => $value) {
|
||||||
|
echo " $key = $value\n";
|
||||||
|
if (isset($configValues[$key])) {
|
||||||
|
$configValues[$key] = $value;
|
||||||
|
echo " [updated] $key = $value\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动客户端
|
||||||
|
try {
|
||||||
|
$client->start();
|
||||||
|
$remote = $client->getAll();
|
||||||
|
foreach ($configValues as $key => $value) {
|
||||||
|
if (isset($remote[$key])) {
|
||||||
|
$configValues[$key] = $remote[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "loaded " . count($remote) . " configs from remote\n\n";
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Failed to init config client, using local defaults: " . $e->getMessage() . "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示当前配置
|
||||||
|
echo "Current configs:\n";
|
||||||
|
ksort($configValues);
|
||||||
|
foreach ($configValues as $key => $value) {
|
||||||
|
echo " $key = $value\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\nListening for config changes... (press Ctrl+C to stop)\n";
|
||||||
|
|
||||||
|
// 信号处理
|
||||||
|
pcntl_signal(SIGINT, function() use ($client) {
|
||||||
|
echo "\nShutting down...\n";
|
||||||
|
$client->stop();
|
||||||
|
exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 同步循环
|
||||||
|
while (!$client->stop) {
|
||||||
|
pcntl_signal_dispatch();
|
||||||
|
$client->syncOnce();
|
||||||
|
sleep($config->syncInterval);
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# Kenaito Config Center PHP Client
|
||||||
|
|
||||||
|
PHP 客户端 SDK,适用于 PHP 7.4+ 项目。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
### 使用 Composer(推荐)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd kenaito-conf-php-client
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
在你的项目中引入:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require kenaito/conf-client
|
||||||
|
```
|
||||||
|
|
||||||
|
### 手动引入
|
||||||
|
|
||||||
|
```php
|
||||||
|
require_once '/path/to/kenaito-conf-php-client/autoload.php';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用
|
||||||
|
|
||||||
|
```php
|
||||||
|
require_once 'vendor/autoload.php';
|
||||||
|
|
||||||
|
use Kenaito\Conf\Config;
|
||||||
|
use Kenaito\Conf\Client;
|
||||||
|
|
||||||
|
$config = new Config([
|
||||||
|
'baseUrl' => 'http://localhost:8080',
|
||||||
|
'appName' => 'my-app',
|
||||||
|
'env' => 'daily',
|
||||||
|
'syncInterval' => 10,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$client = new Client($config);
|
||||||
|
|
||||||
|
// 监听配置变更
|
||||||
|
$client->onChange(function(array $changed) {
|
||||||
|
foreach ($changed as $key => $value) {
|
||||||
|
echo "config changed: $key = $value\n";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动客户端(拉取配置)
|
||||||
|
$client->start();
|
||||||
|
|
||||||
|
// 获取配置
|
||||||
|
$value = $client->get('database.host');
|
||||||
|
$all = $client->getAll();
|
||||||
|
|
||||||
|
// 手动同步一次
|
||||||
|
$client->syncOnce();
|
||||||
|
|
||||||
|
// 停止
|
||||||
|
$client->stop();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置项
|
||||||
|
|
||||||
|
| 参数 | 说明 | 默认值 |
|
||||||
|
|------|------|--------|
|
||||||
|
| `baseUrl` | 配置中心地址 | `http://localhost:8080` |
|
||||||
|
| `appName` | 应用名称 | (必填) |
|
||||||
|
| `env` | 环境标识 | `dev` |
|
||||||
|
| `version` | 配置版本 | `latest` |
|
||||||
|
| `token` | 应用 Token | 空 |
|
||||||
|
| `syncInterval` | 同步间隔(秒) | `10` |
|
||||||
|
| `ignoreGlobal` | 忽略全局配置 | `false` |
|
||||||
|
| `cacheDir` | 缓存目录 | `.` |
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
- PHP 7.4+
|
||||||
|
- ext-curl
|
||||||
|
- ext-json
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd kenaito-conf-php-client-demo
|
||||||
|
php app.php
|
||||||
|
```
|
||||||
|
|
||||||
|
## 环境变量
|
||||||
|
|
||||||
|
Demo 支持以下环境变量:
|
||||||
|
|
||||||
|
| 变量名 | 说明 | 默认值 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| `KENAITO_BASE_URL` | 配置中心地址 | `http://localhost:8080` |
|
||||||
|
| `KENAITO_APP_NAME` | 应用名称 | `kenaito-springboot-demo` |
|
||||||
|
| `KENAITO_ENV` | 环境标识 | `daily` |
|
||||||
|
| `KENAITO_VERSION` | 配置版本 | `latest` |
|
||||||
|
| `KENAITO_TOKEN` | 应用 Token | 空 |
|
||||||
|
| `KENAITO_SYNC_INTERVAL` | 同步间隔(秒) | `10` |
|
||||||
|
| `KENAITO_CACHE_DIR` | 缓存目录 | `.` |
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/src/Config.php';
|
||||||
|
require_once __DIR__ . '/src/Cache.php';
|
||||||
|
require_once __DIR__ . '/src/Listener.php';
|
||||||
|
require_once __DIR__ . '/src/Client.php';
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "kenaito/conf-client",
|
||||||
|
"description": "Kenaito Config Center PHP Client",
|
||||||
|
"version": "1.0.3",
|
||||||
|
"type": "library",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "odboy",
|
||||||
|
"email": "tianjun@odboy.cn"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4",
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-json": "*"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Kenaito\\Conf\\": "src/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kenaito\Conf;
|
||||||
|
|
||||||
|
class Cache
|
||||||
|
{
|
||||||
|
private string $cacheDir;
|
||||||
|
private const CACHE_FILE = 'config-cache.json';
|
||||||
|
|
||||||
|
public function __construct(string $cacheDir = '.')
|
||||||
|
{
|
||||||
|
$this->cacheDir = $cacheDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write(array $data): bool
|
||||||
|
{
|
||||||
|
$path = $this->cacheDir . DIRECTORY_SEPARATOR . self::CACHE_FILE;
|
||||||
|
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
return file_put_contents($path, $json) !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read(): ?array
|
||||||
|
{
|
||||||
|
$path = $this->cacheDir . DIRECTORY_SEPARATOR . self::CACHE_FILE;
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$json = file_get_contents($path);
|
||||||
|
$data = json_decode($json, true);
|
||||||
|
return is_array($data) ? $data : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kenaito\Conf;
|
||||||
|
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
private Config $config;
|
||||||
|
private Cache $cache;
|
||||||
|
private Listener $listener;
|
||||||
|
private array $configs = [];
|
||||||
|
private int $lastTime = 0;
|
||||||
|
private bool $started = false;
|
||||||
|
public bool $stop = false;
|
||||||
|
|
||||||
|
public function __construct(Config $config)
|
||||||
|
{
|
||||||
|
$this->config = $config;
|
||||||
|
$this->cache = new Cache($config->cacheDir);
|
||||||
|
$this->listener = new Listener();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onChange(callable $handler): void
|
||||||
|
{
|
||||||
|
$this->listener->onChange($handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $key): ?string
|
||||||
|
{
|
||||||
|
return $this->configs[$key] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAll(): array
|
||||||
|
{
|
||||||
|
return $this->configs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function start(): void
|
||||||
|
{
|
||||||
|
if ($this->started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->started = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->fetchAll();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
error_log("[ConfigClient] fetch all configs error: " . $e->getMessage() . ", trying cache");
|
||||||
|
$cached = $this->cache->read();
|
||||||
|
if ($cached !== null) {
|
||||||
|
$this->applyConfigs($cached);
|
||||||
|
error_log("[ConfigClient] loaded " . count($this->configs) . " configs from cache");
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException("fetch configs failed and no cache: " . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stop(): void
|
||||||
|
{
|
||||||
|
$this->stop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncOnce(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->fetchChanged();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
error_log("[ConfigClient] sync error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function httpPost(string $url, array $payload): array
|
||||||
|
{
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new \RuntimeException("HTTP request failed: " . $error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($httpCode !== 200) {
|
||||||
|
throw new \RuntimeException("HTTP " . $httpCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($response, true);
|
||||||
|
if (!is_array($data)) {
|
||||||
|
throw new \RuntimeException("JSON decode error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fetchAll(): void
|
||||||
|
{
|
||||||
|
$url = $this->config->baseUrl . '/api/fetchAllConfigs';
|
||||||
|
$payload = [
|
||||||
|
'appName' => $this->config->appName,
|
||||||
|
'env' => $this->config->env,
|
||||||
|
'version' => $this->config->version,
|
||||||
|
'ignoreGlobal' => $this->config->ignoreGlobal,
|
||||||
|
'token' => $this->config->token,
|
||||||
|
];
|
||||||
|
|
||||||
|
$data = $this->httpPost($url, $payload);
|
||||||
|
|
||||||
|
if (isset($data['code']) && $data['code'] !== 0) {
|
||||||
|
throw new \RuntimeException("server error: " . ($data['msg'] ?? 'unknown'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['latest.fetch.timestamp'])) {
|
||||||
|
$this->lastTime = (int)$data['latest.fetch.timestamp'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cache->write($data);
|
||||||
|
$this->applyConfigs($data);
|
||||||
|
|
||||||
|
error_log("[ConfigClient] fetched " . count($this->configs) . " configs");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fetchChanged(): void
|
||||||
|
{
|
||||||
|
$url = $this->config->baseUrl . '/api/fetchChangedConfigs';
|
||||||
|
$payload = [
|
||||||
|
'appName' => $this->config->appName,
|
||||||
|
'env' => $this->config->env,
|
||||||
|
'version' => $this->config->version,
|
||||||
|
'lastTime' => $this->lastTime,
|
||||||
|
'ignoreGlobal' => $this->config->ignoreGlobal,
|
||||||
|
'token' => $this->config->token,
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->httpPost($url, $payload);
|
||||||
|
|
||||||
|
if (isset($result['code']) && $result['code'] !== 0) {
|
||||||
|
throw new \RuntimeException("server error code: " . $result['code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$changedData = $result['data'] ?? [];
|
||||||
|
|
||||||
|
if (isset($changedData['latest.fetch.timestamp'])) {
|
||||||
|
$this->lastTime = (int)$changedData['latest.fetch.timestamp'];
|
||||||
|
unset($changedData['latest.fetch.timestamp']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理删除的配置
|
||||||
|
if (isset($changedData['deletedKeys']) && $changedData['deletedKeys'] !== '') {
|
||||||
|
$deletedKeys = explode(',', $changedData['deletedKeys']);
|
||||||
|
foreach ($deletedKeys as $key) {
|
||||||
|
unset($this->configs[$key]);
|
||||||
|
}
|
||||||
|
error_log("[ConfigClient] " . count($deletedKeys) . " configs deleted: " . implode(', ', $deletedKeys));
|
||||||
|
unset($changedData['deletedKeys']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理变更的配置
|
||||||
|
if (!empty($changedData)) {
|
||||||
|
$changed = [];
|
||||||
|
foreach ($changedData as $key => $value) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
$this->configs[$key] = $value;
|
||||||
|
$changed[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($changed)) {
|
||||||
|
$this->listener->notify($changed);
|
||||||
|
error_log("[ConfigClient] " . count($changed) . " configs changed: " . implode(', ', array_keys($changed)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyConfigs(array $data): void
|
||||||
|
{
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if (strpos($key, 'kenaito.') === 0 || $key === 'latest.fetch.timestamp') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_string($value)) {
|
||||||
|
$this->configs[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kenaito\Conf;
|
||||||
|
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
public string $baseUrl;
|
||||||
|
public string $appName;
|
||||||
|
public string $env;
|
||||||
|
public string $version;
|
||||||
|
public string $token;
|
||||||
|
public int $syncInterval;
|
||||||
|
public bool $ignoreGlobal;
|
||||||
|
public string $cacheDir;
|
||||||
|
|
||||||
|
public function __construct(array $options = [])
|
||||||
|
{
|
||||||
|
$this->baseUrl = $options['baseUrl'] ?? 'http://localhost:8080';
|
||||||
|
$this->appName = $options['appName'] ?? '';
|
||||||
|
$this->env = $options['env'] ?? 'dev';
|
||||||
|
$this->version = $options['version'] ?? 'latest';
|
||||||
|
$this->token = $options['token'] ?? '';
|
||||||
|
$this->syncInterval = $options['syncInterval'] ?? 10;
|
||||||
|
$this->ignoreGlobal = $options['ignoreGlobal'] ?? false;
|
||||||
|
$this->cacheDir = $options['cacheDir'] ?? '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kenaito\Conf;
|
||||||
|
|
||||||
|
class Listener
|
||||||
|
{
|
||||||
|
private array $handlers = [];
|
||||||
|
|
||||||
|
public function onChange(callable $handler): void
|
||||||
|
{
|
||||||
|
$this->handlers[] = $handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notify(array $changed): void
|
||||||
|
{
|
||||||
|
foreach ($this->handlers as $handler) {
|
||||||
|
try {
|
||||||
|
$handler($changed);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
error_log("[ConfigClient] change handler error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user