楚狐在线 - 资讯杂烩
资讯杂烩   在线工具   益智游戏   影音娱乐   网站导航

华为云域名云解析服务(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);
}

  如果您在使用该封装类有任何问题,可留言给我,我会尽快回复

返回首页    发布日期:2026年06月09日

Copyright © 2026   楚狐在线   All Rights ReservedAZ.   反馈留言