Init Repo

This commit is contained in:
root
2019-09-06 23:53:10 +08:00
commit f0ef89dfbb
7905 changed files with 914138 additions and 0 deletions

View File

@ -0,0 +1,12 @@
<?php
interface LtDbConnectionAdapter
{
/**
* @todo 兼容使用Unix Domain Socket方式连接数据库可以不指定port
*/
public function connect($connConf);
public function exec($sql, $connResource);
public function query($sql, $connResource);
public function lastInsertId($connResource);
public function escape($sql, $connResource);
}

View File

@ -0,0 +1,34 @@
<?php
class LtDbConnectionAdapterMysql implements LtDbConnectionAdapter
{
public function connect($connConf)
{
return mysql_connect($connConf["host"] . ":" . $connConf["port"], $connConf["username"], $connConf["password"]);
}
public function exec($sql, $connResource)
{
return mysql_query($sql, $connResource) ? mysql_affected_rows($connResource) : false;
}
public function query($sql, $connResource)
{
$result = mysql_query($sql, $connResource);
$rows = array();
while($row = mysql_fetch_assoc($result))
{
$rows[] = $row;
}
return $rows;
}
public function lastInsertId($connResource)
{
return mysql_insert_id($connResource);
}
public function escape($sql, $connResource)
{
return mysql_real_escape_string($sql, $connResource);
}
}

View File

@ -0,0 +1,35 @@
<?php
class LtDbConnectionAdapterMysqli implements LtDbConnectionAdapter
{
public function connect($connConf)
{
return new mysqli($connConf["host"], $connConf["username"], $connConf["password"], $connConf["dbname"], $connConf["port"]);
}
public function exec($sql, $connResource)
{
$connResource->query($sql);
return $connResource->affected_rows;
}
public function query($sql, $connResource)
{
$rows = array();
$result = $connResource->query($sql);
while($row = $result->fetch_assoc())
{
$rows[] = $row;
}
return $rows;
}
public function lastInsertId($connResource)
{
return $connResource->insert_id;
}
public function escape($sql, $connResource)
{
return mysqli_real_escape_string($connResource, $sql);
}
}

View File

@ -0,0 +1,65 @@
<?php
class LtDbConnectionAdapterPdo implements LtDbConnectionAdapter
{
public function connect($connConf)
{
// $option = array(PDO::ATTR_PERSISTENT => true);
if (isset($connConf['pconnect']) && true == $connConf['pconnect'])
{
$option[PDO::ATTR_PERSISTENT] = true;
}
else
{
$option[PDO::ATTR_PERSISTENT] = false;
}
switch ($connConf['adapter'])
{
case "pdo_mysql":
$dsn = "mysql:host={$connConf['host']};dbname={$connConf['dbname']}";
break;
case "pdo_sqlite":
$connConf["host"] = rtrim($connConf["host"], '\\/') . DIRECTORY_SEPARATOR;
if (!is_dir($connConf["host"]))
{
if (!@mkdir($connConf["host"], 0777, true))
{
trigger_error("Can not create {$connConf['host']}");
}
}
$dsn = "{$connConf['sqlite_version']}:{$connConf['host']}{$connConf['dbname']}";
break;
case "pdo_pgsql":
$dsn = "pgsql:host={$connConf['host']} port={$connConf['port']} dbname={$connConf['dbname']} user={$connConf['username']} password={$connConf['password']}";
break;
case "odbc":
$dsn = "odbc:" . $connConf["host"];
break;
}
return new PDO($dsn, $connConf['username'], $connConf['password'], $option);
}
public function exec($sql, $connResource)
{
return $connResource->exec($sql);
}
public function query($sql, $connResource)
{
return $connResource->query($sql)->fetchAll(PDO::FETCH_ASSOC);
}
/**
*
* @todo pgsql support
*/
public function lastInsertId($connResource)
{
return $connResource->lastInsertId();
}
public function escape($sql, $connResource)
{
// quote返回值带最前面和最后面的单引号, 这里去掉, DbHandler中加
return trim($connResource->quote($sql), "'");
}
}

View File

