热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词专为PHP开发场景设计,能够根据具体数据库类型和错误场景生成专业、准确且用户友好的自定义错误消息。它通过分析错误原因、数据库类型和用户群体,提供技术精准且符合用户体验的解决方案,帮助开发者快速定位和解决数据库连接问题,提升系统可靠性和用户满意度。
<?php
// 场景:大促下单接口连接 MySQL 主库超时,连接池耗尽,自动重试 3 次仍失败
// 目标:对终端用户展示友好的页面提示,同时记录安全的技术日志供排障
// 注意:请在生产环境确保 display_errors=Off,敏感信息不可输出到页面
declare(strict_types=1);
// 可根据需要设置默认时区
date_default_timezone_set('Asia/Shanghai');
// 从环境变量读取数据库配置(不要在页面输出这些值)
$dbHost = getenv('DB_HOST') ?: '127.0.0.1';
$dbPort = (int)(getenv('DB_PORT') ?: 3306);
$dbName = getenv('DB_NAME') ?: 'app';
$dbUser = getenv('DB_USER') ?: 'app_user';
$dbPass = getenv('DB_PASS') ?: '';
const USER_MESSAGE_CODE = 'ORD-DB-CONN-001'; // 固定的业务错误码(对外展示)
const MAX_ATTEMPTS = 3;
const CONNECT_TIMEOUT_SEC = 3; // 连接超时(秒),视网络与数据库情况调整
const BASE_BACKOFF_MS = 160; // 初始退避(毫秒)
/**
* 判断是否为“可重试”的瞬时连接错误
*/
function isTransientConnectionError(?string $sqlState, ?int $driverCode, string $msg): bool
{
$sqlState = strtoupper((string)$sqlState);
// 常见可重试情形:网络/握手/连接数耗尽/超时
if (in_array($driverCode, [1040, 2002, 2006, 2013], true)) {
// 1040 Too many connections
// 2002 Can't connect / connection timed out / refused
// 2006 MySQL server has gone away
// 2013 Lost connection during query
return true;
}
// SQLSTATE 类别 08 开头为连接类错误;08S01 通信故障
if (strpos($sqlState, '08') === 0 || $sqlState === '08S01') {
return true;
}
// HY000 为通用错误,结合报错信息中的 timeout 关键词做兜底判断(不在页面展示)
if ($sqlState === 'HY000' && preg_match('/(timeout|timed out|too many connections)/i', $msg)) {
return true;
}
return false;
}
/**
* 带指数退避 + 抖动的连接重试
*/
function connectWithRetry(int $maxAttempts, string $refId): PDO
{
global $dbHost, $dbPort, $dbName, $dbUser, $dbPass;
$dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4', $dbHost, $dbPort, $dbName);
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_TIMEOUT => CONNECT_TIMEOUT_SEC, // 连接超时
// 如需持久连接(谨慎评估连接复用与资源占用),可开启:
// PDO::ATTR_PERSISTENT => true,
];
$attempt = 0;
$lastEx = null;
while ($attempt < $maxAttempts) {
$attempt++;
$start = microtime(true);
try {
$pdo = new PDO($dsn, $dbUser, $dbPass, $options);
// 可选:快速探活,确保连接可用
$pdo->query('SELECT 1');
return $pdo;
} catch (PDOException $e) {
$elapsedMs = (int)((microtime(true) - $start) * 1000);
$sqlState = $e->getCode(); // 对于 PDOException,这一般是 SQLSTATE
$driverCode = $e->errorInfo[1] ?? null; // MySQL 驱动错误码(如 1040、2002 等)
$msg = $e->getMessage();
// 记录安全的技术日志(不包含 DSN、账号等敏感信息)
error_log(sprintf(
'DB_CONN_RETRY ref=%s attempt=%d/%d elapsed=%dms sqlstate=%s code=%s note=%s',
$refId,
$attempt,
$maxAttempts,
$elapsedMs,
(string)$sqlState,
(string)$driverCode,
// 仅记录简要错误类型关键词,避免输出环境细节
isTransientConnectionError($sqlState, $driverCode, $msg) ? 'transient' : 'non-transient'
));
$lastEx = $e;
// 非瞬时错误直接终止(如认证失败/权限问题)
if (!isTransientConnectionError($sqlState, $driverCode, $msg)) {
break;
}
// 达到最大重试次数,结束
if ($attempt >= $maxAttempts) {
break;
}
// 指数退避 + 抖动,缓解雪崩与连接风暴
$backoffMs = (int)(BASE_BACKOFF_MS * pow(2, $attempt - 1));
$jitter = random_int(0, 120);
usleep(($backoffMs + $jitter) * 1000);
}
}
// 汇总失败日志(仅服务端)
if ($lastEx instanceof PDOException) {
$sqlState = $lastEx->getCode();
$driverCode = $lastEx->errorInfo[1] ?? null;
error_log(sprintf(
'DB_CONN_FAIL ref=%s attempts=%d sqlstate=%s code=%s class=%s',
$refId,
$attempt,
(string)$sqlState,
(string)$driverCode,
get_class($lastEx)
));
}
throw $lastEx ?? new RuntimeException('Database connection failed');
}
/**
* 生成对终端用户友好的 HTML 消息(不含敏感信息)
*/
function renderUserFriendlyError(string $refId): string
{
$now = date('Y-m-d H:i:s');
$safeCode = USER_MESSAGE_CODE . '-' . $refId;
$html = <<<HTML
<div style="max-width:680px;margin:48px auto;padding:24px;border:1px solid #eee;border-radius:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#1f2937;">
<h2 style="margin-top:0;color:#111827;">抱歉,当前下单人数过多,系统暂时繁忙</h2>
<p style="line-height:1.7;">
您的订单尚未提交成功,请稍后重试。我们已为您保留购物车中的商品与优惠。
</p>
<ul style="line-height:1.8;margin:0 0 12px 18px;">
<li>建议在 1–3 分钟后再次尝试</li>
<li>如多次失败,请联系在线客服并提供参考编号</li>
</ul>
<div style="background:#f9fafb;border:1px dashed #d1d5db;border-radius:6px;padding:12px 14px;margin:14px 0;">
<div style="font-size:14px;color:#374151;">参考编号:<strong>{$safeCode}</strong></div>
<div style="font-size:12px;color:#6b7280;">时间:{$now}</div>
</div>
<div>
<a href="/" style="display:inline-block;background:#111827;color:#fff;text-decoration:none;padding:10px 16px;border-radius:6px;">返回首页</a>
<a href="javascript:location.reload()" style="display:inline-block;margin-left:8px;color:#111827;text-decoration:underline;">刷新重试</a>
</div>
</div>
HTML;
return $html;
}
// --- 页面控制流示例(web_page 场景) ---
$refId = strtoupper(bin2hex(random_bytes(4))); // 简短、可读的参考编号片段(不含敏感信息)
try {
// 连接主库(写入场景不建议切换到只读库)
$pdo = connectWithRetry(MAX_ATTEMPTS, $refId);
// 业务逻辑(示例):此处执行创建订单等写入操作
// $pdo->beginTransaction();
// ... 下单相关 SQL ...
// $pdo->commit();
// 如果成功,这里渲染成功页面或继续后续流程
echo "<p>订单创建成功(示例)。</p>";
} catch (Throwable $e) {
// 统一以 503 告知页面层“暂时不可用”
http_response_code(503);
// 展示给终端用户的友好提示(不包含任何连接配置、主机名、端口等敏感信息)
echo renderUserFriendlyError($refId);
// 如需埋点或告警,可在此处调用内部告警系统(请勿把敏感信息带到外部系统)
// alert('order-db-conn-fail', ['ref' => $refId]);
}
抱歉,当前下单人数过多,系统暂时繁忙 您的订单尚未提交成功,请稍后重试。我们已为您保留购物车中的商品与优惠。
(说明:参考编号与时间为示例。实际页面将显示当前时间与动态编号,不包含任何敏感信息。)
错误类型:
可能原因:
解决建议:
<?php
/**
* PostgreSQL 连接错误处理(日志专用,安全脱敏)
* 目标场景:内容推荐服务启动后连接只读从库被拒绝,SSL证书过期导致握手失败
* 目标用户群体:developer
* 显示位置:log_file
*/
declare(strict_types=1);
/**
* 读取配置(建议通过环境变量注入)
*/
$config = [
'service_name' => getenv('APP_SERVICE_NAME') ?: 'content-recommendation',
'db_role' => 'read-replica',
'db_type' => 'postgresql',
'host' => getenv('PGHOST') ?: 'replica-host', // 不要在日志中输出
'port' => (int)(getenv('PGPORT') ?: 5432), // 不要在日志中输出
'dbname' => getenv('PGDATABASE') ?: 'appdb', // 不要在日志中输出
'user' => getenv('PGUSER') ?: 'appuser', // 不要在日志中输出
'password' => getenv('PGPASSWORD') ?: '', // 不要在日志中输出
'sslmode' => getenv('PGSSLMODE') ?: 'verify-full', // prefer verify-full
'sslrootcert' => getenv('PGSSLROOTCERT') ?: null,
'sslcert' => getenv('PGSSLCERT') ?: null,
'sslkey' => getenv('PGSSLKEY') ?: null,
'connect_timeout' => (int)(getenv('PG_CONNECT_TIMEOUT') ?: 5),
'application_name' => getenv('PG_APP_NAME') ?: 'content-recommendation',
'log_file' => getenv('APP_DB_LOG') ?: '/var/log/app/db_errors.log',
];
/**
* 建立 PDO 连接(不在日志中输出 DSN/凭据)
*/
function connectToPg(array $cfg): PDO {
$dsnParts = [
"host={$cfg['host']}",
"port={$cfg['port']}",
"dbname={$cfg['dbname']}",
"sslmode={$cfg['sslmode']}",
"connect_timeout={$cfg['connect_timeout']}",
"application_name={$cfg['application_name']}",
];
if (!empty($cfg['sslrootcert'])) $dsnParts[] = "sslrootcert={$cfg['sslrootcert']}";
if (!empty($cfg['sslcert'])) $dsnParts[] = "sslcert={$cfg['sslcert']}";
if (!empty($cfg['sslkey'])) $dsnParts[] = "sslkey={$cfg['sslkey']}";
$dsn = 'pgsql:' . implode(';', $dsnParts);
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
// 对 pgsql 而言,连接超时应使用 connect_timeout DSN 参数;ATTR_TIMEOUT 对部分驱动无效
];
return new PDO($dsn, $cfg['user'], $cfg['password'], $options);
}
/**
* 错误分类与建议
*/
function categorizePgConnectionError(PDOException $e): array {
$msg = $e->getMessage();
$code = $e->getCode() ?: null; // 对连接阶段 SQLSTATE 可能为空
$classification = 'CONNECTION_FAILURE';
$retryable = true;
$suggestions = ['check_replica_status', 'verify_host_port', 'firewall_rules', 'pg_hba_conf'];
$m = strtolower($msg);
if ((str_contains($m, 'ssl') && (str_contains($m, 'expired') || str_contains($m, 'certificate verify failed') || str_contains($m, 'handshake') || str_contains($m, 'unknown ca'))) {
$classification = 'SSL_CERT_EXPIRED';
$retryable = false; // 证书过期不应盲目重试
$suggestions = ['renew_certificate', 'update_ca_bundle', 'sync_system_time', 'restart_db_and_app'];
} elseif (str_contains($m, 'connection refused') || str_contains($m, 'econnrefused') || str_contains($m, 'could not connect to server')) {
$classification = 'CONNECTION_REFUSED';
$retryable = true;
$suggestions = ['check_replica_status', 'verify_host_port', 'firewall_rules', 'pg_hba_conf'];
} elseif (str_contains($m, 'no pg_hba.conf entry')) {
$classification = 'ACCESS_DENIED';
$retryable = false;
$suggestions = ['pg_hba_conf', 'server_reload'];
} elseif (str_contains($m, 'timeout') || str_contains($m, 'timed out')) {
$classification = 'CONNECT_TIMEOUT';
$retryable = true;
$suggestions = ['network_latency', 'increase_timeout', 'route_health'];
}
return [
'classification' => $classification,
'retryable' => $retryable,
'suggestions' => $suggestions,
'sqlstate' => $code,
];
}
/**
* 日志工具(JSON 行,敏感信息脱敏)
*/
function ensureLogDir(string $logFile): void {
$dir = dirname($logFile);
if (!is_dir($dir)) {
@mkdir($dir, 0750, true);
}
}
function generateEventId(): string {
return bin2hex(random_bytes(8)); // 16 hex chars
}
function safeHash(string $val): string {
return 'sha256:' . substr(hash('sha256', $val), 0, 12);
}
function sanitizeMessage(string $msg): string {
$patterns = [
'/password\s*=\s*[^;\s]*/i',
'/user\s*=\s*[^;\s]*/i',
'/dbname\s*=\s*[^;\s]*/i',
'/host\s*=\s*[^;\s]*/i',
'/sslkey\s*=\s*[^;\s]*/i',
'/sslcert\s*=\s*[^;\s]*/i',
'/sslrootcert\s*=\s*[^;\s]*/i',
'/port\s*=\s*\d+/i',
'/(postgres:\/\/)[^\s"]+/i',
];
$msg = preg_replace($patterns, '$1[redacted]', $msg);
// 压缩多余空白
return trim(preg_replace('/\s+/', ' ', $msg));
}
function safeLog(string $logFile, array $payload): void {
ensureLogDir($logFile);
$line = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// message_type=3 表示写入文件
@error_log($line . PHP_EOL, 3, $logFile);
}
/**
* 尝试连接并在失败时记录安全日志
*/
try {
// 生产环境:在服务启动或首次数据拉取前进行连接握手
$pdo = connectToPg($config);
// 可选:验证只读从库(避免误连主库)
// $isReplica = $pdo->query("SELECT pg_is_in_recovery() AS rep")->fetchColumn();
// if ((int)$isReplica !== 1) { /* 处理误连 */ }
} catch (PDOException $e) {
$cat = categorizePgConnectionError($e);
$now = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('c');
$raw = $e->getMessage();
$safe = sanitizeMessage($raw);
// 为了可观测性而不泄露敏感信息:仅记录指纹
$hostFingerprint = isset($config['host']) ? safeHash((string)$config['host']) : null;
$payload = [
'level' => 'error',
'event' => 'DB_CONNECT_FAILED',
'event_id' => generateEventId(),
'timestamp' => $now,
'service' => $config['service_name'],
'db' => [
'type' => $config['db_type'],
'role' => $config['db_role'],
],
'classification'=> $cat['classification'],
'summary' => match ($cat['classification']) {
'SSL_CERT_EXPIRED' => '连接到只读从库失败:SSL证书已过期,握手失败',
'CONNECTION_REFUSED'=> '连接到只读从库失败:连接被拒绝',
'ACCESS_DENIED' => '连接到只读从库失败:访问被拒绝(pg_hba.conf)',
'CONNECT_TIMEOUT' => '连接到只读从库失败:连接超时',
default => '连接到只读从库失败:未知连接错误',
},
'sqlstate' => $cat['sqlstate'],
'retryable' => $cat['retryable'],
'hints' => $cat['suggestions'],
'safe_excerpt' => $safe,
'raw_message_digest' => safeHash($raw),
'host_fingerprint' => $hostFingerprint,
'php_version' => PHP_VERSION,
];
safeLog($config['log_file'], $payload);
// 对上层调用者抛出通用异常(避免在界面或其他通道暴露细节)
throw new RuntimeException('Database connection failed. See log for details.');
}
{"level":"error","event":"DB_CONNECT_FAILED","event_id":"9f3c12ab8d4e7c21","timestamp":"2025-12-08T10:35:12+00:00","service":"content-recommendation","db":{"type":"postgresql","role":"read-replica"},"classification":"SSL_CERT_EXPIRED","summary":"连接到只读从库失败:SSL证书已过期,握手失败","sqlstate":null,"retryable":false,"hints":["renew_certificate","update_ca_bundle","sync_system_time","restart_db_and_app"],"safe_excerpt":"SSL error: handshake failure (certificate expired).","raw_message_digest":"sha256:3a91e5c0f2d1","host_fingerprint":"sha256:7c4e1a0b2f33","php_version":"8.2.12"}
<?php
/**
* 安全的 SQL Server 连接与 DNS 延迟诊断(CLI 专用)
* - 目标:在 DNS 解析偶发变慢导致驱动超时时,向管理员输出友好且可操作的诊断信息
* - 注意:不会在控制台输出连接字符串或密码等敏感信息
*/
if (PHP_SAPI !== 'cli') {
fwrite(STDERR, "This script is intended for CLI only.\n");
exit(1);
}
/*========================
可调参数(通过环境变量覆盖)
=========================*/
$cfg = [
'host' => getenv('DB_HOST') ?: '',
'port' => getenv('DB_PORT') ?: '1433',
'db' => getenv('DB_NAME') ?: '',
'user' => getenv('DB_USER') ?: '',
'pass' => getenv('DB_PASSWORD') ?: '',
'loginTimeout' => (int)(getenv('DB_LOGIN_TIMEOUT') ?: 5), // 初始登录超时秒
'maxLoginTimeout' => (int)(getenv('DB_MAX_LOGIN_TIMEOUT') ?: 12),// 重试时的上限
'dnsSlowThresholdMs' => (int)(getenv('DB_DNS_SLOW_THRESHOLD_MS') ?: 150),
'retries' => (int)(getenv('DB_CONN_RETRIES') ?: 2), // 总尝试次数(含主机名与IP回退)
'logFile' => getenv('APP_LOG') ?: '/var/log/app/db_connect.log',
'encrypt' => (getenv('DB_ENCRYPT') ?: 'yes') === 'yes', // 加密连接
'trustServerCert' => (getenv('DB_TRUST_SERVER_CERT') ?: 'no') === 'yes', // 是否信任服务器证书(生产环境建议no)
];
if (!$cfg['host'] || !$cfg['db'] || !$cfg['user']) {
fwrite(STDERR, "配置不完整:请设置 DB_HOST / DB_NAME / DB_USER(密码通过 DB_PASSWORD 提供)。\n");
exit(2);
}
/*========================
工具函数
=========================*/
function log_safe($file, $line) {
$dir = dirname($file);
if (!is_dir($dir)) {
@mkdir($dir, 0770, true);
}
$ts = date('c');
@file_put_contents($file, "[$ts] $line\n", FILE_APPEND);
}
function mask_value($value, $keepStart = 3, $keepEnd = 3) {
$len = mb_strlen($value);
if ($len <= ($keepStart + $keepEnd)) return str_repeat('*', $len);
return mb_substr($value, 0, $keepStart) . str_repeat('*', $len - $keepStart - $keepEnd) . mb_substr($value, -$keepEnd);
}
function mask_host_for_display($host) {
// 保留首段与末段顶级域名,其他以***遮蔽
$parts = explode('.', $host);
if (count($parts) <= 2) {
return mask_value($host, 2, 2);
}
$first = $parts[0];
$lastTwo = array_slice($parts, -2);
return $first . '.***.' . implode('.', $lastTwo);
}
function dns_resolve_with_metrics($host) {
$start = microtime(true);
$ips = @gethostbynamel($host);
$durationMs = (int)round((microtime(true) - $start) * 1000);
return [
'ok' => is_array($ips) && count($ips) > 0,
'ips' => $ips ?: [],
'durationMs' => $durationMs,
];
}
function sqlsrv_connect_try($server, array $cfg, &$driverName = null, &$driverErrs = []) {
$driverErrs = [];
// 优先使用 sqlsrv 扩展
if (function_exists('sqlsrv_connect')) {
$driverName = 'sqlsrv';
$connectionInfo = [
'Database' => $cfg['db'],
'UID' => $cfg['user'],
'PWD' => $cfg['pass'],
'LoginTimeout' => (int)$cfg['loginTimeout'],
'MultipleActiveResultSets' => true,
'ConnectionPooling' => true,
'Encrypt' => $cfg['encrypt'],
'TrustServerCertificate' => $cfg['trustServerCert'],
// 跨子网连接优化(AlwaysOn 场景)
'MultiSubnetFailover' => true,
];
$conn = @sqlsrv_connect($server, $connectionInfo);
if (!$conn) {
$errs = sqlsrv_errors(SQLSRV_ERR_ALL);
$driverErrs = is_array($errs) ? $errs : [];
}
return $conn;
}
// 退回 PDO_SQLSRV
if (extension_loaded('pdo_sqlsrv')) {
$driverName = 'pdo_sqlsrv';
$dsn = sprintf(
'sqlsrv:Server=%s;Database=%s;LoginTimeout=%d;Encrypt=%d;TrustServerCertificate=%d;MultiSubnetFailover=1',
$server,
$cfg['db'],
(int)$cfg['loginTimeout'],
$cfg['encrypt'] ? 1 : 0,
$cfg['trustServerCert'] ? 1 : 0
);
try {
$pdo = new PDO($dsn, $cfg['user'], $cfg['pass'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_TIMEOUT => (int)$cfg['loginTimeout'],
]);
return $pdo;
} catch (PDOException $e) {
$driverErrs = [[
'SQLSTATE' => $e->getCode(),
'code' => 0,
'message' => $e->getMessage(),
]];
return false;
}
}
$driverName = 'none';
$driverErrs = [[
'SQLSTATE' => null,
'code' => null,
'message' => 'Neither sqlsrv nor pdo_sqlsrv extension is available.',
]];
return false;
}
function classify_sql_errors(array $errs) {
// 简要分类:是否为超时/网络/DNS类问题
$codes = [];
$msgs = [];
foreach ($errs as $e) {
$codes[] = strtoupper((string)($e['SQLSTATE'] ?? ''));
$msgs[] = strtolower((string)($e['message'] ?? ''));
}
$all = implode(' | ', $msgs);
$isTimeout = (stripos($all, 'timeout') !== false) || in_array('HYT00', $codes, true) || in_array('S1T00', $codes, true);
$isDns = (stripos($all, 'dns') !== false) || (stripos($all, '11001') !== false) || (stripos($all, 'name or service not known') !== false);
$isNetwork = (stripos($all, '08001') !== false) || (stripos($all, '10060') !== false) || (stripos($all, '10061') !== false);
return [$isTimeout, $isDns, $isNetwork];
}
/*========================
主流程
=========================*/
$maskedHost = mask_host_for_display($cfg['host']);
$serverWithPort = "tcp:{$cfg['host']},{$cfg['port']}";
$dns = dns_resolve_with_metrics($cfg['host']);
$dnsSlow = $dns['durationMs'] >= $cfg['dnsSlowThresholdMs'];
$attempts = [];
$connected = false;
$conn = null;
$driver = null;
$driverErrs = [];
// 第一次:使用主机名连接
$attempts[] = [
'label' => 'hostname',
'server' => $serverWithPort,
'timeout' => $cfg['loginTimeout'],
];
$resolvedIp = $dns['ok'] ? $dns['ips'][0] : null;
if ($resolvedIp) {
$attempts[] = [
'label' => 'ip-fallback',
'server' => "tcp:{$resolvedIp},{$cfg['port']}",
'timeout' => min($cfg['maxLoginTimeout'], $cfg['loginTimeout'] + 4),
];
}
// 控制最大尝试次数
$attempts = array_slice($attempts, 0, max(1, $cfg['retries']));
$errorsCollected = [];
foreach ($attempts as $i => $a) {
$cfg['loginTimeout'] = (int)$a['timeout'];
$conn = sqlsrv_connect_try($a['server'], $cfg, $driver, $driverErrs);
if ($conn) {
$connected = true;
break;
}
$errorsCollected[] = [
'attempt' => $a['label'],
'server' => $a['server'], // 日志用,不在CLI输出
'timeout' => $a['timeout'],
'driver' => $driver,
'errors' => $driverErrs,
];
// 指数回退短暂等待(避免立即拥塞)
usleep(200000 * ($i + 1)); // 200ms, 400ms...
}
// 成功则输出简短通过信息(管理员可见,不含敏感)
if ($connected) {
echo "数据库连接检查通过:SQL Server 已连接。DNS解析耗时 {$dns['durationMs']} ms。驱动={$driver}。\n";
exit(0);
}
/*========================
失败:输出面向管理员的友好诊断
=========================*/
[$isTimeout, $isDns, $isNetwork] = classify_sql_errors(array_merge(...array_map(fn($e) => $e['errors'], $errorsCollected)));
$errorId = 'DB_CONN_DNS_TIMEOUT';
$lines = [];
$lines[] = "错误:登录模块无法连接数据库(SQL Server)";
$lines[] = "错误编号:{$errorId}";
$lines[] = "影响范围:用户登录验证暂时不可用";
$lines[] = "";
$lines[] = "关键信息:";
$lines[] = "- 目标主机(遮蔽):{$maskedHost}:{$cfg['port']}";
$lines[] = "- 目标数据库:".mask_value($cfg['db'], 1, 1);
$lines[] = "- DNS 解析:".($dns['ok'] ? "成功,用时 {$dns['durationMs']} ms" : "失败");
$lines[] = "- 连接尝试:".count($attempts)." 次(含 IP 回退:".($resolvedIp ? "是" : "否").")";
$lines[] = "- 触发类型:".(
$isDns ? "DNS 解析/路由延迟" :
($isTimeout ? "连接超时" :
($isNetwork ? "网络不可达/防火墙" : "未知"))
);
$lines[] = "";
$lines[] = "驱动返回(节选):";
if (!empty($errorsCollected)) {
$firstErrs = $errorsCollected[0]['errors'] ?: [];
$maxShow = 2;
$count = 0;
foreach ($firstErrs as $e) {
$count++;
if ($count > $maxShow) break;
$sqlstate = $e['SQLSTATE'] ?? '';
$code = $e['code'] ?? '';
$msg = trim(preg_replace('/\s+/', ' ', (string)($e['message'] ?? '')));
$msg = mb_strimwidth($msg, 0, 220, '...');
$lines[] = "- SQLSTATE={$sqlstate}; CODE={$code}; MSG={$msg}";
}
} else {
$lines[] = "- 无可用的驱动错误详情";
}
$lines[] = "";
$lines[] = "可能原因:";
$lines[] = "- 跨区域 DNS 解析偶发变慢或缓存未命中,导致登录超时(HYT00 等)。";
$lines[] = "- 网络抖动/丢包,或防火墙对 1433 端口限速。";
$lines[] = "- 多子网/异地容灾场景中,连接路径切换导致初次建连耗时。";
$lines[] = "";
$lines[] = "建议操作:";
$lines[] = "1) 立即缓解:";
$lines[] = " - 提高登录超时至 10~15 秒(DB_LOGIN_TIMEOUT),并开启 IP 回退(本脚本已内置)。";
$lines[] = " - 在应用侧启用连接重试,或短期在本机配置 hosts 静态解析(仅应急,变更需记录)。";
$lines[] = "2) 排查验证:";
$lines[] = " - 测试 DNS 时延:nslookup {$cfg['host']} 或 dig {$cfg['host']} 并记录耗时/TTL。";
$lines[] = " - 连通性检查:telnet {$maskedHost} 1433 或 Test-NetConnection(Windows)。";
$lines[] = " - 查看防火墙与安全组是否对跨区域流量限速/丢弃。";
$lines[] = "3) 长期优化:";
$lines[] = " - 启用/确认 MultiSubnetFailover=Yes(已在连接参数中开启)。";
$lines[] = " - 引入就近 DNS/缓存(如 systemd-resolved、nscd)并监控解析时延与命中率。";
$lines[] = " - 与 DBA/网络团队确认跨区域链路与 SQL 监听端点策略,必要时提供就近只读/写入端点。";
$lines[] = "";
$lines[] = "提示:为安全起见,已隐藏连接字符串和密码。详细技术信息请查看应用日志。";
$finalMessage = implode("\n", $lines);
echo $finalMessage . "\n";
/*========================
安全日志(包含更详细上下文,但仍不记录密码)
=========================*/
$logCtx = [
'driver' => $driver,
'host' => $cfg['host'],
'port' => $cfg['port'],
'db' => $cfg['db'],
'user_masked' => mask_value($cfg['user'], 1, 1),
'dns_ok' => $dns['ok'],
'dns_ms' => $dns['durationMs'],
'attempts' => array_map(function($a){ return ['label'=>$a['label'],'timeout'=>$a['timeout']]; }, $attempts),
'errors' => $errorsCollected,
];
log_safe($cfg['logFile'], 'DB_CONNECT_FAIL '.json_encode($logCtx, JSON_UNESCAPED_UNICODE));
exit(70); // EX_SOFTWARE
错误:登录模块无法连接数据库(SQL Server) 错误编号:DB_CONN_DNS_TIMEOUT 影响范围:用户登录验证暂时不可用
关键信息:
驱动返回(节选):
可能原因:
建议操作:
提示:为安全起见,已隐藏连接字符串和密码。详细技术信息请查看应用日志。
集成方式:
注意事项:
以一条可复用的专业提示词,自动生成“会说人话”的数据库连接错误消息与可直接落地的PHP处理代码,帮助研发与产品团队:
在开发或排障时,一键生成可用的错误处理代码与友好提示;快速锁定原因、给出排查步骤,统一团队消息规范。
将复杂报错转化为易懂说明,用于状态页、告警或工单回复;提供替代操作与自助引导,减少重复沟通。
无需深厚经验,复制即用;为电商下单、CMS读取、用户登录等场景配置可靠提示,降低流失与投诉。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
半价获取高级提示词-优惠即将到期