自定义缺省匹配 -> 默认区间匹配 -> 默认缺省匹配 * - 底层依赖NotORM实现数据库的操作 * *
使用示例:
``` * //需要提供以下格式的DB配置 * $config = array( * //可用的DB服务器集群 * 'servers' => array( * 'db_demo' => array( * 'host' => 'localhost', //数据库域名 * 'name' => 'phalapi', //数据库名字 * 'user' => 'root', //数据库用户名 * 'password' => '', //数据库密码 * 'port' => '3306', //数据库端口 * 'charset' => 'UTF8', //数据库字符集 * ), * ), * * //自定义表的存储路由 * 'tables' => array( * '__default__' => array( //默认 * 'prefix' => 'tbl_', * 'key' => 'id', * 'map' => array( * array('db' => 'db_demo'), //默认缺省 * array('start' => 0, 'end' => 2, 'db' => 'db_demo'), //默认区间 * ), * ), * 'demo' => array( //自定义 * 'prefix' => 'tbl_', * 'key' => 'id', * 'map' => array( * array('db' => 'db_demo'), //自定义缺省 * array('start' => 0, 'end' => 2, 'db' => 'db_demo'), //定义区间 * ), * ), * ), * ); * * $notorm = new PhalApi_DB_NotORM($config); * * //根据ID对3取模的映射获取数据 * $rs = $notorm->demo_0->select('*')->where('id', 10)->fetch(); * $rs = $notorm->demo_1->select('*')->where('id', 11)->fetch(); ``` * * @property string table_name 数据库表名 * * @package PhalApi\DB * @link http://www.notorm.com/ * @license http://www.phalapi.net/license GPL 协议 * @link http://www.phalapi.net/ * @author dogstar 2014-11-22 */ class PhalApi_DB_NotORM /** implements PhalApi_DB */ { /** * @var NotORM $_notorms NotORM的实例池 */ protected $_notorms = array(); /** * @var PDO $_pdos PDO连接池,统一管理,避免重复连接 */ protected $_pdos = array(); /** * @var array $_configs 数据库配置 */ protected $_configs = array(); /** * @var boolean 是否开启调试模式,调试模式下会输出全部执行的SQL语句和对应消耗的时间 */ protected $debug = FALSE; /** * @var boolean 是否保持原来数据库结果集中以主键为KEY的返回方式(默认不使用) */ protected $isKeepPrimaryKeyIndex = FALSE; /** * @param array $configs 数据库配置 * @param boolean $debug 是否开启调试模式 */ public function __construct($configs, $debug = FALSE) { $this->_configs = $configs; $this->debug = $debug; } public function __get($name) { $notormKey = $this->createNotormKey($name); if (!isset($this->_notorms[$notormKey])) { list($tableName, $suffix) = $this->parseName($name); $router = $this->getDBRouter($tableName, $suffix); $structure = new NotORM_Structure_Convention( $router['key'], '%s_id', '%s', $router['prefix']); $this->_notorms[$notormKey] = new NotORM($router['pdo'], $structure); $this->_notorms[$notormKey]->debug = $this->debug; $this->_notorms[$notormKey]->isKeepPrimaryKeyIndex = $this->isKeepPrimaryKeyIndex; if ($router['isNoSuffix']) { $name = $tableName; } } return $this->_notorms[$notormKey]->$name; } public function __set($name, $value) { foreach ($this->_notorms as $notorm) { $notorm->$name = $value; } } protected function createNotormKey($tableName) { return '__' . $tableName . '__'; } /** * 解析分布式表名 * 表名 + ['_' + 数字后缀],如:user_0, user_1, ... user_100 * @param string $name */ protected function parseName($name) { $tableName = $name; $suffix = NULL; $pos = strrpos($name, '_'); if ($pos !== FALSE) { $tableId = substr($name, $pos + 1); if (is_numeric($tableId)) { $tableName = substr($name, 0, $pos); $suffix = intval($tableId); } } return array($tableName, $suffix); } /** * 获取分布式数据库路由 * @param string $tableName 数据库表名 * @param string $suffix 分布式下的表后缀 * @return array 数据库配置 * @throws PhalApi_Exception_InternalServerError */ protected function getDBRouter($tableName, $suffix) { $rs = array('prefix' => '', 'key' => '', 'pdo' => NULL, 'isNoSuffix' => FALSE); $defaultMap = !empty($this->_configs['tables']['__default__']) ? $this->_configs['tables']['__default__'] : array(); $tableMap = !empty($this->_configs['tables'][$tableName]) ? $this->_configs['tables'][$tableName] : $defaultMap; if (empty($tableMap)) { throw new PhalApi_Exception_InternalServerError( T('No table map config for {tableName}', array('tableName' => $tableName)) ); } $dbKey = NULL; $dbDefaultKey = NULL; if (!isset($tableMap['map'])) { $tableMap['map'] = array(); } foreach ($tableMap['map'] as $map) { $isMatch = FALSE; if ((isset($map['start']) && isset($map['end']))) { if ($suffix !== NULL && $suffix >= $map['start'] && $suffix <= $map['end']) { $isMatch = TRUE; } } else { $dbDefaultKey = $map['db']; if ($suffix === NULL) { $isMatch = TRUE; } } if ($isMatch) { $dbKey = isset($map['db']) ? trim($map['db']) : NULL; break; } } //try to use default map if no perfect match if ($dbKey === NULL) { $dbKey = $dbDefaultKey; $rs['isNoSuffix'] = TRUE; } if ($dbKey === NULL) { throw new PhalApi_Exception_InternalServerError( T('No db router match for {tableName}', array('tableName' => $tableName)) ); } $rs['pdo'] = $this->getPdo($dbKey); $rs['prefix'] = isset($tableMap['prefix']) ? trim($tableMap['prefix']) : ''; $rs['key'] = isset($tableMap['key']) ? trim($tableMap['key']) : 'id'; return $rs; } /** * 获取 PDO连接 * @param string $dbKey 数据库表名唯一KEY * @return PDO */ protected function getPdo($dbKey) { if (!isset($this->_pdos[$dbKey])) { $dbCfg = isset($this->_configs['servers'][$dbKey]) ? $this->_configs['servers'][$dbKey] : array(); if (empty($dbCfg)) { throw new PhalApi_Exception_InternalServerError( T('no such db:{db} in servers', array('db' => $dbKey))); } try { $this->_pdos[$dbKey] = $this->createPDOBy($dbCfg); } catch (PDOException $ex) { //异常时,接口异常返回,并隐藏数据库帐号信息 $errorMsg = T('can not connect to database: {db}', array('db' => $dbKey)); if (DI()->debug) { $errorMsg = T('can not connect to database: {db}, code: {code}, cause: {msg}', array('db' => $dbKey, 'code' => $ex->getCode(), 'msg' => $ex->getMessage())); } throw new PhalApi_Exception_InternalServerError($errorMsg); } } return $this->_pdos[$dbKey]; } /** * 针对MySQL的PDO链接,如果需要采用其他数据库,可重载此函数 * @param array $dbCfg 数据库配置 * @return PDO */ protected function createPDOBy($dbCfg) { $dsn = sprintf('mysql:dbname=%s;host=%s;port=%d', $dbCfg['name'], isset($dbCfg['host']) ? $dbCfg['host'] : 'localhost', isset($dbCfg['port']) ? $dbCfg['port'] : 3306 ); $charset = isset($dbCfg['charset']) ? $dbCfg['charset'] : 'UTF8'; $pdo = new PDO( $dsn, $dbCfg['user'], $dbCfg['password'] ); $pdo->exec("SET NAMES '{$charset}'"); return $pdo; } /** * 断开数据库链接 */ public function disconnect() { foreach ($this->_pdos as $dbKey => $pdo) { $this->_pdos[$dbKey] = NULL; unset($this->_pdos[$dbKey]); } foreach ($this->_notorms as $notormKey => $notorm) { $this->_notorms[$notormKey] = NULL; unset($this->_notorms[$notormKey]); } } /** * 为历史修改埋单:保持原来数据库结果集中以主键为KEY的返回方式 * * - PhalSpi 1.3.1 及以下版本才需要用到此切换动作 * - 涉及影响的数据库操作有:fetchAll()/fetchRows()等 * * @return PhalApi_DB_NotORM */ public function keepPrimaryKeyIndex() { $this->isKeepPrimaryKeyIndex = TRUE; return $this; } /** ------------------ 事务操作 ------------------ **/ /** * 开启数据库事务 * @param string $whichDB 指定数据库标识 * @return NULL */ public function beginTransaction($whichDB) { $this->getPdo($whichDB)->beginTransaction(); } /** * 提交数据库事务 * @param string $whichDB 指定数据库标识 * @return NULL */ public function commit($whichDB) { $this->getPdo($whichDB)->commit(); } /** * 回滚数据库事务 * @param string $whichDB 指定数据库标识 * @return NULL */ public function rollback($whichDB) { $this->getPdo($whichDB)->rollback(); } }