@ -0,0 +1,46 @@
<?php
class LtDbConnectionAdapterPgsql implements LtDbConnectionAdapter
{
public function connect($connConf)
{
if (isset($connConf['pconnect']) && true == $connConf['pconnect'])
{
$func = 'pg_pconnect';
}
else
{
$func = 'pg_connect';
}
return $func("host={$connConf['host']} port={$connConf['port']} user={$connConf['username']} password={$connConf['password']}");
}
public function exec($sql, $connResource)
{
$result = pg_query($connResource, $sql);
return pg_affected_rows($result);
}
public function query($sql, $connResource)
{
$result = pg_query($connResource, $sql);
return pg_fetch_all($result);
}
// SELECT CURRVAL(
// pg_get_serial_sequence('my_tbl_name','id_col_name'));"
// ------------------------------------------------------
// CREATE FUNCTION last_insert_id() RETURNS bigint AS $$
// SELECT lastval();
// $$ LANGUAGE SQL VOLATILE;
public function lastInsertId($connResource)
{
$result = pg_query($connResource, "SELECT lastval()");
$row = pg_fetch_array($result, 0, PGSQL_NUM);
return $row[0];
}
public function escape($sql, $connResource)
{
return pg_escape_string($sql);
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* Sqlite 预定义了类 SQLiteDatabase 本实现没有使用。
* 这里使用的全部是过程函数。
* 无论是函数还是类本实现只支持sqlite的2.x系列版本。
* php5.3新增扩展sqlite3用来支持3.x版本。
* PDO则同时支持2.x和3.x版本。
*/
class LtDbConnectionAdapterSqlite implements LtDbConnectionAdapter
{
public function connect($connConf)
{
if (isset($connConf['pconnect']) && true == $connConf['pconnect'])
{
$func = 'sqlite_popen';
}
else
{
$func = 'sqlite_open';
}
$connConf["host"] = rtrim($connConf["host"], '\\/') . DIRECTORY_SEPARATOR;
if(!is_dir($connConf["host"]))
{
if(!@mkdir($connConf["host"], 0777, true))
{
trigger_error("Can not create {$connConf['host']}");
}
}
$error = '';
$connResource = $func($connConf["host"] . $connConf["dbname"], 0666, $error);
if (!$connResource)
{
trigger_error($error, E_USER_ERROR);
}
else
{
return $connResource;
}
}
public function exec($sql, $connResource)
{
if(empty($sql))
{
return 0;
}
sqlite_exec($connResource, $sql);
// echo '<pre>';
// print_r(debug_backtrace());
// debug_print_backtrace();
// echo '</pre>';
// delete from table 结果为0原因未知。
// 使用 delete from table where 1 能返回正确结果
return sqlite_changes($connResource);
}
public function query($sql, $connResource)
{
$result = sqlite_query($connResource, $sql, SQLITE_ASSOC);
return sqlite_fetch_all($result, SQLITE_ASSOC);
}
public function lastInsertId($connResource)
{
return sqlite_last_insert_rowid($connResource);
}
public function escape($sql, $connResource)
{
return sqlite_escape_string($sql);
}
}

View File

@ -0,0 +1,31 @@
<?php
interface LtDbSqlAdapter
{
/**
* Return SQL statements
*/
public function setCharset($charset);
public function setSchema($schema);
public function showSchemas($database);
public function showTables($schema);
public function showFields($table);
public function beginTransaction();
public function commit();
public function rollBack();
public function limit($limit, $offset);
/**
* Retrive recordset
*/
public function getSchemas($queryResult);
public function getTables($queryResult);
public function getFields($queryResult);
/**
* Parse SQL
*/
public function detectQueryType($sql);
}

View File

@ -0,0 +1,90 @@
<?php
class LtDbSqlAdapterMysql implements LtDbSqlAdapter
{
public function setCharset($charset)
{
return "SET NAMES " . str_replace('-', '', $charset);
}
public function setSchema($schema)
{
return "USE $schema";
}
public function showSchemas($database)
{
return "SHOW DATABASES";
}
public function showTables($schema)
{
return "SHOW TABLES";
}
public function showFields($table)
{
return "DESCRIBE $table";
}
public function beginTransaction()
{
return "START TRANSACTION";
}
public function commit()
{
return "COMMIT";
}
public function rollBack()
{
return "ROLLBACK";
}
public function limit($limit, $offset)
{
return " LIMIT $limit OFFSET $offset";
}
public function getSchemas($queryResult)
{
}
public function getTables($queryResult)
{
}
public function getFields($queryResult)
{
foreach ($queryResult as $key => $value)
{
$fields[$value['Field']]['name'] = $value['Field'];
$fields[$value['Field']]['type'] = $value['Type'];
/*
* not null is NO or empty, null is YES
*/
$fields[$value['Field']]['notnull'] = (bool) ($value['Null'] != 'YES');
$fields[$value['Field']]['default'] = $value['Default'];
$fields[$value['Field']]['primary'] = (strtolower($value['Key']) == 'pri');
}
return $fields;
}
public function detectQueryType($sql)
{
if (preg_match("/^\s*SELECT|^\s*EXPLAIN|^\s*SHOW|^\s*DESCRIBE/i", $sql))
{
$ret = 'SELECT';
}
else if (preg_match("/^\s*INSERT/i", $sql))
{
$ret = 'INSERT';
}
else if (preg_match("/^\s*UPDATE|^\s*DELETE|^\s*REPLACE/i", $sql))
{
$ret = 'CHANGE_ROWS';
}
else if (preg_match("/^\s*USE|^\s*SET/i", $sql))
{
$ret = 'SET_SESSION_VAR';
}
else
{
$ret = 'OTHER';
}
return $ret;
}
}

View File

@ -0,0 +1,81 @@
<?php
class LtDbSqlAdapterPgsql implements LtDbSqlAdapter
{
public function setCharset($charset)
{
return "SET client_encoding TO '$charset'";
}
public function setSchema($schema)
{
return "SET search_path TO $schema";
}
public function beginTransaction()
{
return "";
}
public function commit()
{
return "";
}
public function rollBack()
{
return "";
}
public function showSchemas($database)
{
}
public function showTables($schema)
{
return "SELECT case when n.nspname='public' then c.relname else n.nspname||'.'||c.relname end as relname
FROM pg_class c join pg_namespace n on (c.relnamespace=n.oid)
WHERE c.relkind = 'r'
AND n.nspname NOT IN ('information_schema','pg_catalog')
AND n.nspname NOT LIKE 'pg_temp%'
AND n.nspname NOT LIKE 'pg_toast%'
ORDER BY relname";
}
public function showFields($table)
{
return "SELECT a.attnum, a.attname AS field, t.typname AS type,
format_type(a.atttypid, a.atttypmod) AS complete_type,
a.attnotnull AS isnotnull,
( SELECT 't' FROM pg_index
WHERE c.oid = pg_index.indrelid
AND pg_index.indkey[0] = a.attnum
AND pg_index.indisprimary = 't') AS pri,
(SELECT pg_attrdef.adsrc FROM pg_attrdef
WHERE c.oid = pg_attrdef.adrelid
AND pg_attrdef.adnum=a.attnum) AS default
FROM pg_attribute a, pg_class c, pg_type t
WHERE c.relname = '$table'
AND a.attnum > 0
AND a.attrelid = c.oid
AND a.atttypid = t.oid
ORDER BY a.attnum";
}
public function limit($limit, $offset)
{
return " LIMIT $limit OFFSET $offset";
}
public function getSchemas($queryResult)
{
}
public function getTables($queryResult)
{
}
public function getFields($queryResult)
{
}
public function detectQueryType($sql)
{
}
}

View File

@ -0,0 +1,120 @@
<?php
class LtDbSqlAdapterSqlite implements LtDbSqlAdapter
{
public function setCharset($charset)
{
// return 'PRAGMA encoding = "' . $charset . '"';
return '';
}
public function setSchema($schema)
{
return '';
}
public function beginTransaction()
{
return 'BEGIN TRANSACTION';
}
public function commit()
{
return 'COMMIT TRANSACTION';
}
public function rollBack()
{
return 'ROLLBACK TRANSACTION';
}
public function showSchemas($database)
{
//return "SHOW DATABASES";
return '';
}
public function showTables($schema)
{
// 临时表及其索引不在 SQLITE_MASTER 表中而在 SQLITE_TEMP_MASTER 中出现
return "SELECT name FROM sqlite_master WHERE type='table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table' ORDER BY name";
}
public function showFields($table)
{
return "PRAGMA table_info('" . $table . "')";
}
public function limit($limit, $offset)
{
return " LIMIT $limit OFFSET $offset";
}
public function getSchemas($queryResult)
{
}
public function getTables($queryResult)
{
return $queryResult;
}
public function getFields($queryResult)
{
$fields = array();
foreach ($queryResult as $key => $value)
{
// 字段名
$fields[$value['name']]['name'] = $value['name'];
// 字段类型
$fulltype = $value['type'];
$size = null;
$precision = null;
$scale = null;
if (preg_match('/^([^\(]+)\(\s*(\d+)\s*,\s*(\d+)\s*\)$/',$fulltype, $matches))
{
$type = $matches[1];
$precision = $matches[2];
$scale = $matches[3]; // aka precision
}
elseif (preg_match('/^([^\(]+)\(\s*(\d+)\s*\)$/',$fulltype, $matches))
{
$type = $matches[1];
$size = $matches[2];
}
else
{
$type = $fulltype;
}
$fields[$value['name']]['type'] = $type;
/**
* not null is 99, null is 0
*/
$fields[$value['name']]['notnull'] = (bool) ($value['notnull'] != 0);
$fields[$value['name']]['default'] = $value['dflt_value'];
$fields[$value['name']]['primary'] = (bool) ($value['pk'] == 1 && strtoupper($fulltype) == 'INTEGER');
}
return $fields;
}
public function detectQueryType($sql)
{
if (preg_match("/^\s*SELECT|^\s*PRAGMA/i", $sql))
{
$ret = 'SELECT';
}
else if (preg_match("/^\s*INSERT/i", $sql))
{
$ret = 'INSERT';
}
else if (preg_match("/^\s*UPDATE|^\s*DELETE|^\s*REPLACE/i", $sql))
{
$ret = 'CHANGE_ROWS';
}
else if (preg_match("/^\s*USE|^\s*SET/i", $sql))
{
$ret = 'SET_SESSION_VAR';
}
else
{
$ret = 'OTHER';
}
return $ret;
}
}

View File

@ -0,0 +1,91 @@
<?php
class LtDb
{
public $configHandle;
public $group;
public $node;
protected $dbh;
public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil", false))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}
public function init()
{
$this->dbh = new LtDbHandle;
$this->dbh->configHandle = $this->configHandle;
$this->dbh->group = $this->getGroup();
$this->dbh->node = $this->getNode();
$this->dbh->init();
}
public function getDbHandle()
{
return $this->dbh;
}
public function getTDG($tableName)
{
$tg = new LtDbTableDataGateway;
$tg->configHandle = $this->configHandle;
$tg->tableName = $tableName;
$tg->createdColumn = 'created';
$tg->modifiedColumn = 'modified';
$tg->dbh = $this->dbh;
return $tg;
}
public function getSqlMapClient()
{
$smc = new LtDbSqlMapClient;
$smc->configHandle = $this->configHandle;
$smc->dbh = $this->dbh;
return $smc;
}
public function changeNode($node)
{
$this->node = $node;
$this->dbh->node = $node;
}
protected function getGroup()
{
if ($this->group)
{
return $this->group;
}
$servers = $this->configHandle->get("db.servers");
if (1 == count($servers))
{
return key($servers);
}
return false;
}
protected function getNode()
{
if ($this->node)
{
return $this->node;
}
$servers = $this->configHandle->get("db.servers");
if (1 == count($servers[$this->getGroup()]))
{
return key($servers[$this->getGroup()]);
}
return false;
}
}

View File

@ -0,0 +1,15 @@
<?php
class LtDbAdapterFactory
{
public function getConnectionAdapter($connectionAdapterType)
{
$LtDbConnectionAdapter = "LtDbConnectionAdapter" . ucfirst($connectionAdapterType);
return new $LtDbConnectionAdapter;
}
public function getSqlAdapter($sqlAdapterType)
{
$LtDbSqlAdapter = "LtDbSqlAdapter" . ucfirst($sqlAdapterType);
return new $LtDbSqlAdapter;
}
}

View File

@ -0,0 +1,122 @@
<?php
class LtDbConfigBuilder
{
protected $servers = array();
protected $tables = array();
protected $adapters = array(
//"php_ext" => array("connection_adapter" => "", "sql_adapter" => "")
"pgsql" => array("connection_adapter" => "pgsql", "sql_adapter" => "pgsql"),
"pdo_pgsql" => array("connection_adapter" => "pdo", "sql_adapter" => "pgsql"),
"oci" => array("connection_adapter" => "oci", "sql_adapter" => "oracle"),
"pdo_oci" => array("connection_adapter" => "pdo", "sql_adapter" => "oracle"),
"mssql" => array("connection_adapter" => "mssql", "sql_adapter" => "mssql"),
"pdo_dblib" => array("connection_adapter" => "pdo", "sql_adapter" => "mssql"),
"mysql" => array("connection_adapter" => "mysql", "sql_adapter" => "mysql"),
"mysqli" => array("connection_adapter" => "mysqli", "sql_adapter" => "mysql"),
"pdo_mysql" => array("connection_adapter" => "pdo", "sql_adapter" => "mysql"),
"sqlite" => array("connection_adapter" => "sqlite", "sql_adapter" => "sqlite"),
"sqlite3" => array("connection_adapter" => "sqlite3", "sql_adapter" => "sqlite"),
"pdo_sqlite" => array("connection_adapter" => "pdo", "sql_adapter" => "sqlite"),
);
protected $defaultConfig = array(
"host" => "localhost", //some ip, hostname
//"port" => 3306,
"username" => "root",
"password" => null,
//"adapter" => "mysql", //mysql,mysqli,pdo_mysql,sqlite,pdo_sqlite
"charset" => "UTF-8",
"pconnect" => true, //true,false
"connection_ttl" => 3600, //any seconds
"dbname" => null, //default dbname
"schema" => null, //default schema
"connection_adapter" => null,
"sql_adapter" => null,
);
protected $defaultAdapterConfigs = array(
"pgsql" => array(
"port" => 5432,
),
"oracle" => array(
"port" => 1521,
),
"mssql" => array(
"port" => 1433,
),
"mysql" => array(
"port" => 3306,
"pconnect" => false,
"connection_ttl" => 30,
),
);
public function addSingleHost($hostConfig)
{
$this->addHost("group_0", "node_0", "master", $hostConfig);
}
public function addHost($groupId, $nodeId = "node_0", $role = "master", $hostConfig)
{
if (isset($this->servers[$groupId][$nodeId][$role]))
{//以相同role的第一个host为默认配置
$ref = $this->servers[$groupId][$nodeId][$role][0];
}
else if ("slave" == $role && isset($this->servers[$groupId][$nodeId]["master"]))
{//slave host以master的第一个host为默认配置
$ref = $this->servers[$groupId][$nodeId]["master"][0];
}
else if (isset($this->servers[$groupId]) && count($this->servers[$groupId]))
{//以本group第一个node的master第一个host为默认配置
$refNode = key($this->servers[$groupId]);
$ref = $this->servers[$groupId][$refNode]["master"][0];
}
else
{
if (!isset($hostConfig["adapter"]))
{
trigger_error("No db adapter specified");
}
$ref = $this->defaultConfig;
if (isset($this->defaultAdapterConfigs[$this->adapters[$hostConfig["adapter"]]["sql_adapter"]]))
{
$ref = array_merge($ref, $this->defaultAdapterConfigs[$this->adapters[$hostConfig["adapter"]]["sql_adapter"]]);
}
}
$conf = array_merge($ref, $hostConfig);
$conf = array_merge($conf, $this->adapters[$conf["adapter"]]);
$conf = $this->convertDbnameToSchema($conf);
$this->servers[$groupId][$nodeId][$role][] = $conf;
}
public function getServers()
{
return $this->servers;
}
public function getTables()
{
return $this->tables;
}
public function buildTablesConfig()
{
}
/**
* Convert dbname to schema for: FrontBase, MySQL, mSQL, MS SQL Server, MaxDB, Sybase
* See: http://www.php.net/manual-lookup.php?pattern=_select_db
*/
protected function convertDbnameToSchema($conf)
{
if (preg_match("/fbsql|mysql|msql|mssql|maxdb|sybase/i", $conf["sql_adapter"]) && isset($conf["dbname"]))
{
$conf["schema"] = $conf["dbname"];
$conf["dbname"] = null;
}
return $conf;
}
}

View File

@ -0,0 +1,117 @@
<?php
class LtDbConnectionManager
{
/**
* Connection management
* array(
* "connection" => connection resource id,
* "expire_time" => expire time,
* "schema" => default schema name,
* "charset" => char set / encoding
* )
*/
static public $connectionPool;
public $configHandle;
protected $connectionAdapter;
protected $sqlAdapter;
private $servers;
public function getConnection($group, $node, $role = "master")
{
if(empty($this->servers))
{
$this->servers = $this->configHandle->get("db.servers");
}
if (($connection = $this->getNewConnection($group, $node, $role)) ||($connection = $this->getCachedConnection($group, $node, $role)))
{
return array(
"connectionAdapter" => $this->connectionAdapter,
"connectionResource" => $connection
);
}
else
{
trigger_error("db server can not be connected: group=$group, node=$node, role=$role", E_USER_ERROR);
return false;
}
}
protected function getConnectionKey($connConf)
{
return $connConf['adapter'] . $connConf['host'] . $connConf['port'] . $connConf['username'] . $connConf['dbname'];
}
protected function saveConnection($connConf, $connection, $ttl)
{
$connectionInfo = array(
"connection" => $connection,
"expire_time" => time() + $ttl,
"schema" => $connConf["schema"],
"charset" => $connConf["charset"],
);
self::$connectionPool[$this->getConnectionKey($connConf)] = $connectionInfo;
}
protected function getCachedConnection($group, $node, $role)
{
foreach($this->servers[$group][$node][$role] as $hostConfig)
{
$key = $this->getConnectionKey($hostConfig);
if(isset(self::$connectionPool[$key]) && time() < self::$connectionPool[$key]['expire_time'])
{//cached connection resource FOUND
$connectionInfo = self::$connectionPool[$key];
if ($connectionInfo["schema"] != $hostConfig["schema"] || $connectionInfo["charset"] != $hostConfig["charset"])
{//检查当前schema和charset与用户要操作的目标不一致
$hostConfig = $this->servers[$group][$node][$role][$hostIndexArray[$hashNumber]];
$dbFactory = new LtDbAdapterFactory;
$this->connectionAdapter = $dbFactory->getConnectionAdapter($hostConfig["connection_adapter"]);
$this->sqlAdapter = $dbFactory->getSqlAdapter($hostConfig["sql_adapter"]);
if ($connectionInfo["schema"] != $hostConfig["schema"])
{
$this->connectionAdapter->exec($this->sqlAdapter->setSchema($hostConfig["schema"]), $connectionInfo["connection"]);
}
if ($connectionInfo["charset"] != $hostConfig["charset"])
{
$this->connectionAdapter->exec($this->sqlAdapter->setCharset($hostConfig["charset"]), $connectionInfo["connection"]);
}
$this->saveConnection($hostConfig, $connectionInfo["connection"], $hostConfig["connection_ttl"]);
}
return $connectionInfo["connection"];
}
}
return false;
}
protected function getNewConnection($group, $node, $role)
{
$hostTotal = count($this->servers[$group][$node][$role]);
$hostIndexArray = array_keys($this->servers[$group][$node][$role]);
while ($hostTotal)
{
$hashNumber = substr(microtime(),7,1) % $hostTotal;
$hostConfig = $this->servers[$group][$node][$role][$hostIndexArray[$hashNumber]];
$dbFactory = new LtDbAdapterFactory;
$this->connectionAdapter = $dbFactory->getConnectionAdapter($hostConfig["connection_adapter"]);
$this->sqlAdapter = $dbFactory->getSqlAdapter($hostConfig["sql_adapter"]);
if ($connection = $this->connectionAdapter->connect($hostConfig))
{
$this->connectionAdapter->exec($this->sqlAdapter->setSchema($hostConfig["schema"]), $connection);
$this->connectionAdapter->exec($this->sqlAdapter->setCharset($hostConfig["charset"]), $connection);
$this->saveConnection($hostConfig, $connection, $hostConfig["connection_ttl"]);
return $connection;
}
else
{
//trigger_error('connection fail', E_USER_WARNING);
//delete the unavailable server
for ($i = $hashNumber; $i < $hostTotal - 1; $i ++)
{
$hostIndexArray[$i] = $hostIndexArray[$i+1];
}
unset($hostIndexArray[$hostTotal-1]);
$hostTotal --;
}//end else
}//end while
return false;
}
}

View File

@ -0,0 +1,200 @@
<?php
class LtDbHandle
{
public $configHandle;
public $group;
public $node;
public $role = "master";
public $connectionAdapter;
public $connectionResource;
public $sqlAdapter;
protected $connectionManager;
private $servers;
public function __construct()
{
}
public function init()
{
if(empty($this->servers))
{
$this->servers = $this->configHandle->get("db.servers");
}
$this->connectionManager = new LtDbConnectionManager;
$this->connectionManager->configHandle = $this->configHandle;
$this->sqlAdapter = $this->getCurrentSqlAdapter();
$connectionInfo = $this->connectionManager->getConnection($this->group, $this->node, $this->role);
$this->connectionAdapter = $connectionInfo["connectionAdapter"];
$this->connectionResource = $connectionInfo["connectionResource"];
}
/**
* Trancaction methods
*/
public function beginTransaction()
{
return $this->connectionAdapter->exec($this->sqlAdapter->beginTransaction(), $this->connectionResource);
}
public function commit()
{
return $this->connectionAdapter->exec($this->sqlAdapter->commit(), $this->connectionResource);
}
public function rollBack()
{
return $this->connectionAdapter->exec($this->sqlAdapter->rollBack(), $this->connectionResource);
}
/**
* Execute an sql query
*
* @param $sql
* @param $bind
* @param $forceUseMaster
* @return false on query failed
* --sql type-- --return value--
* SELECT, SHOW, DESECRIBE, EXPLAIN rowset or NULL when no record found
* INSERT the ID generated for an AUTO_INCREMENT column
* UPDATE, DELETE, REPLACE affected count
* USE, DROP, ALTER, CREATE, SET etc true
* @notice 每次只能执行一条SQL
* 不要通过此接口执行USE DATABASE, SET NAMES这样的语句
*/
public function query($sql, $bind = null, $forceUseMaster = false)
{
$sql = trim($sql);
if (empty($sql))
{
trigger_error('Empty the SQL statement');
}
$queryType = $this->sqlAdapter->detectQueryType($sql);
switch ($queryType)
{
case "SELECT":
if (!$forceUseMaster && isset($this->servers[$this->group][$this->node]["slave"]))
{
$this->role = "slave";
}
$queryMethod = "select";
break;
case "INSERT":
$this->role = "master";
$queryMethod = "insert";
break;
case "CHANGE_ROWS":
$this->role = "master";
$queryMethod = "changeRows";
break;
case "SET_SESSION_VAR":
$queryMethod = "setSessionVar";
break;
case "OTHER":
default:
$this->role = "master";
$queryMethod = "other";
break;
}
$connectionInfo = $this->connectionManager->getConnection($this->group, $this->node, $this->role);
$this->connectionAdapter = $connectionInfo["connectionAdapter"];
$this->connectionResource = $connectionInfo["connectionResource"];
if (is_array($bind) && 0 < count($bind))
{
$sql = $this->bindParameter($sql, $bind);
}
return $this->$queryMethod($sql, $this->connectionResource);
}
/**
* function posted by renlu
*/
public function escape($str)
{
return $this->connectionAdapter->escape($str, $this->connectionResource);
}
/**
* function posted by renlu
*/
public function insertid()
{
return $this->connectionAdapter->lastInsertId($this->connectionResource);
}
/**
* Generate complete sql from sql template (with placeholder) and parameter
*
* @param $sql
* @param $parameter
* @return string
* @todo 兼容pgsql等其它数据库pgsql的某些数据类型不接受单引号引起来的值
*/
public function bindParameter($sql, $parameter)
{
// 注意替换结果尾部加一个空格
$sql = preg_replace("/:([a-zA-Z0-9_\-\x7f-\xff][a-zA-Z0-9_\-\x7f-\xff]*)\s*([,\)]?)/", "\x01\x02\x03\\1\x01\x02\x03\\2 ", $sql);
foreach($parameter as $key => $value)
{
$find[] = "\x01\x02\x03$key\x01\x02\x03";
if ($value instanceof LtDbSqlExpression)
{
$replacement[] = $value->__toString();
}
else
{
$replacement[] = "'" . $this->connectionAdapter->escape($value, $this->connectionResource) . "'";
}
}
$sql = str_replace($find, $replacement, $sql);
return $sql;
}
protected function getCurrentSqlAdapter()
{
$factory = new LtDbAdapterFactory;
$host = key($this->servers[$this->group][$this->node][$this->role]);
return $factory->getSqlAdapter($this->servers[$this->group][$this->node][$this->role][$host]["sql_adapter"]);
}
protected function select($sql, $connResource)
{
$result = $this->connectionAdapter->query($sql, $connResource);
if (empty($result))
{
return null;
}
else
{
return $result;
}
}
protected function insert($sql, $connResource)
{
if ($result = $this->connectionAdapter->exec($sql, $connResource))
{
return $this->connectionAdapter->lastInsertId($connResource);
}
else
{
return $result;
}
}
protected function changeRows($sql, $connResource)
{
return $this->connectionAdapter->exec($sql, $connResource);
}
/**
*
* @todo 更新连接缓存
*/
protected function setSessionVar($sql, $connResource)
{
return false === $this->connectionAdapter->exec($sql, $connResource) ? false : true;
}
protected function other($sql, $connResource)
{
return false === $this->connectionAdapter->exec($sql, $connResource) ? false : true;
}
}

View File

@ -0,0 +1,15 @@
<?php
class LtDbSqlExpression
{
private $_expression;
public function __construct($string)
{
$this->_expression = (string) $string;
}
public function __toString()
{
return (string) $this->_expression;
}
}

View File

@ -0,0 +1,16 @@
<?php
/**
* 加工工厂类由开发者自行开发,继承自这个类
*/
abstract class LtAbstractDbSqlMapFilterObject {
// query()方法返回的结果集,用于加工的原料
public $result;
/**
* 需要被继承实现逻辑的操作类输入query()方法返回的结果集
* 经过处理后返回开发者定义的对象或结构
*/
abstract protected function process();
}

View File

@ -0,0 +1,14 @@
<?php
class LtDbSqlMapClient
{
public $configHandle;
public $dbh;
public function execute($mapId, $bind = null)
{
$sqlMap = $this->configHandle->get($this->dbh->group . "." . $mapId);
$forceUseMaster = isset($sqlMap["force_use_master"]) ? $sqlMap["force_use_master"] : false;
return $this->dbh->query($sqlMap["sql"], $bind, $forceUseMaster);
}
}

View File

@ -0,0 +1,24 @@
<?php
/**
* 用于加工DB句柄query方法返回的数组
* 开发者在一次会话中可配置多个Filter
*/
class LtDbSqlMapResultFactory {
// Filter列表
public $filters;
public $configHandle;
public function init() {
}
/**
* 工厂入口sql map client调用的方法
* 在这个方法中调用开发者自定义的
* LtAbstractSqlMapFilterObject.process()方法
* 可配置多个process方法
*/
public function run() {
}
}

View File

@ -0,0 +1,300 @@
<?php
/**
* Database Table abstract
*
* @todo pretty join support
*/
class LtDbTableDataGateway
{
public $configHandle;
public $dbh;
/**
* The created field name
*
* @var string
*/
public $createdColumn;
/**
* The modified field name
*
* @var string
*/
public $modifiedColumn;
/**
* The table name
*
* @var string
*/
public $tableName;
/**
* The fields array
*
* @var array
*/
protected $fields;
/**
* The primary key
*
* @var string
*/
protected $primaryKey;
protected $servers;
/**
* Build table's field list
*
* @return array
*/
protected function buildFieldList()
{
if (!empty($this->fields))
{
return true;
}
$servers = $this->configHandle->get('db.servers');
$group = $this->dbh->group;
$node = $this->dbh->node;
$role = $this->dbh->role;
$table = $this->tableName;
$host = key($servers[$group][$node][$role]);
$key = md5($group . $node . $role . $table . $host . $table);
if (!$value = $this->configHandle->get($key))
{
$sql = $this->dbh->sqlAdapter->showFields($this->tableName);
$rs = $this->dbh->query($sql);
$this->fields = $this->dbh->sqlAdapter->getFields($rs);
foreach($this->fields as $field)
{
if ($field['primary'] == 1)
{
$this->primaryKey = $field['name'];
break;
}
}
$value['fields'] = $this->fields;
$value['primaryKey'] = $this->primaryKey;
$this->configHandle->addConfig(array($key => $value));
}
else
{
$this->fields = $value['fields'];
$this->primaryKey = $value['primaryKey'];
}
}
/**
* A shortcut to SELECT COUNT(*) FROM table WHERE condition
*
* @param array $args
* @return integer
* @example count(array('expression' => 'id < :id', 'value' => array('id' => 10)));
*/
public function count($args = null)
{
$selectTemplate = 'SELECT COUNT(*) AS total FROM %s%s';
$where = isset($args['where']['expression']) ? ' WHERE ' . $args['where']['expression'] : '';
$bind = isset($args['where']['value']) ? $args['where']['value'] : array();
$join = isset($args['join']) ? ' ' . $args['join'] : '';
$sql = sprintf($selectTemplate, $this->tableName, $join . $where);
$queryResult = $this->dbh->query($sql, $bind);
return $queryResult[0]['total'];
}
/**
* Delete a row by primary key
*
* @param string $primaryKeyId
* @return string
* @example delete(10);
*/
public function delete($primaryKeyId)
{
$this->buildFieldList();
$where['expression'] = $this->primaryKey . '=:' . $this->primaryKey;
$where['value'][$this->primaryKey] = $primaryKeyId;
return $this->deleteRows($where);
}
/**
* Delete many rows from table
* Please use this method carefully!
*
* @param array $args
* @return integer
* @example deleteRows(array('expression' => "id > :id", 'value' => array('id' => 2)));
*/
public function deleteRows($args = null)
{
$deleteTemplate = 'DELETE FROM %s%s';
$where = isset($args['expression']) ? ' WHERE ' . $args['expression'] : '';
$bind = isset($args['value']) ? $args['value'] : array();
$sql = sprintf($deleteTemplate, $this->tableName, $where);
return $this->dbh->query($sql, $bind);
}
/**
* Fetch one row from table by primary key
*
* @param string $primaryKeyId
* @param array $args
* @param boolean $useSlave
* @return array
* @example fetch(10)
*/
public function fetch($primaryKeyId, $args = null, $useSlave = true)
{
$this->buildFieldList();
$fetchRowsArgs['where']['expression'] = $this->tableName . '.' . $this->primaryKey . '=:' . $this->primaryKey;
$fetchRowsArgs['where']['value'][$this->primaryKey] = $primaryKeyId;
$fetchRowsArgs['fields'] = isset($args['fields']) ? $args['fields'] : null;
$fetchRowsArgs['join'] = isset($args['join']) ? $args['join'] : null;
$fetchResult = $this->fetchRows($fetchRowsArgs, $useSlave);
return $fetchResult ? $fetchResult[0] : $fetchResult;
}
/**
* Fetch many rows from table
*
* @param array $args
* @param boolean $useSlave
* @return array
* @example fetchRows(array('where' => array('expression' => "id > :id", 'value' => array('id' => 2))));
*/
public function fetchRows($args = null, $useSlave = true)
{
$this->buildFieldList();
$selectTemplate = 'SELECT %s FROM %s%s';
$fields = isset($args['fields']) ? $args['fields'] : '*';
$where = isset($args['where']['expression']) ? ' WHERE ' . $args['where']['expression'] : '';
$bind = isset($args['where']['value']) ? $args['where']['value'] : array();
$join = isset($args['join']) ? ' ' . $args['join'] : '';
$orderby = isset($args['orderby']) ? ' ORDER BY ' . $args['orderby'] : '';
$groupby = isset($args['groupby']) ? ' GROUP BY ' . $args['groupby'] : '';
$sql = sprintf($selectTemplate, $fields, $this->tableName, $join . $where . $groupby . $orderby);
if (isset($args['limit']))
{
$offset = isset($args['offset']) ? $args['offset'] : 0;
$sql = $sql . ' ' . $this->dbh->sqlAdapter->limit($args['limit'], $offset);
}
return $this->dbh->query($sql, $bind);
}
/**
* Insert one row into table, then return the inserted row's pk
*
* @param array $args
* @return string
* @example insert(array('name' => 'lily', 'age' => '12'));
*/
public function insert($args = null)
{
$this->buildFieldList();
$insertTemplate = 'INSERT INTO %s (%s) VALUES (%s)';
$fields = array();
$placeHolders = array();
foreach($args as $field => $value)
{
if (isset($this->fields[$field]))
{
$fields[] = $field;
$placeholders[] = ":$field";
$values[$field] = $value;
}
}
if (isset($this->fields[$this->createdColumn]) && !isset($args[$this->createdColumn]))
{
$fields[] = $this->createdColumn;
$placeholders[] = ':' . $this->createdColumn;
$values[$this->createdColumn] = time();
}
if (isset($this->fields[$this->modifiedColumn]) && !isset($args[$this->modifiedColumn]))
{
$fields[] = $this->modifiedColumn;
$placeholders[] = ':' . $this->modifiedColumn;
$values[$this->modifiedColumn] = time();
}
$sql = sprintf($insertTemplate, $this->tableName, implode(",", $fields), implode(",", $placeholders));
$bind = $values;
$queryResult = $this->dbh->query($sql, $bind);
return isset($args[$this->primaryKey]) ? $args[$this->primaryKey] : $queryResult;
}
/**
* Update one row by primary key
*
* @param string $primaryKeyId
* @param array $args
* @return integer
* @example update(1, array('name' => 'lily', 'age' => '18'));
*/
public function update($primaryKeyId, $args = null)
{
$this->buildFieldList();
$where['expression'] = $this->primaryKey . '=:' . $this->primaryKey;
$where['value'][$this->primaryKey] = $primaryKeyId;
return $this->updateRows($where, $args);
}
/**
* Update manay rows
* Please use this method carefully!
*
* @param array $where
* @param array $args
* @return integer
* @example updateRows(array('expression' => "id > :id", 'value' => array('id' => 2)), array('name' => 'kiwi', 'age' => '1'));
*/
public function updateRows($where, $args = null)
{
$this->buildFieldList();
$updateTemplate = 'UPDATE %s SET %s%s';
$fields = array();
$bindParameters = array();
$placeholderStyle = isset($where['value']) && array_key_exists(0, $where['value']) ? 'questionMark' : 'named';
foreach($args as $field => $value)
{
if (isset($this->fields[$field]))
{
if ($args[$field] instanceof DbExpression)
{
$fields[] = "$field=" . $args[$field]->__toString();
}
else
{
if ('named' == $placeholderStyle)
{
$fields[] = "$field=:$field";
$bindParameters[$field] = $args[$field];
}
else
{
$fields[] = "$field=?";
$bindParameters[] = $args[$field];
}
}
}
}
if (isset($this->fields[$this->modifiedColumn]) && !isset($args[$this->modifiedColumn]))
{
if ('named' == $placeholderStyle)
{
$fields[] = $this->modifiedColumn . '=:' . $this->modifiedColumn;
$bindParameters[$this->modifiedColumn] = time();
}
else
{
$fields[] = $this->modifiedColumn . '=?';
$bindParameters[] = time();
}
}
$whereCause = isset($where['expression']) ? ' WHERE ' . $where['expression'] : '';
$bind = isset($where['value']) ? array_merge($bindParameters, $where['value']) : $bindParameters;
$sql = sprintf($updateTemplate, $this->tableName, implode(",", $fields), $whereCause);
return $this->dbh->query($sql, $bind);
}
}