# `naisi_generate_client_mark`

根据客户端信息创建可校验的 18 字符 client mark。

全部字符为 [a-z0-9]。前 11 字符是规范化客户端 JSON 的 SHA-256 摘要对 36^11
取模后的 Base36 表示；中间 6 字符来自密码学安全随机源，均匀采样且允许重复；
最后 1 字符使用 Luhn mod 36 校验前述内容，并绑定大小写归一化的客户端来源。
因此，即使 client_info 完全相同，每次生成的完整 mark 通常也不同。

本函数使用调用方提供的浏览器或设备信息，但不读取服务器设备机器指纹、Customer ID、
Build ID 或 License ID，也不使用项目私有盐或内部指纹材料。跨设备验证同一个 mark
时，规范化后的 client_info、client_source 和算法版本必须一致。

注意：应生成一次并保存完整 mark，后续使用 naisi_verify_client_mark() 验证；不得通过
再次生成并比较完整字符串来验证。client mark 是客户端一致性校验值，不是登录令牌、
数字签名或可靠的硬件唯一标识。仅使用新的 11+6+1 规则，不保留旧版校验逻辑。

## 函数签名

```php
function naisi_generate_client_mark(array $client_info, string $client_source, ?string &$error = null): ?string
```

## 调用前提

应用入口先调用 [naisi_is_ext_license_valid](/web/developers/naisilib/naisi-is-ext-license-valid) 并确认返回 `true`。认证 Agent 发布的许可与 heartbeat 快照，并建立或复用本请求的 BASIC 证明；不会读取许可文件或检查当前 Host。

授权类别：`LICENSE`；执行模式：`BASIC`。许可不代替登录、租户或权限上下文；身份与数据要求见本页参数及用途说明。

## 参数

### `$client_info`

- 类型：`array`
- 必填：是
- 默认值：无
- 方向：输入参数。

已解码的客户端信息数组，最多 256 项，且必须包含 userAgent、language、colorDepth、pixelRatio、hardwareConcurrency、screenResolution、availableScreenResolution、timezoneOffset、timezone、sessionStorage、localStorage、indexedDb、openDatabase、platform、hasLiedResolution、hasLiedOs、hasLiedBrowser、touchSupport。三个显示环境字段 pixelRatio、screenResolution 和 availableScreenResolution 必须提供但不参与指纹，其他附加键会参与计算。

### `$client_source`

- 类型：`string`
- 必填：是
- 默认值：无
- 方向：输入参数。

客户端来源代码。当前版本只接受大小写完全一致的 "web-admin"。

### `$error`

- 类型：`?string`
- 必填：否
- 默认值：`null`
- 方向：引用输出参数；失败时清空，仅在成功后包含有效结果。

错误输出；成功时清空，客户端信息或来源无效时写入 "invalid_client_info"，生成失败时写入 "client_mark_creation_failed"。 该参数按引用传递，调用结束后可能被写入输出结果。

## 返回值

类型：`string|null`

成功时返回固定 18 字符 [a-z0-9] 的 client mark；参数无效、未授权或生成失败时返回 null，并尽可能通过 $error 给出非敏感错误代码。

## 调用示例

当前请求须满足上方调用前提；请检查实际返回值和引用输出，随机结果及数据包内容以运行环境为准。

```php
$clientInfo = [
    'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/150.0.0.0',
    'language' => 'zh-CN',
    'colorDepth' => 24,
    'pixelRatio' => 1,
    'hardwareConcurrency' => 8,
    'screenResolution' => '1920x1080',
    'availableScreenResolution' => '1920x1040',
    'timezoneOffset' => -480,
    'timezone' => 'Asia/Shanghai',
    'sessionStorage' => true,
    'localStorage' => true,
    'indexedDb' => true,
    'openDatabase' => false,
    'platform' => 'Win32',
    'hasLiedResolution' => false,
    'hasLiedOs' => false,
    'hasLiedBrowser' => false,
    'touchSupport' => false,
];
$error = null;
$result = naisi_generate_client_mark($clientInfo, 'web-admin', $error);
var_dump($result, $error);
```

## 版本信息

- 自版本：`1.0.6`
- 实现模块：`client`
- 授权类别：`LICENSE` / `BASIC`
- 失败回退：`null`
- 文档同步：`2026-10-18`
