Repository: multilinguals/apollo-php-client
Branch: master
Commit: 497dd462549a
Files: 7
Total size: 11.8 KB
Directory structure:
gitextract_jsdxusry/
├── .gitignore
├── README.md
├── composer.json
├── examples/
│ └── laravel/
│ ├── README.md
│ ├── apollo.php
│ └── env/
│ └── .env_tpl.php
└── src/
└── ApolloClient.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
.idea
apolloConfig.*
test.php
vendor
================================================
FILE: README.md
================================================
# [携程Apollo](https://github.com/ctripcorp/apollo)的PHP客户端
## install
php version >= 7.0
```bash
$ composer require multilinguals/apollo-client
```
php version >= 5.4 , <7.0
```bash
$ composer require multilinguals/apollo-client --ignore-platform-reqs
```
## Features
- 支持apollo配置变更的实时获取
- 支持拉取配置后自定义的回调处理
## Usage
客户端以cli的方式后台启动执行,支持apollo配置的适时获取,并将配置保存在指定的目录供应用程序读取解析
### 客户端示例代码
```php
#!/usr/bin/env php
<?php
require 'vender/autoload.php'; // autoload
use Org\Multilinguals\Apollo\Client\ApolloClient;
//specify address of apollo server
$server = getenv('CONFIG_SERVER'); // get server address from env
//specify your appid at apollo config server
$appid = getenv('APPID'); // get appid from env
//specify namespaces of appid at apollo config server
$namespaces = getenv('NAMESPACE'); // get namespaces from env
$namespaces = explode(',', $namespaces);
$apollo = new ApolloClient($server, $appid, $namespaces);
if ($clientIp = getenv('CLIENTIP')) {
$apollo->setClientIp($clientIp);
}
ini_set('memory_limit','128M');
$pid = getmypid();
echo "start [$pid]\n";
$restart = true; //auto start if failed
do {
$error = $apollo->start();
if ($error) echo('error:'.$error."\n");
}while($error && $restart);
```
### 配置管理
拉取的配置默认保存在脚本所在目录,每个namespace的配置以`apolloConfig.{$namespaceName}.php`的方式命名保存
### Docker环境客户端自启动
在docker的启动脚本中加入启动代码,一般的php容器启动脚本是docker-php-entrypoint
```bash
if [ -f "/path/to/start.php" ]; then
apollo_ps=$(ps -aux | grep -c "php /path/to/start.php")
if [ $apollo_ps -eq 1 ]; then
php /path/to/start.php &
fi
fi
```
================================================
FILE: composer.json
================================================
{
"name": "multilinguals/apollo-client",
"description": "apollo client for php",
"type": "library",
"license": "MIT",
"keywords": [
"Apollo",
"Client"
],
"homepage": "https://github.com/multilinguals/apollo-php-client",
"require": {
"php": "~7.0"
},
"autoload": {
"psr-4": {
"Org\\Multilinguals\\Apollo\\Client\\": "src/"
}
}
}
================================================
FILE: examples/laravel/README.md
================================================
### apollo client for laravel
#### 启动apollo客户端
php apollo.php
================================================
FILE: examples/laravel/apollo.php
================================================
#!/usr/bin/env php
<?php
require 'vendor/autoload.php';
use Org\Multilinguals\Apollo\Client\ApolloClient;
define('SAVE_DIR', __DIR__); //定义apollo配置本地化存储路径
//指定env模板和文件
define('ENV_DIR', __DIR__.DIRECTORY_SEPARATOR.'env');
define('ENV_TPL', ENV_DIR.DIRECTORY_SEPARATOR.'.env_tpl.php');
define('ENV_FILE', ENV_DIR.DIRECTORY_SEPARATOR.'.env');
//定义apollo配置变更时的回调函数,动态异步更新.env
$callback = function () {
$list = glob(SAVE_DIR.DIRECTORY_SEPARATOR.'apolloConfig.*');
$apollo = [];
foreach ($list as $l) {
$config = require $l;
if (is_array($config) && isset($config['configurations'])) {
$apollo = array_merge($apollo, $config['configurations']);
}
}
if (!$apollo) {
throw new Exception('Load Apollo Config Failed, no config available');
}
ob_start();
include ENV_TPL;
$env_config = ob_get_contents();
ob_end_clean();
file_put_contents(ENV_FILE, $env_config);
};
//指定apollo的服务地址
$server = 'http://127.0.0.1:8081';
//指定appid
$appid = 'demo';
//指定要拉取哪些namespace的配置
$namespaces = ['application', 'public.mysql', 'public.redis'];
$apollo = new ApolloClient($server, $appid, $namespaces);
//如果需要灰度发布,指定clientIp
/*
* $clientIp = '10.160.2.131';
* if (isset($clientIp) && filter_var($clientIp, FILTER_VALIDATE_IP)) {
* $apollo->setClientIp($clientIp);
* }
*/
//从apollo上拉取的配置默认保存在脚本目录,可自行设置保存目录
$apollo->save_dir = SAVE_DIR;
ini_set('memory_limit','128M');
$pid = getmypid();
echo "start [$pid]\n";
$restart = false; //失败自动重启
do {
$error = $apollo->start($callback); //此处传入回调
if ($error) echo('error:'.$error."\n");
}while($error && $restart);
================================================
FILE: examples/laravel/env/.env_tpl.php
================================================
<?php
echo "
DB_HOST={$apollo['mysql.url']}
DB_PORT={$apollo['mysql.port']}
DB_DATABASE={$apollo['mysql.db']}
DB_USERNAME={$apollo['mysql.user']}
DB_PASSWORD={$apollo['mysql.password']}
REDIS_HOST={$apollo['redis.url']}
REDIS_PORT={$apollo['redis.port']}
REDIS_PASSWORD={$apollo['redis.password']}
REDIS_DB={$apollo['redis.db']}
";
================================================
FILE: src/ApolloClient.php
================================================
<?php
namespace Org\Multilinguals\Apollo\Client;
class ApolloClient
{
protected $configServer; //apollo服务端地址
protected $appId; //apollo配置项目的appid
protected $cluster = 'default';
protected $clientIp = '127.0.0.1'; //绑定IP做灰度发布用
protected $notifications = [];
protected $pullTimeout = 10; //获取某个namespace配置的请求超时时间
protected $intervalTimeout = 60; //每次请求获取apollo配置变更时的超时时间
public $save_dir; //配置保存目录
/**
* ApolloClient constructor.
* @param string $configServer apollo服务端地址
* @param string $appId apollo配置项目的appid
* @param array $namespaces apollo配置项目的namespace
*/
public function __construct($configServer, $appId, array $namespaces)
{
$this->configServer = $configServer;
$this->appId = $appId;
foreach ($namespaces as $namespace) {
$this->notifications[$namespace] = ['namespaceName' => $namespace, 'notificationId' => -1];
}
$this->save_dir = dirname($_SERVER['SCRIPT_FILENAME']);
}
public function setCluster($cluster)
{
$this->cluster = $cluster;
}
public function setClientIp($ip)
{
$this->clientIp = $ip;
}
public function setPullTimeout($pullTimeout) {
$pullTimeout = intval($pullTimeout);
if ($pullTimeout < 1 || $pullTimeout > 300) {
return;
}
$this->pullTimeout = $pullTimeout;
}
public function setIntervalTimeout($intervalTimeout) {
$intervalTimeout = intval($intervalTimeout);
if ($intervalTimeout < 1 || $intervalTimeout > 300) {
return;
}
$this->intervalTimeout = $intervalTimeout;
}
private function _getReleaseKey($config_file) {
$releaseKey = '';
if (file_exists($config_file)) {
$last_config = require $config_file;
is_array($last_config) && isset($last_config['releaseKey']) && $releaseKey = $last_config['releaseKey'];
}
return $releaseKey;
}
//获取单个namespace的配置文件路径
public function getConfigFile($namespaceName) {
return $this->save_dir.DIRECTORY_SEPARATOR.'apolloConfig.'.$namespaceName.'.php';
}
//获取单个namespace的配置-无缓存的方式
public function pullConfig($namespaceName) {
$base_api = rtrim($this->configServer, '/').'/configs/'.$this->appId.'/'.$this->cluster.'/';
$api = $base_api.$namespaceName;
$args = [];
$args['ip'] = $this->clientIp;
$config_file = $this->getConfigFile($namespaceName);
$args['releaseKey'] = $this->_getReleaseKey($config_file);
$api .= '?' . http_build_query($args);
$ch = curl_init($api);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->pullTimeout);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$body = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($httpCode == 200) {
$result = json_decode($body, true);
$content = '<?php return ' . var_export($result, true) . ';';
file_put_contents($config_file, $content);
}elseif ($httpCode != 304) {
echo $body ?: $error."\n";
return false;
}
return true;
}
//获取多个namespace的配置-无缓存的方式
public function pullConfigBatch(array $namespaceNames) {
if (! $namespaceNames) return [];
$multi_ch = curl_multi_init();
$request_list = [];
$base_url = rtrim($this->configServer, '/').'/configs/'.$this->appId.'/'.$this->cluster.'/';
$query_args = [];
$query_args['ip'] = $this->clientIp;
foreach ($namespaceNames as $namespaceName) {
$request = [];
$config_file = $this->getConfigFile($namespaceName);
$request_url = $base_url.$namespaceName;
$query_args['releaseKey'] = $this->_getReleaseKey($config_file);
$query_string = '?'.http_build_query($query_args);
$request_url .= $query_string;
$ch = curl_init($request_url);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->pullTimeout);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$request['ch'] = $ch;
$request['config_file'] = $config_file;
$request_list[$namespaceName] = $request;
curl_multi_add_handle($multi_ch, $ch);
}
$active = null;
// 执行批处理句柄
do {
$mrc = curl_multi_exec($multi_ch, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($multi_ch) == -1) {
usleep(100);
}
do {
$mrc = curl_multi_exec($multi_ch, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
// 获取结果
$response_list = [];
foreach ($request_list as $namespaceName => $req) {
$response_list[$namespaceName] = true;
$result = curl_multi_getcontent($req['ch']);
$code = curl_getinfo($req['ch'], CURLINFO_HTTP_CODE);
$error = curl_error($req['ch']);
curl_multi_remove_handle($multi_ch,$req['ch']);
curl_close($req['ch']);
if ($code == 200) {
$result = json_decode($result, true);
$content = '<?php return '.var_export($result, true).';';
file_put_contents($req['config_file'], $content);
}elseif ($code != 304) {
echo 'pull config of namespace['.$namespaceName.'] error:'.($result ?: $error)."\n";
$response_list[$namespaceName] = false;
}
}
curl_multi_close($multi_ch);
return $response_list;
}
protected function _listenChange(&$ch, $callback = null) {
$base_url = rtrim($this->configServer, '/').'/notifications/v2?';
$params = [];
$params['appId'] = $this->appId;
$params['cluster'] = $this->cluster;
do {
$params['notifications'] = json_encode(array_values($this->notifications));
$query = http_build_query($params);
curl_setopt($ch, CURLOPT_URL, $base_url.$query);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
$error = curl_error($ch);
if ($httpCode == 200) {
$res = json_decode($response, true);
$change_list = [];
foreach ($res as $r) {
if ($r['notificationId'] != $this->notifications[$r['namespaceName']]['notificationId']) {
$change_list[$r['namespaceName']] = $r['notificationId'];
}
}
$response_list = $this->pullConfigBatch(array_keys($change_list));
foreach ($response_list as $namespaceName => $result) {
$result && ($this->notifications[$namespaceName]['notificationId'] = $change_list[$namespaceName]);
}
//如果定义了配置变更的回调,比如重新整合配置,则执行回调
($callback instanceof \Closure) && call_user_func($callback);
}elseif ($httpCode != 304) {
throw new \Exception($response ?: $error);
}
}while (true);
}
/**
* @param $callback 监听到配置变更时的回调处理
* @return mixed
*/
public function start($callback = null) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, $this->intervalTimeout);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
try {
$this->_listenChange($ch, $callback);
}catch (\Exception $e) {
curl_close($ch);
return $e->getMessage();
}
}
}
gitextract_jsdxusry/
├── .gitignore
├── README.md
├── composer.json
├── examples/
│ └── laravel/
│ ├── README.md
│ ├── apollo.php
│ └── env/
│ └── .env_tpl.php
└── src/
└── ApolloClient.php
SYMBOL INDEX (12 symbols across 1 files)
FILE: src/ApolloClient.php
class ApolloClient (line 5) | class ApolloClient
method __construct (line 22) | public function __construct($configServer, $appId, array $namespaces)
method setCluster (line 32) | public function setCluster($cluster)
method setClientIp (line 37) | public function setClientIp($ip)
method setPullTimeout (line 42) | public function setPullTimeout($pullTimeout) {
method setIntervalTimeout (line 50) | public function setIntervalTimeout($intervalTimeout) {
method _getReleaseKey (line 58) | private function _getReleaseKey($config_file) {
method getConfigFile (line 68) | public function getConfigFile($namespaceName) {
method pullConfig (line 73) | public function pullConfig($namespaceName) {
method pullConfigBatch (line 106) | public function pullConfigBatch(array $namespaceNames) {
method _listenChange (line 168) | protected function _listenChange(&$ch, $callback = null) {
method start (line 204) | public function start($callback = null) {
Condensed preview — 7 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (14K chars).
[
{
"path": ".gitignore",
"chars": 83,
"preview": "# Created by .ignore support plugin (hsz.mobi)\n.idea\napolloConfig.*\ntest.php\nvendor"
},
{
"path": "README.md",
"chars": 1575,
"preview": "# [携程Apollo](https://github.com/ctripcorp/apollo)的PHP客户端\n\n## install\nphp version >= 7.0\n```bash\n$ composer require multi"
},
{
"path": "composer.json",
"chars": 375,
"preview": "{\n \"name\": \"multilinguals/apollo-client\",\n \"description\": \"apollo client for php\",\n \"type\": \"library\",\n \"license\": \""
},
{
"path": "examples/laravel/README.md",
"chars": 62,
"preview": "### apollo client for laravel\n\n#### 启动apollo客户端\nphp apollo.php"
},
{
"path": "examples/laravel/apollo.php",
"chars": 1643,
"preview": "#!/usr/bin/env php\n<?php\nrequire 'vendor/autoload.php';\nuse Org\\Multilinguals\\Apollo\\Client\\ApolloClient;\n\ndefine('SAVE_"
},
{
"path": "examples/laravel/env/.env_tpl.php",
"chars": 333,
"preview": "<?php\necho \"\nDB_HOST={$apollo['mysql.url']}\nDB_PORT={$apollo['mysql.port']}\nDB_DATABASE={$apollo['mysql.db']}\nDB_USERNAM"
},
{
"path": "src/ApolloClient.php",
"chars": 7965,
"preview": "<?php\n\nnamespace Org\\Multilinguals\\Apollo\\Client;\n\nclass ApolloClient\n{\n protected $configServer; //apollo服务端地址\n p"
}
]
About this extraction
This page contains the full source code of the multilinguals/apollo-php-client GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 7 files (11.8 KB), approximately 3.4k tokens, and a symbol index with 12 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.