用 PHP编写简单MySQL Bridge

发布: 2013-07-23 22:37

许多虚拟主机空间提供MySQL服务,但不提供外部访问服务。

如果需要外部依旧能够使用MySQL服务,需要在虚拟主机上安装一个桥接程序。

当然这种桥接程序提供的是另一套接口了,而不是直接走的mysql协议了。

其他使用这个服务的程序需要改到这套接口调用上。

桥接程序不复杂,最简单的情况下只需要提供一个query接口即可。

拿PHP为例,定义简单接口,实例MySQL服务的桥接接口程序。
服务器端程序pmp.php,定义了一个有两个参数的query接口,
/**
* @param sql string
* @param params array
*/
/*
settings.php template:
$db_host = "";
$db_port = 3306;
$db_user = "";
$db_pass = "";
$db_name = "";
*/

require('settings.php');

$json_command = $_POST['command'];
$command = json_decode($json_command, true);

if (isset($command['dbname']) && !empty($command['dbname'])) {
$db_name = $command['dbname'];
}
$sql = $command['sql'];
$params = $command['params'];

$dsn = "mysql:host={$db_host};port={$db_port};dbname={$db_name}";
$options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'');

$pdo = new PDO($dsn, $db_user, $db_pass, $options);
$stmt = $pdo->prepare($sql);

$ret = false;
if ($stmt) {
$ret = $stmt->execute($params);
$rows = array();
for ($i = 0; $i < 3000; $i ++) {
$row = $stmt->fetch(PDO::FETCH_BOTH);
if ($row === false) {
break;
}
$rows[] = $row;
}
}
$result = array('ret'=> $ret,
'rows' => $rows,
'count' => count($rows),
'last_insert_id' => $pdo->lastInsertId(),
'affected_rows' => $stmt->rowCount(),
'pdo_errno' => $pdo->errorCode(),
'pdo_error' => $pdo->errorInfo(),
'stmt_errno' => $stmt->errorCode(),
'stmt_error' => $stmt->errorInfo(),
'sql' => $sql,
'params' => $params,
);

$json_result = json_encode($result);
echo $json_result;

?>

客户端封装类,pmpc.php
class Pmpc
{
private $_service_url = 'http://102.118.123.121/pmp.php';
private $_service_ip = 'name.vaapp.com';
private $_db_name = '';
private $_timeout = 10;

public function __construct()
{
}

public function query($sql, $params, $db_name = '')
{
$command = array('sql' => $sql, 'params' => $params);
if (!empty($this->_db_name)) {
$command['db_name'] = $this->_db_name;
}
if (!empty($db_name)) {
$command['db_name'] = $db_name;
}
$jcommand = json_encode($command);

$headers = array('Host: ' . $this->_service_ip);
$data = curl_post($this->_service_url, array('command'=>$jcommand), $headers, $this->_timeout);
return $data;
}
};

function curl_post($url, $data = array(), $header = array(), $timeout = 5, $port = 80)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
//curl_setopt($ch, CURLOPT_PORT, $port);
!empty ($header) && curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

$result = array();
$result['result'] = curl_exec($ch);
if (0 != curl_errno($ch)) {
$result['errno'] = curl_errno($ch);
$result['error'] = "Error:\n" . curl_error($ch);
$result['reqinfo'] = curl_info($ch);
}
curl_close($ch);
return $result;
}
?>

调用的时候也非常简单,如pmpt.php,
require_once('pmpc.php');
$pc = new Pmpc();

$limit = rand(1,9);
$sql = "SELECT * FROM node WHERE 1 = :v1 ORDER BY nid LIMIT {$limit}";
$params = array(':v1' => 1);
$res = $pc->query($sql, $params);
var_dump($res);

?>

这里的参数规则定义了必须使用数据库的bind模式,防止拼接SQL时容易出现的特殊符号转义问题。
客户端与服务器端之间的数据传递使用的是json格式,方便数据格式的转换。
这个实现支持客户端传入数据库名,但不支持客户端传入数据库IP及用户名密码等。

目前的实现,还不太完善,如服务器端桥接程序没有很好的处理错误及异常情况。
客户端的封装没有在query方法返回类似数据库查询的数组结果,
而是返回了原始的HTTP请求的响应文本,需要改进为,客户端封装接口传入的参数与返回
的结果都是PHP的数组格式。


同类产品http://www.razorsql.com/


原文: http://qtchina.tk/?q=node/740

Powered by zexport