华为云域名云解析服务(DNS)API接口PHP封装
目前互联网上提供免费的域名托管服务的厂家很多,如阿里云、腾讯云、CloudFlare、华为云等等,我们将域名托管到这些云解析DNS服务商后,虽然服务商都提供了控制台用于域名及解析记录的管理,但是有时候如果我们需要自己实现DDNS动态域名解析等一些基于官网API接口能力的功能,就需要使用到官网提供的API能力,本文将针对华为云的API接口,通过PHP封装了常用的函数类,用于托管域名、记录集等的管理。 华为云DNS API采用RESTful风格,支持Token和AK/SK两种鉴权方式,本文采用Token方式实现,旨在帮助开发者快速使用集成华为云云解析服务(DNS)的API接口,实现对域名(Zone)和解析记录(Record Set)的自动化管理,如域名托管、记录添加及修改等。 华为云API官方文档地址详见:https://support.huaweicloud.com/api-dns/dns_api_10000.html 下面是PHP实现的API封装类,代码如下:
/**
* 华为云 DNS API 管理类 (支持公网域名、记录集管理及智能 DDNS 更新)
* 适配华为云 API 版本: v2 v2.1
* 官网API文档地址 https://support.huaweicloud.com/api-dns/dns_api_10000.html
* 引用或者使用本文,请注明来源:楚狐在线- https://chuhu.org
*/
class DNSHuaweiCloud
{
// 华为云凭证配置
// 下面的用户名和密码,您可以登录华为云控制台后,点击右上角您的用户名,选择下方【我的凭证】页面,获取IAM用户名和账号名,项目ID和区域在项目列表中选择即可。
private $iam_username = '你的IAM用户名'; //IAM用户名
private $iam_password = '你的华为登录密码'; //IAM密码
private $domain_name = '账号名'; //您的主账号名(通常与用户名相同)
private $project_id = '05a61f7319800fc32f5ac00ebb63bd11'; //您的项目ID(例如: cn-north-4的project_id)
private $region_id = 'cn-north-4'; // 华为云区域 新加坡ap-southeast-3 香港ap-southeast-1
private $token = null; //缓存的 IAM Token 字符串
private $token_expires = 0; //Token 的过期时间戳
protected static $obj = null; //self|null 单例对象存储
/**
* 单例模式获取当前类实例
* @return self
*/
public static function Obj ()
{
if (is_null(static::$obj)) {
self::$obj = new self();
}
return self::$obj;
}
/**
* 构造函数 (支持传入参数切换环境)
* @param string|null $para 环境标识,传入 "OTHER_ACCOUNT" 可自动切换为预设环境
*/
protected function __construct($para = null) {
if ($para === "OTHER_ACCOUNT") {
$this->iam_username = 'OTHER_ACCOUNT_USER';
$this->iam_password = 'OTHER_ACCOUNT_PASSWORD';
$this->domain_name = 'OTHER_ACCOUNT_DOMAIN';
$this->project_id = '05a61f73088025712f70c00e9cea1cd1';
$this->region_id = 'ap-southeast-3';
}
}
/**
* 获取临时 IAM Token 认证串
* 内部逻辑包含缓存机制,未过期时直接返回缓存 Token
* @return string|null 成功返回 Token 字符串,失败返回 null
*/
private function getAuthToken()
{
if ($this->token && time() < $this->token_expires) {
return $this->token;
}
$url = "https://iam.myhuaweicloud.com/v3/auth/tokens";
$payload = [
"auth" => [
"identity" => [
"methods" => ["password"],
"password" => [
"user" => [
"name" => $this->iam_username,
"password" => $this->iam_password,
"domain" => ["name" => $this->domain_name]
]
]
],
"scope" => [
"project" => ["id" => $this->project_id]
]
]
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $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_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
$headers = substr($response, 0, $header_size);
if (preg_match('/X-Subject-Token:\s*([^\r\n]+)/i', $headers, $matches)) {
$this->token = trim($matches[1]);
$this->token_expires = time() + 3600; // 缓存1小时
return $this->token;
}
return null;
}
// =========================================================================
// 一、公网域名管理
// =========================================================================
/**
* 1. 列出当前项目下的所有公网域名
* 华为云 API: GET /v2/zones?type=public
* @param string|null $zone_id 选填。若传入特定 ZoneID 则直接转换为查询该单个域名的详情
* @return array 统一处理后的域名列表数组(过滤清除了华为云官方尾部的点号)
*/
public function DomainListAll($zone_id = null)
{
if (!empty($zone_id)) {
return $this->ZoneDetailQuery($zone_id);
}
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2/zones?type=public&limit=100";
$rtn = $this->request_post($url, "GET");
$final_list = array();
if (isset($rtn['zones']) && is_array($rtn['zones'])) {
foreach ($rtn['zones'] as $oneZone) {
$final_list[] = array(
'domain' => strtolower(rtrim($oneZone['name'], '.')),
'id' => $oneZone['id'],
'status' => ($oneZone['status'] === 'ACTIVE') ? 'active' : 'pending'
);
}
}
return $final_list;
}
/**
* 2. 查询特定域名对应的 zone_id
* 华为云 API: GET /v2/zones?name=...&type=public
* @param string $domain 域名名称,例如 "example.com" 或 "example.com."
* @return string|null 成功返回对应的 zone_id 字符串,未找到返回 null
*/
public function DomainIDQuery($domain)
{
$search_name = rtrim(strtolower($domain), '.') . '.';
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2/zones?type=public&name=" . urlencode($search_name);
$rtn = $this->request_post($url, "GET");
if (!empty($rtn['zones']) && isset($rtn['zones'][0]['id'])) {
return $rtn['zones'][0]['id'];
}
return null;
}
/**
* 3. 查询单个公网域名详细配置信息
* 华为云 API: GET /v2/zones/{zone_id}
* @param string $zone_id 华为云主域名区域 ID
* @return array 包含域名状态、名称服务器(NS)等信息的元数组
*/
public function ZoneDetailQuery($zone_id)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2/zones/{$zone_id}";
return $this->request_post($url, "GET");
}
/**
* 4. 创建新的公网域名
* 华为云 API: POST /v2/zones
* @param string $domain 欲注册的主域名,例如 "my-new-domain.com"
* @param string $comment 域名的描述/备注信息
* @return array 统一的业务层响应结构 ['success' => bool, 'result' => array, 'errors' => array]
*/
public function CreateZone($domain, $comment = "")
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2/zones";
$zone_name = rtrim(strtolower($domain), '.') . '.';
$data = array(
'name' => $zone_name,
'zone_type' => 'public',
'description' => $comment,
'ttl' => 300
);
$rtn = $this->request_post($url, "POST", $data);
$success = isset($rtn['id']);
return [
'success' => $success,
'result' => $rtn,
'errors' => $success ? [] : [['code' => $rtn['code'] ?? 500, 'message' => $rtn['message'] ?? 'Create Zone Failed']]
];
}
/**
* 5. 删除指定的公网域名
* 华为云 API: DELETE /v2/zones/{zone_id}
* @param string $zone_id 欲删除域名的 zone_id
* @return array 统一的业务层响应结构,包含成功标识
*/
public function DeleteZone($zone_id)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2/zones/{$zone_id}";
$rtn = $this->request_post($url, "DELETE");
$success = !isset($rtn['code']);
return [
'success' => $success,
'errors' => $success ? [] : [['code' => $rtn['code'], 'message' => $rtn['message']]]
];
}
/**
* 6. 修改公网域名的描述/备注信息
* 华为云 API: PATCH /v2/zones/{zone_id}
* @param string $zone_id 欲修改域名的 zone_id
* @param string $comment 新的描述/备注内容
* @return array 华为云原始接口返回的修改后域名数据明细
*/
public function EditZoneDescription($zone_id, $comment)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2/zones/{$zone_id}";
$data = array(
'description' => $comment
);
return $this->request_post($url, "PATCH", $data);
}
// =========================================================================
// 二、基础/单条域名解析记录集管理
// =========================================================================
/**
* 1. 获取某个托管域名(Zone)下的全量解析记录集列表
* 查询域名下的记录集列表 - ShowRecordSetByZone
* 华为云 API: GET /v2/zones/{zone_id}/recordsets
* @param string $zone_id 域名的 zone_id
* @return array 适配通用网关的清洗后列表数据(去除了TXT双引号及主机名尾部的点号)
*/
public function DNSRecordsList($zone_id)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets?limit=500";
$rtn = $this->request_post($url, "GET");
$cf_format = ['success' => isset($rtn['recordsets']), 'result' => [], 'errors' => []];
if ($cf_format['success']) {
foreach ($rtn['recordsets'] as $rs) {
foreach ($rs['records'] as $val) {
$cf_format['result'][] = [
'id' => $rs['id'],
'zone_id' => $rs['zone_id'],
'zone_name' => $rs['zone_name'],
'name' => rtrim($rs['name'], '.'),
'type' => $rs['type'],
'status' => $rs['status'],
'created_at' => $rs['created_at'] ?? '',
'updated_at' => $rs['updated_at'] ?? '',
'content' => trim($val, '"'),
'ttl' => $rs['ttl']
];
}
}
} else {
$cf_format['errors'][] = ['code' => $rtn['code'] ?? 500, 'message' => $rtn['message'] ?? 'Unknown Error'];
}
return $cf_format;
}
/**
* 2. 查询单条特定解析记录的详细数据
* 查询记录集 - ShowRecordSetWithLine
* 华为云 API: GET /v2/zones/{zone_id}/recordsets/{recordset_id}
* @param string $zone_id 域名的 zone_id
* @param string $dns_record_id 解析记录的唯一标识 ID
* @return array 格式化后的单条记录明细
*/
public function DNSRecordDetails($zone_id, $dns_record_id)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets/{$dns_record_id}";
$rtn = $this->request_post($url, "GET");
$cf_format = ['success' => isset($rtn['id']), 'result' => [], 'errors' => []];
if ($cf_format['success']) {
$cf_format['result'] = [
'id' => $rtn['id'],
'name' => rtrim($rtn['name'], '.'),
'type' => $rtn['type'],
'status' => $rtn['status'],
'content' => isset($rtn['records'][0]) ? trim($rtn['records'][0], '"') : '',
'ttl' => $rtn['ttl']
];
} else {
$cf_format['errors'][] = ['code' => $rtn['code'] ?? 500, 'message' => $rtn['message'] ?? 'Unknown Error'];
}
return $cf_format;
}
/**
* 3. 在特定域名下创建单条解析记录
* 华为云 API: POST /v2/zones/{zone_id}/recordsets
* @param string $zone_id 域名的 zone_id
* @param string $domain_name 完整的子域名,例如 "www.example.com"
* @param string $content 记录集的值 (如 IP 地址、别名指针等)
* @param string $type 记录类型: "A", "AAAA", "CNAME", "TXT", "MX" 等
* @param int $ttl 缓存生存时间 (秒),DDNS 建议设置为 60
* @param string $comment 记录集的描述备注
* @return array 结果响应数组,成功时返回新生成的记录 ID
*/
public function CreateDNSRecord($zone_id, $domain_name, $content, $type="A", $ttl=60, $comment="")
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets";
$domain_name = rtrim($domain_name, '.') . '.';
$type = strtoupper($type);
if (in_array($type, ['TXT', 'SPF', 'CAA'])) {
$content = '"' . trim($content, '"') . '"';
}
$data = [
'name' => $domain_name,
'description' => $comment,
'type' => $type,
'ttl' => intval($ttl),
'records' => [$content]
];
$rtn = $this->request_post($url, "POST", $data);
$success = isset($rtn['id']);
return [
'success' => $success,
'result' => $success ? ['id' => $rtn['id']] : [],
'errors' => $success ? [] : [['code' => $rtn['code'] ?? 500, 'message' => $rtn['message'] ?? 'Create Failed']]
];
}
/**
* 4. 更新单条已有解析记录的值或 TTL
* 修改记录集 - UpdateRecordSets
* 华为云 API: PUT /v2.1/zones/{zone_id}/recordsets/{recordset_id}
* @param string $zone_id 域名的 zone_id
* @param string $dns_record_id 解析记录的唯一标识 ID
* @param string $domain_name 完整的子域名值
* @param string $new_ip 新的解析目标记录值(如新公网 IP)
* @param string $type 记录类型 ("A"/"AAAA"/"CNAME"等)
* @param int $ttl 新的缓存生存时间 (秒)
* @param string $comment 记录集的描述备注
* @return array 结果响应数组
*/
public function UpdateDNSRecord($zone_id, $dns_record_id, $domain_name, $new_ip, $type, $ttl, $comment="")
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets/{$dns_record_id}";
$type = strtoupper($type);
if (in_array($type, ['TXT', 'SPF', 'CAA'])) {
$new_ip = '"' . trim($new_ip, '"') . '"';
}
$data = [
'name' => $domain_name.".",
'ttl' => intval($ttl),
'type' => $type,
'description' => $comment,
'records' => [$new_ip]
];
$rtn = $this->request_post($url, "PUT", $data);
$success = isset($rtn['id']);
return [
'success' => $success,
'result' => $success ? ['id' => $rtn['id']] : [],
'errors' => $success ? [] : [['code' => $rtn['code'] ?? 500, 'message' => $rtn['message'] ?? 'Update Failed']]
];
}
/**
* 5. 删除单条特定的解析记录
* 华为云 API: DELETE /v2.1/zones/{zone_id}/recordsets/{recordset_id}
* @param string $zone_id 域名的 zone_id
* @param string $dns_record_id 解析记录的唯一标识 ID
* @return array 成功标识结构
*/
public function DeleteDNSRecord($zone_id, $dns_record_id)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets/{$dns_record_id}";
$rtn = $this->request_post($url, "DELETE");
$success = !isset($rtn['code']);
return [
'success' => $success,
'errors' => $success ? [] : [['code' => $rtn['code'], 'message' => $rtn['message']]]
];
}
/**
* 获取子域名的记录集ID
* 通过主机记录(子域名前缀)和记录类型,从当前zone中查找对应的记录集ID
*
* @param string $zone_id 域名的 zone_id
* @param string $sub_domain 子域名前缀,如 "www"、"api"、"@"
* @param string $type 记录类型,如 "A"、"AAAA"、"CNAME"、"MX"、"TXT" 等
* @return array 返回结果,格式:['success' => 1, 'record_id' => string|null, 'message' => string, 'matched_count' => 2, 'all_matches' => array(详细的记录列表)]
*
* @example
* // 获取 www 的 A 记录ID
* $result = DNSHuaweiCloud::Obj()->GetDNSRecordId($zone_id, 'www', 'A');
* if ($result['success']) {
* echo "记录集ID: " . $result['record_id'];
* }
*
* @example
* // 获取主域名 @ 的 MX 记录ID
* $result = DNSHuaweiCloud::Obj()->GetDNSRecordId($zone_id, '@', 'MX');
*/
//返回record_id字符串
public function GetDNSRecordIds($zone_id, $sub_domain, $type = 'A')
{
$result = $this->GetDNSRecordId($zone_id, $sub_domain, $type);
return $result['record_id'];
}
//返回完整的数组
public function GetDNSRecordId($zone_id, $sub_domain, $type = 'A')
{
// 1. 参数验证
if (empty($zone_id)) {
return [
'success' => false,
'record_id' => null,
'message' => 'zone_id 不能为空'
];
}
if (empty($sub_domain)) {
return [
'success' => false,
'record_id' => null,
'message' => '子域名不能为空'
];
}
$type = strtoupper($type);
// 2. 获取当前zone下的所有记录集
$list_result = $this->DNSRecordsList($zone_id);
if (!$list_result['success']) {
return [
'success' => false,
'record_id' => null,
'message' => '获取记录集列表失败:' . ($list_result['errors'][0]['message'] ?? '未知错误')
];
}
// 3. 标准化子域名进行匹配
// 处理 @ 符号(表示主域名)
if ($sub_domain === '@') {
// 先获取zone的域名
$zone_detail = $this->ZoneDetailQuery($zone_id);
$zone_name = rtrim($zone_detail['name'] ?? '', '.');
$search_name = $zone_name; // 主域名不带点
} else {
$search_name = rtrim($sub_domain, '.');
}
// 4. 遍历查找匹配的记录
$matched_records = [];
foreach ($list_result['result'] as $record) {
// DNSRecordsList 返回的 name 已经去掉了末尾的点
$record_name = $record['name'];
$record_type = $record['type'];
// 精确匹配:记录名称和类型都要相同
if ($record_name === $search_name && $record_type === $type) {
$matched_records[] = $record;
}
}
// 5. 处理匹配结果
if (empty($matched_records)) {
return [
'success' => false,
'record_id' => null,
'message' => "未找到记录集:子域名 '{$sub_domain}' 类型 '{$type}'"
];
}
if (count($matched_records) > 1) {
// 如果存在多条匹配(比如相同主机记录、相同类型,但不同解析线路)
// 返回第一条,同时给出提示
return [
'success' => true,
'record_id' => $matched_records[0]['id'],
'message' => "找到 " . count($matched_records) . " 条匹配记录,返回第一条(默认线路)。如需指定线路,请使用 GetRecordSetIdByLine 方法。",
'matched_count' => count($matched_records),
'all_matches' => $matched_records
];
}
return [
'success' => true,
'record_id' => $matched_records[0]['id'],
'message' => '成功获取记录集ID'
];
}
// =========================================================================
// 三、新增:高级批量解析记录集管理(暂时无法有效)
// =========================================================================
/**
* 1. 批量创建解析记录 : 批量创建记录集 - BatchCreateRecordSetsTask
* 华为云 API: POST /v2.1/zones/{zone_id}/recordsets/batch-create-task
* @param string $zone_id 域名的 zone_id
* @param array $records_array 包含多条记录的二维数组。格式见下方注释。
* @return array 统一的业务层响应结构
* * $records_array 示例:
* [
* ['name' => 'CS2','records' => ['192.168.31.101', '192.168.111.101'],'type' => 'A','ttl' => 600,'description' => '批量测试2'],
* ['name' => 'txt2','records' => ['000000000000000'],'type' => 'TXT','ttl' => 600,'description' => '批量测试21']
* ]
*/
public function BatchCreateDNSRecords($zone_id, $records_array)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets/batch-create-task";
$recordsets = [];
foreach ($records_array as $item) {
$type = strtoupper($item['type'] ?? 'A');
$records = (array)$item['records'];
// TXT/SPF/CAA 类型自动添加双引号
if (in_array($type, ['TXT', 'SPF', 'CAA'])) {
$records = array_map(function($record) {
// 如果值没有被双引号包裹,则自动添加
if (!preg_match('/^".*"$/', $record)) {
return '"' . $record . '"';
}
return $record;
}, $records);
}
$recordsets[] = [
'name' => $item['name'],
'type' => $type,
'ttl' => intval($item['ttl'] ?? 60),
'records' => $records,
'weight' => intval($item['weight'] ?? 1),
'description' => $item['description'] ?? ''
];
}
$data = ['recordsets' => $recordsets];
$rtn = $this->request_post($url, "POST", $data);
// 正确的判断:成功响应中应该包含 task_id
$success = isset($rtn['task_id']);
return [
'success' => $success,
'task_id' => $rtn['task_id'] ?? null, // 返回任务ID
'result' => $success ? $rtn : [],
'errors' => $success ? [] : [['code' => $rtn['code'] ?? 500, 'message' => $rtn['message'] ?? 'Batch Create Failed']]
];
}
/**
* 2. 批量删除解析记录 : 批量删除域名下的记录集 - BatchDeleteRecordSetWithLine
* 华为云 API: DELETE /v2.1/zones/{zone_id}/recordsets
* @param string $zone_id 域名的 zone_id
* @param array $dns_record_ids 解析记录的唯一标识 ID 数组
* @return array 统一的业务层响应结构
* * $dns_record_ids 示例: ['recordset_id_1', 'recordset_id_2']
* $dns_record_ids = ['ff8080829e038c30019eaa9c832b12b9','ff8080829e038c30019eaa9c837a12bc'];
*/
public function BatchDeleteDNSRecords($zone_id, $dns_record_ids)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets";
if (empty($dns_record_ids) || !is_array($dns_record_ids)) {
return ['success' => false, 'errors' => [['code' => 400, 'message' => '参数不能为空且必须是数组']]];
}
$data = [
'recordset_ids' => $dns_record_ids
];
$rtn = $this->request_post($url, "DELETE", $data);
print_r($rtn);
$success = !isset($rtn['code']);
return [
'success' => $success,
'errors' => $success ? [] : [['code' => $rtn['code'], 'message' => $rtn['message']]]
];
}
/**
* 3. 批量修改记录集 : 批量修改记录集 - BatchUpdateRecordSetWithLine
* 华为云 API: DELETE /v2.1/zones/{zone_id}/recordsets
*
* 原子性操作:请求记录集将全部完成修改,或不做任何修改。
* 仅公网域名支持,单次最多支持50个记录集。
*
* @param string $zone_id 域名的 zone_id
* @param array $recordsets_array 记录集数组,每个元素包含以下字段:
* - id (string, 必填) : 记录集ID(必须已存在)
* - records (array, 必填) : 解析记录值数组
* - ttl (int, 可选) : 缓存时间(秒),默认300,范围1~2147483647
* - weight (int, 可选) : 权重0~1000。当weight=null表示不设置权重,weight=0表示备用域名
* - description (string, 可选) : 记录集描述,最长255字符
* @return array 统一响应结构:
* - success (bool) : 是否成功提交任务
* - result (array) : 修改后的记录集列表
* - errors (array) : 错误详情
*
* @example
* $recordsets = [
* [
* 'id' => 'ff8080829e17f4a6019eaaa01c960223',
* 'records' => ['111.168.11.111', '222.168.12.212'],
* 'ttl' => 600,
* 'description' => '批量修改测试11'
* ],
*
* [
* 'id' => 'ff8080829e038c30019eaa9c83c912c0',
* 'records' => ['dddddddddd'],
* 'ttl' => 600,
* 'description' => '批量修改测试33'
* ]
* ];
* $result = DNSHuaweiCloud::Obj()->BatchUpdateRecordSets($zone_id, $recordsets);
*/
public function BatchUpdateDNSRecords($zone_id, $recordsets_array)
{
// 1. 参数验证
if (empty($zone_id)) {
return [
'success' => false,
'result' => [],
'errors' => [['code' => 400, 'message' => 'zone_id 不能为空']]
];
}
if (empty($recordsets_array) || !is_array($recordsets_array)) {
return [
'success' => false,
'result' => [],
'errors' => [['code' => 400, 'message' => 'recordsets_array 参数必须为非空数组']]
];
}
// 单次最多支持50个记录集
if (count($recordsets_array) > 50) {
return [
'success' => false,
'result' => [],
'errors' => [['code' => 400, 'message' => '单次最多支持修改50个记录集']]
];
}
// 2. 构建请求体
$recordsets = [];
foreach ($recordsets_array as $index => $item) {
// 验证必填字段
if (empty($item['id'])) {
return [
'success' => false,
'result' => [],
'errors' => [['code' => 400, 'message' => "第 " . ($index + 1) . " 条记录集缺少必填字段 id"]]
];
}
if (empty($item['records']) || !is_array($item['records'])) {
return [
'success' => false,
'result' => [],
'errors' => [['code' => 400, 'message' => "第 " . ($index + 1) . " 条记录集的 records 必须为非空数组"]]
];
}
$recordset = [
'id' => $item['id'],
'records' => (array)$item['records']
];
// 可选字段(仅在提供时添加)
if (isset($item['ttl'])) {
$recordset['ttl'] = (int)$item['ttl'];
}
if (isset($item['weight'])) {
$recordset['weight'] = (int)$item['weight'];
}
if (isset($item['description']) && $item['description'] !== '') {
$recordset['description'] = $item['description'];
}
// 注意:name, type, line 字段在批量修改接口中不支持修改,会被忽略
$recordsets[] = $recordset;
}
// 3. 构建请求URL和发送请求
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets";
$data = ['recordsets' => $recordsets];
$rtn = $this->request_post($url, "PUT", $data);
// 4. 处理响应
// 成功响应包含 recordsets 数组(HTTP 202)
$success = isset($rtn['recordsets']) && is_array($rtn['recordsets']);
return [
'success' => $success,
'result' => $success ? $rtn['recordsets'] : [],
'errors' => $success ? [] : [['code' => $rtn['code'] ?? 500, 'message' => $rtn['message'] ?? 'Batch Update Failed']]
];
}
/**
* 4. 查询批量创建记录集任务状态
* @param string $zone_id 域名ID
* @param string $task_id 任务ID
* @return array
*/
public function QueryBatchTaskStatus($zone_id, $task_id)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets/batch-create-task?task_id={$task_id}";
$rtn = $this->request_post($url, "GET");
if (isset($rtn['status'])) {
return [
'success' => true,
'status' => $rtn['status'], // PENDING 或 DONE
'success_count' => $rtn['success_count'] ?? 0,
'error_count' => $rtn['error_count'] ?? 0,
'error_items' => $rtn['error_items'] ?? [] // 失败详情
];
}
return [
'success' => false,
'message' => $rtn['message'] ?? 'Query failed'
];
}
/**
* 5. 删除批量创建记录集任务 - 释放任务锁(在执行批量失败的适合,可以先执行下该函数)
* 华为云 API: DELETE /v2.1/zones/{zone_id}/recordsets/batch-create-task
*
* @param string $zone_id 域名的 zone_id
* @return array 统一响应结构:['success' => bool, 'errors' => array]
*/
public function DeleteBatchTask($zone_id)
{
$url = "https://dns.{$this->region_id}.myhuaweicloud.com/v2.1/zones/{$zone_id}/recordsets/batch-create-task";
$rtn = $this->request_post($url, "DELETE");
// 成功删除时返回空数组(HTTP 204)
$success = empty($rtn) || !isset($rtn['code']);
return [
'success' => $success,
'errors' => $success ? [] : [['code' => $rtn['code'] ?? 500, 'message' => $rtn['message'] ?? 'Delete Batch Task Failed']]
];
}
// =========================================================================
// 四、核心业务集成函数 (Advanced DDNS Smart Engine)
// =========================================================================
/**
* 智能 DDNS 动态域名一键式调度引擎
* 自动处理完整闭环逻辑:扫描云端记录 -> 不存在执行创建 -> 存在且IP改变执行更新 -> 相同则静默跳过
* @param string $domain 华为云托管公网域名,如:XXX.COM
* @param string $sub_domain 子域名前缀 (如 "nas")
* @param string $current_ip 当前由网关或本地探测到的最新外部公网 IP 地址
* @param string $type 记录模式 (默认为IPv4的 "A" 记录,若使用IPv6请传入 "AAAA")
* @param boolean $force 强制更新 true,默认更新false
* @param int $ttl 缓存生存期,DDNS 业务强烈建议锁死 60 秒,保证最快生效速度
* @param string $comment 记录集的描述信息
* @return array 规范后的统一操作反馈汇报:['success' => 布尔, 'action' => 'skip|create|update|error', 'message' => '明文日志信息']
*/
public function DynamicDNS($domain, $sub_domain, $current_ip, $type = "A", $force = false, $ttl = 60, $comment = "DDNSAutoUpdate")
{
//通过主域名查询$zone_id
$zone_id = $this->DomainIDQuery($domain);
$current_ip = trim($current_ip);
$type = strtoupper($type);
// 格式化域名对齐规范
$domain_name = $sub_domain.".".$domain;
$search_name = rtrim(strtolower($domain_name), '.');
// 1. 获取当前 Zone 下的所有解析记录
$list_res = $this->DNSRecordsList($zone_id);
if (!$list_res['success']) {
return [
'success' => false,
'action' => 'error',
'message' => '获取云端解析记录列表失败:' . ($list_res['errors'][0]['message'] ?? 'Unknown')
];
}
$target_record = null;
// 2. 遍历查找是否存在匹配的记录(主机名和类型均相同)
foreach ($list_res['result'] as $record) {
if ($record['name'] === $search_name && strtoupper($record['type']) === $type) {
$target_record = $record;
break;
}
}
// 3. 根据比对结果执行对应操作
if ($target_record === null) {
// 情况 A:不存在该记录 -> 执行【创建】
if($comment=="DDNSAutoUpdate"){
$comment = "创建DDNS记录 ".date("Y-m-d H:i:s");
}
$create_res = $this->CreateDNSRecord($zone_id, $domain_name, $current_ip, $type, $ttl, $comment);
if ($create_res['success']) {
return [
'success' => true,
'action' => 'create',
'message' => "成功创建 DDNS 记录 [{$domain_name}] -> [{$current_ip}]"
];
} else {
return [
'success' => false,
'action' => 'create_error',
'message' => '创建 DDNS 记录失败:' . ($create_res['errors'][0]['message'] ?? 'Unknown')
];
}
} else {
// 情况 B:存在该记录,比对 IP(TXT等记录在 DNSRecordsList 内部已清洗掉双引号)
$cloud_ip = $target_record['content'];
if ($cloud_ip === $current_ip && !$force) {
// 情况 B-1:IP 相同 -> 【跳过】不请求华为云,防止触发 API 频率限制
return [
'success' => true,
'action' => 'skip',
'message' => "IP 未发生变化 ({$current_ip}),跳过更新。"
];
} else {
// 情况 B-2:IP 不同 -> 执行【更新】
if($comment=="DDNSAutoUpdate"){
$comment = "更新DDNS记录 ".date("Y-m-d H:i:s");
}
$update_res = $this->UpdateDNSRecord($zone_id, $target_record['id'], $domain_name, $current_ip, $type, $ttl, $comment);
if ($update_res['success']) {
return [
'success' => true,
'action' => 'update',
'message' => "成功更新 DDNS 记录 [{$domain_name}]: 由 [{$cloud_ip}] 变更为 [{$current_ip}]"
];
} else {
return [
'success' => false,
'action' => 'update_error',
'message' => '更新 DDNS 记录失败:' . ($update_res['errors'][0]['message'] ?? 'Unknown')
];
}
}
}
}
/**
* 智能 DDNS 动态域名一键式调度引擎
* 自动处理完整闭环逻辑:扫描云端记录 -> 不存在执行创建 -> 存在且IP改变执行更新 -> 相同则静默跳过
* @param string $zone_id 华为云托管公网域名区域 ID
* @param string $domain_name 完整的动态解析子域名 (如 "nas.example.com")
* @param string $current_ip 当前由网关或本地探测到的最新外部公网 IP 地址
* @param string $type 记录模式 (默认为IPv4的 "A" 记录,若使用IPv6请传入 "AAAA")
* @param boolean $force 强制更新 true,默认更新false
* @param int $ttl 缓存生存期,DDNS 业务强烈建议锁死 60 秒,保证最快生效速度
* @param string $comment 记录集的描述信息
* @return array 规范后的统一操作反馈汇报:['success' => 布尔, 'action' => 'skip|create|update|error', 'message' => '明文日志信息']
*/
public function DynamicDNS2($zone_id, $domain_name, $current_ip, $type = "A", $force = false, $ttl = 60, $comment = "DDNSAutoUpdate")
{
$current_ip = trim($current_ip);
$type = strtoupper($type);
// 格式化域名对齐规范
$search_name = rtrim(strtolower($domain_name), '.');
// 1. 获取当前 Zone 下的所有解析记录
$list_res = $this->DNSRecordsList($zone_id);
if (!$list_res['success']) {
return [
'success' => false,
'action' => 'error',
'message' => '获取云端解析记录列表失败:' . ($list_res['errors'][0]['message'] ?? 'Unknown')
];
}
$target_record = null;
// 2. 遍历查找是否存在匹配的记录(主机名和类型均相同)
foreach ($list_res['result'] as $record) {
if ($record['name'] === $search_name && strtoupper($record['type']) === $type) {
$target_record = $record;
break;
}
}
// 3. 根据比对结果执行对应操作
if ($target_record === null) {
// 情况 A:不存在该记录 -> 执行【创建】
if($comment=="DDNSAutoUpdate"){
$comment = "创建DDNS记录 ".date("Y-m-d H:i:s");
}
$create_res = $this->CreateDNSRecord($zone_id, $domain_name, $current_ip, $type, $ttl, $comment);
if ($create_res['success']) {
return [
'success' => true,
'action' => 'create',
'message' => "成功创建 DDNS 记录 [{$domain_name}] -> [{$current_ip}]"
];
} else {
return [
'success' => false,
'action' => 'create_error',
'message' => '创建 DDNS 记录失败:' . ($create_res['errors'][0]['message'] ?? 'Unknown')
];
}
} else {
// 情况 B:存在该记录,比对 IP(TXT等记录在 DNSRecordsList 内部已清洗掉双引号)
$cloud_ip = $target_record['content'];
if ($cloud_ip === $current_ip && !$force) {
// 情况 B-1:IP 相同 -> 【跳过】不请求华为云,防止触发 API 频率限制
return [
'success' => true,
'action' => 'skip',
'message' => "IP 未发生变化 ({$current_ip}),跳过更新。"
];
} else {
// 情况 B-2:IP 不同 -> 执行【更新】
if($comment=="DDNSAutoUpdate"){
$comment = "更新DDNS记录 ".date("Y-m-d H:i:s");
}
$update_res = $this->UpdateDNSRecord($zone_id, $target_record['id'], $domain_name, $current_ip, $type, $ttl, $comment);
if ($update_res['success']) {
return [
'success' => true,
'action' => 'update',
'message' => "成功更新 DDNS 记录 [{$domain_name}]: 由 [{$cloud_ip}] 变更为 [{$current_ip}]"
];
} else {
return [
'success' => false,
'action' => 'update_error',
'message' => '更新 DDNS 记录失败:' . ($update_res['errors'][0]['message'] ?? 'Unknown')
];
}
}
}
}
// =========================================================================
// 五、底层底层通信引擎 (HTTP cURL Core Engine)
// =========================================================================
/**
* 全网底层网络请求路由分发器
* 支持在 DELETE、POST、PUT 等操作中自由安全地载入网络 Payload Body
* @param string $url 目标请求端点全路径
* @param string $param HTTP 标准动作动词 ("GET"|"POST"|"PUT"|"DELETE"|"PATCH")
* @param array|null $data 传递的附加载荷数组
* @return array 统一解析状态或错误捕获后的转换数组
*/
function request_post($url, $param, $data=null)
{
if (empty($url) || empty($param)) {
return ["code" => 400, "message" => "缺少必要的URL地址或request参数!"];
}
$token = $this->getAuthToken();
if (!$token) {
return ["code" => 401, "message" => "获取华为云 IAM Token 失败,请检查凭证!"];
}
$curl = curl_init();
$setopts = array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $param,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Auth-Token: {$token}"
]
);
// 华为云的批量删除 (DELETE) 同样需要在 Body 区域中传递数据 JSON 串,所以放开限制
if (isset($data) && in_array($param, ["POST", "PUT", "PATCH", "DELETE"])) {
$setopts[CURLOPT_POSTFIELDS] = json_encode($data);
}
curl_setopt_array($curl, $setopts);
$response = curl_exec($curl);
$err = curl_error($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($err) {
return ["code" => 500, "message" => "发生错误#:" . $err];
} else {
if ($http_code === 204) {
return [];
}
$res_arr = json_decode($response, true);
if ($http_code >= 400) {
return [
"code" => $res_arr['error_code'] ?? $http_code,
"message" => $res_arr['error_msg'] ?? "HTTP 响应异常"
];
}
return empty($res_arr) ? [] : (array)$res_arr;
}
}
}
下面提供一个简单的API调用示例,如下:
# 创建一个托管域名
$domain = "test.example.com"
$rtn = DNSHuaweiCloud::Obj()->CreateZone($domain);
print_r($rtn);
# 查询当前账号下面的所有托管域名清单
$rtn = DNSHuaweiCloud::Obj()->DomainListAll();
print_r($rtn);
通过这个PHP封装类,我就可以用自己的域名,在自己的家庭网络设备如NAS上实现家里宽带的动态域名解析了,您可以创建一个ddns.php文件,核心代码如下:
//引入前面的类文件
require_once($_SERVER['DOCUMENT_ROOT'].'/DNSHuaweiCloud-inc.php');
#获取当前客户端的ip
$REMOTE_ADDR = $_SERVER['REMOTE_ADDR'];
//IPv4地址更新
if(filter_var($REMOTE_ADDR, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
{
$rtn = DNSHuaweiCloud::Obj()->DynamicDNS("example.com", "ddns", $REMOTE_ADDR, "A");
print_r($rtn);
}
//IPv6地址更新
if(filter_var($REMOTE_ADDR, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
{
$rtn = DNSHuaweiCloud::Obj()->DynamicDNS("example.com", "ddns", $REMOTE_ADDR, "AAAA");
print_r($rtn);
}
如果您在使用该封装类有任何问题,可留言给我,我会尽快回复。 |