274 lines
10 KiB
PHP
274 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* MySQL数据库连接测试脚本 - 修复密码特殊符号问题
|
|
* 使用方法:
|
|
* php mysql_test.php --host=localhost --user=root --password='your@password#' --database=test
|
|
* php mysql_test.php -h localhost -u root -p 'your@password#' -d test
|
|
*/
|
|
|
|
class MySQLConnectionTester {
|
|
private $host;
|
|
private $username;
|
|
private $password;
|
|
private $database;
|
|
private $port;
|
|
|
|
public function __construct($config = []) {
|
|
$this->host = $config['host'] ?? 'localhost';
|
|
$this->username = $config['username'] ?? 'root';
|
|
$this->password = $config['password'] ?? '';
|
|
$this->database = $config['database'] ?? 'test';
|
|
$this->port = $config['port'] ?? 3306;
|
|
}
|
|
|
|
public function testConnection() {
|
|
$this->printHeader();
|
|
|
|
// 安全地显示连接信息(隐藏密码)
|
|
$this->printConnectionInfo();
|
|
|
|
try {
|
|
// 测试连接
|
|
$result = $this->connect();
|
|
$this->printSuccess($result);
|
|
|
|
// 测试查询
|
|
$this->testQueries($result['pdo']);
|
|
|
|
} catch (Exception $e) {
|
|
$this->printError($e);
|
|
return false;
|
|
}
|
|
|
|
$this->printFooter();
|
|
return true;
|
|
}
|
|
|
|
private function connect() {
|
|
$start_time = microtime(true);
|
|
|
|
$dsn = "mysql:host={$this->host};port={$this->port};dbname={$this->database};charset=utf8mb4";
|
|
$options = [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
PDO::ATTR_EMULATE_PREPARES => false,
|
|
PDO::ATTR_TIMEOUT => 5, // 5秒连接超时
|
|
];
|
|
|
|
$pdo = new PDO($dsn, $this->username, $this->password, $options);
|
|
$end_time = microtime(true);
|
|
|
|
$connection_time = round(($end_time - $start_time) * 1000, 2);
|
|
|
|
return [
|
|
'pdo' => $pdo,
|
|
'connection_time' => $connection_time,
|
|
'server_version' => $pdo->getAttribute(PDO::ATTR_SERVER_VERSION),
|
|
'client_version' => $pdo->getAttribute(PDO::ATTR_CLIENT_VERSION)
|
|
];
|
|
}
|
|
|
|
private function testQueries($pdo) {
|
|
echo "执行测试查询...\n";
|
|
|
|
// 测试1: 简单查询
|
|
$stmt = $pdo->query("SELECT 1 as test_value, NOW() as current_time");
|
|
$result = $stmt->fetch();
|
|
echo "✅ 简单查询测试: 成功\n";
|
|
|
|
// 测试2: 显示当前用户和数据库
|
|
$stmt = $pdo->query("SELECT USER() as current_user, DATABASE() as current_database");
|
|
$info = $stmt->fetch();
|
|
echo "当前用户: {$info['current_user']}\n";
|
|
echo "当前数据库: {$info['current_database']}\n";
|
|
|
|
// 测试3: 显示进程列表(前5个)
|
|
try {
|
|
$stmt = $pdo->query("SHOW PROCESSLIST LIMIT 5");
|
|
$processes = $stmt->fetchAll();
|
|
echo "当前连接数: " . count($processes) . "\n";
|
|
} catch (Exception $e) {
|
|
echo "⚠️ 无法查看进程列表(权限不足)\n";
|
|
}
|
|
}
|
|
|
|
private function printHeader() {
|
|
echo "========================================\n";
|
|
echo "MySQL数据库连接测试\n";
|
|
echo "========================================\n";
|
|
}
|
|
|
|
private function printConnectionInfo() {
|
|
echo "连接信息:\n";
|
|
echo "主机: {$this->host}:{$this->port}\n";
|
|
echo "数据库: {$this->database}\n";
|
|
echo "用户名: {$this->username}\n";
|
|
echo "密码: " . ($this->password ? "***(已隐藏)" : "(空密码)") . "\n";
|
|
echo "========================================\n";
|
|
}
|
|
|
|
private function printSuccess($result) {
|
|
echo "✅ 数据库连接成功!\n";
|
|
echo "连接耗时: {$result['connection_time']}ms\n";
|
|
echo "MySQL服务器版本: {$result['server_version']}\n";
|
|
echo "MySQL客户端版本: {$result['client_version']}\n";
|
|
echo "----------------------------------------\n";
|
|
}
|
|
|
|
private function printError($e) {
|
|
echo "❌ 数据库连接失败!\n";
|
|
echo "错误代码: " . $e->getCode() . "\n";
|
|
echo "错误信息: " . $e->getMessage() . "\n";
|
|
|
|
$this->printTroubleshooting($e->getCode());
|
|
echo "========================================\n";
|
|
}
|
|
|
|
private function printTroubleshooting($error_code) {
|
|
echo "\n常见问题排查:\n";
|
|
$suggestions = [
|
|
1045 => ["- 检查用户名和密码是否正确", "- 检查用户是否有远程访问权限"],
|
|
1049 => ["- 检查数据库名是否存在", "- 检查数据库名拼写是否正确"],
|
|
2002 => ["- 检查主机地址是否正确", "- 检查MySQL服务是否启动", "- 检查防火墙设置"],
|
|
2003 => ["- 检查端口是否正确", "- 检查MySQL服务是否启动", "- 检查防火墙设置"],
|
|
1044 => ["- 检查用户是否有访问该数据库的权限", "- 检查数据库权限设置"],
|
|
1698 => ["- 检查MySQL用户认证插件设置", "- 尝试使用mysql_native_password插件"],
|
|
1130 => ["- 检查MySQL用户的主机权限设置", "- 允许用户从当前主机连接"],
|
|
2013 => ["- 连接超时,检查网络状况", "- 检查MySQL服务器负载"]
|
|
];
|
|
|
|
if (isset($suggestions[$error_code])) {
|
|
foreach ($suggestions[$error_code] as $suggestion) {
|
|
echo "$suggestion\n";
|
|
}
|
|
} else {
|
|
echo "- 检查网络连接\n";
|
|
echo "- 检查MySQL服务状态\n";
|
|
echo "- 检查防火墙和网络安全组设置\n";
|
|
echo "- 检查密码中的特殊字符是否正确转义\n";
|
|
}
|
|
}
|
|
|
|
private function printFooter() {
|
|
echo "========================================\n";
|
|
echo "所有测试完成!数据库连接正常。\n";
|
|
echo "========================================\n";
|
|
}
|
|
}
|
|
|
|
// 更安全的命令行参数处理
|
|
function parseCommandLineArgs() {
|
|
global $argv;
|
|
|
|
$options = [
|
|
'host' => 'localhost',
|
|
'username' => 'root',
|
|
'password' => '',
|
|
'database' => 'test',
|
|
'port' => 3306
|
|
];
|
|
|
|
// 手动解析参数,避免getopt对特殊字符的处理问题
|
|
for ($i = 1; $i < count($argv); $i++) {
|
|
$arg = $argv[$i];
|
|
|
|
// 处理长选项 --option=value
|
|
if (strpos($arg, '--') === 0) {
|
|
$parts = explode('=', $arg, 2);
|
|
$key = substr($parts[0], 2);
|
|
$value = $parts[1] ?? '';
|
|
|
|
switch ($key) {
|
|
case 'host': $options['host'] = $value; break;
|
|
case 'user': $options['username'] = $value; break;
|
|
case 'password': $options['password'] = $value; break;
|
|
case 'database': $options['database'] = $value; break;
|
|
case 'port': $options['port'] = $value; break;
|
|
case 'help': showHelp(); exit(0);
|
|
}
|
|
}
|
|
// 处理短选项 -o value
|
|
elseif (strpos($arg, '-') === 0) {
|
|
$key = $arg[1];
|
|
$value = $argv[$i + 1] ?? '';
|
|
|
|
switch ($key) {
|
|
case 'h': $options['host'] = $value; $i++; break;
|
|
case 'u': $options['username'] = $value; $i++; break;
|
|
case 'p': $options['password'] = $value; $i++; break;
|
|
case 'd': $options['database'] = $value; $i++; break;
|
|
case 'P': $options['port'] = $value; $i++; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
// 交互式输入函数(用于安全输入密码)
|
|
function readPassword($prompt = "请输入密码: ") {
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
|
echo $prompt;
|
|
// Windows系统隐藏输入比较困难,直接显示
|
|
$password = trim(fgets(STDIN));
|
|
} else {
|
|
// Linux/Mac系统可以使用stty隐藏输入
|
|
echo $prompt;
|
|
system('stty -echo');
|
|
$password = trim(fgets(STDIN));
|
|
system('stty echo');
|
|
echo "\n";
|
|
}
|
|
return $password;
|
|
}
|
|
|
|
function showHelp() {
|
|
echo "MySQL数据库连接测试工具\n\n";
|
|
echo "使用方法:\n";
|
|
echo " php " . basename(__FILE__) . " [选项]\n\n";
|
|
echo "选项:\n";
|
|
echo " -h, --host 数据库主机 (默认: localhost)\n";
|
|
echo " -u, --user 数据库用户名 (默认: root)\n";
|
|
echo " -p, --password 数据库密码 (建议用引号包裹特殊密码)\n";
|
|
echo " -d, --database 数据库名 (默认: test)\n";
|
|
echo " -P, --port 端口号 (默认: 3306)\n";
|
|
echo " --help 显示此帮助信息\n\n";
|
|
echo "密码特殊字符处理:\n";
|
|
echo " 如果密码包含特殊字符,请使用单引号或双引号包裹:\n";
|
|
echo " 正确: php script.php -p 'my@pass#word!'\n";
|
|
echo " 正确: php script.php -p \"my@pass#word!\"\n";
|
|
echo " 错误: php script.php -p my@pass#word!\n\n";
|
|
echo "示例:\n";
|
|
echo " php " . basename(__FILE__) . " -h 192.168.1.100 -u myuser -p 'my@pass#word!' -d mydb\n";
|
|
echo " php " . basename(__FILE__) . " --host=mysql.example.com --user=admin --password='your@password#'\n";
|
|
}
|
|
|
|
// 主程序
|
|
if (PHP_SAPI === 'cli') {
|
|
// 命令行模式
|
|
$config = parseCommandLineArgs();
|
|
|
|
// 如果密码为空,尝试交互式输入
|
|
if (empty($config['password'])) {
|
|
$config['password'] = readPassword();
|
|
}
|
|
|
|
$tester = new MySQLConnectionTester($config);
|
|
$success = $tester->testConnection();
|
|
exit($success ? 0 : 1);
|
|
} else {
|
|
// Web浏览器模式
|
|
echo "<pre>";
|
|
$config = [
|
|
'host' => $_POST['host'] ?? $_GET['host'] ?? 'localhost',
|
|
'username' => $_POST['user'] ?? $_GET['user'] ?? 'root',
|
|
'password' => $_POST['password'] ?? $_GET['password'] ?? '',
|
|
'database' => $_POST['database'] ?? $_GET['database'] ?? 'test',
|
|
'port' => $_POST['port'] ?? $_GET['port'] ?? 3306
|
|
];
|
|
|
|
$tester = new MySQLConnectionTester($config);
|
|
$tester->testConnection();
|
|
echo "</pre>";
|
|
}
|
|
?>
|