简单Web应用框架设计

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liebert/article/details/77173027

框架抽象了通用流程,对成熟的、稳定的流程进行了封装。对于开发者来说通过框架提供的规范(比如子类化抽象类或者实现相关接口,实现相关的交互协议和接口)就可以将客户化的代码植入具体的流程中,实现具体场景下客户定制需求。

对于web应用,我们可以简单将其抽象归纳为如图中所示几个部分:客户端(Client)、请求处理(Router)、业务处理(Controller)、外部调用(eAPI)、视图引擎(View)、服务引擎(Service)、数据持久(DAO)和数据库文件系统。


1.请求处理Router

请求处理主要是对客户端全部请求的统一入口,和硬件路由器的功用相似。请求处理主要对客户端请求进行过滤、定位控制器、权限校验等作用。

2.业务处理Controller

业务处理主要是结合具体的业务对用户的请求进行处理,其实这也是我们常说的控制器。

3.视图引擎View

视图引擎主要调用模板对业务数据进行渲染,输出html界面文件。

4.服务引擎Service

服务类似于业务处理,从整个应用层面来看,服务属于一类稳定的、安全的、全局的业务应用,处于业务和数据的中间。在有的应用中,直接将服务层合并到了业务处理中,但是我还是倾向于将服务剥离出来。

5.数据持久DAO

数据持久主要用于和数据库文件系统交互,也就是我们经常说的CURD操作。通常调用数据库或文件系统提供的CURD驱动接口(DataDriver)实现。

6.数据库、文件系统

数据库、文件系统主要用于提供数据的CURD具体实现,如:MySQL、NTFS文件系统,并为三方应用提供数据驱动接口DataDriver。

结合以上分析,我做了一个简单的web框架。具体源码地址:https://github.com/liebertLEOS/PHPFrame


(1)Start.php

相当于框架加载器Loader和Router的综合,当然,后期要进行剥离

<?php
/**
 *          Mysqli(Frame\Start.php)
 *
 *    功  能:系统启动文件
 *
 *    作  者:李伯特
 *    完成时间:2015/08/14
 *    修  改:2015/09/01   修复了默认控制器不存在,自动创建默认控制器文件
 *
 */
//加载配置文件
require_once 'function.php';
require_once 'Tools.class.php';

use Core\Tools;
use Core\Hook;

//定义系统根目录
defined('SYS_PATH')           or define('SYS_PATH', substr(dirname(__FILE__).'\\', 0, -6));
//定义应用名称
defined('APP_NAME')           or define('APP_NAME', 'App');
//定义应用存放目录
defined('APP_PATH')           or define('APP_PATH', SYS_PATH.APP_NAME.'\\');
//定义框架库目录
defined('FRAME_PATH')         or define('FRAME_PATH',SYS_PATH.'Core\\');
//定义框架库配置目录
defined('CONF_PATH')          or define('CONF_PATH',FRAME_PATH.'Conf\\');
//定义视图目录,也就是模板存放目录
defined('TPL_PATH')           or define('TPL_PATH',SYS_PATH.'APP\\View\\');
//定义缓存目录
defined('RUNTIME_PATH')       or define('RUNTIME_PATH', SYS_PATH.'Runtime\\');
//定义html文件缓存的目录
defined('HTML_PATH')          or define('HTML_PATH', RUNTIME_PATH.'\\html');
//定义html文件的后缀
defined('HTML_FILE_SUFFIX')   or define('HTML_FILE_SUFFIX', 'html');

//开启会话
//session_start();
// 自动加载类库
spl_autoload_register('autoload');
function autoload($class){
    $name = strstr($class, '\\', true);
    //如果是系统框架(Core)下类文件,否则加载应用目录(APP_PATH)下的类文件
    if(in_array($name, array('Frame')) || is_dir(SYS_PATH.$name)){
        $class = SYS_PATH.$class.'.class.php';
    }else {
        $class = APP_PATH.$class.'.class.php';
    }
    if(!is_file($class)){
        E('类文件不存在:'.$class);
    }
    require_once $class;
}

//加载系统配置变量
if(file_exists(CONF_PATH.'config.php')){
    C(include CONF_PATH.'config.php');
}
//加载系统模式配置
if(file_exists(CONF_PATH.'Mode\\common.php')){
    C(include CONF_PATH.'Mode\\common.php');
}
//加载模式行为
$tags = C('tags');
if(isset($tags)){
    Hook::import($tags);
}
//判断缓存目录是否创建
if(!is_dir(RUNTIME_PATH)){
    //创建缓存目录
    mkdir(RUNTIME_PATH, 0700);
}
//判断是否加载默认模块
$module     = isset($_GET['m'])? $_GET['m']:C('DEFAULT_MODULE');
//判断控制器
$controller = ucfirst(isset($_GET['c'])? $_GET['c']:C('DEFAULT_CONTROLLER'));
//判断函数
$function   = isset($_GET['f'])? $_GET['f']:C('DEFAULT_FUNCTION');

//判断模块是否存在
if(!file_exists(APP_PATH.$module)){
    //判断默认模块是否为默认模块
    if(C('DEFAULT_MODULE') != $module)
        E('访问的模块不存在:'.APP_PATH.APP_PATH.$module);
    /*
     * 如果应用目录不存在,需要初始化创建
     * 支持自动部署
     * */
    if(!is_dir(APP_PATH)){
        //创建应用目录
        mkdir(APP_PATH, 0700);
        //创建公共文件目录
        mkdir(APP_PATH.'\\Common', 0700);
        //创建配置文件目录
        mkdir(APP_PATH.'\\Conf', 0700);
    }
    /*
     * 默认模块不存在,支持自动创建机制
     * */
    //创建默认模块目录
    mkdir(APP_PATH.C('DEFAULT_MODULE'), 0700);
    //创建控制器目录
    mkdir(APP_PATH.C('DEFAULT_MODULE').'\\'.C('DEFAULT_C_LAYER'), 0700);
    //创建视图目录
    mkdir(APP_PATH.C('DEFAULT_MODULE').'\\'.C('DEFAULT_V_LAYER'), 0700);
    //创建模型目录
    mkdir(APP_PATH.C('DEFAULT_MODULE').'\\'.C('DEFAULT_M_LAYER'), 0700);
    //创建行为目录
    mkdir(APP_PATH.C('DEFAULT_MODULE').'\\'.C('DEFAULT_B_LAYER'), 0700);
    //创建默认控制器文件
    $file=APP_PATH.C('DEFAULT_MODULE').'\\'.C('DEFAULT_C_LAYER').'\\'.C('DEFAULT_CONTROLLER').'Controller.class.php';
    $str = '<?php
namespace '.C("DEFAULT_MODULE").'\\'.C("DEFAULT_C_LAYER").';
use Frame\Controller;

class IndexController extends Controller{
    public function index(){
        echo "Welcome to LEOS Studio! ";
    }
}
?>';
    file_put_contents($file, $str);//创建默认控制器文件
    
}
//模块缓存目录不存在时创建
if(!is_dir(RUNTIME_PATH.$module)){
    mkdir(RUNTIME_PATH.$module, 0700);
}
/* *****************************************************************************
 * 加载应用配置变量
 * ****************************************************************************/
if(file_exists(APP_PATH.'\\Conf\\config.php')){
    C(include APP_PATH.'\\Conf\\config.php');
}
/* *****************************************************************************
 * 判断控制器是否存在
 * ****************************************************************************/
$c = APP_PATH.$module.'\\'.C('DEFAULT_C_LAYER').'\\'.$controller.'Controller.class.php';
if(!file_exists($c)){
    E('访问的控制器不存在:'.$c);
}

/* *****************************************************************************
 * 判断控制器下是否存在该方法
 * ****************************************************************************/
$obj = Tools::instance($module.'\\'.C('DEFAULT_C_LAYER').'\\'.$controller.'Controller');
if(!method_exists($obj, $function)){
    E('该控制器下不存在此方法:public function '.$function);
}

define('MODULE_NAME',$module);//传递全局模块目录
define('CONTROLLER_NAME',$controller);//传递全局控制器名

$obj->$function();//运行此方法
2.Controller.php
业务处理器基类Controller.class.php

<?php
/**
 *          Controller(Frame\Controller.class.php)
 *
 *    功  能:用户控制器类,用户业务逻辑控制器基础类
 *
 *    作  者:李伯特
 *    完成时间:2015/08/14
 *    修  改:2015/08/26 增加了ajaxReturn,处理客户端的异步请求
 *
 */
namespace Core;

use Core\Tools;

/*
 * Frame框架控制器基类,接口
 * */

abstract class Controller
{
    /*
     * 视图类对象 用于加载模板,渲染模板
     * */
    protected $view = null;
    /*
     * 控制器参数数组
     * */
    protected $config = array();

    /*
     * 重写控制器构造函数
     * 此处通过构造注入的方式注入视图类实例
     * */
    public function __construct()
    {
        $this->view = Tools::instance('Core\View');//实例化视图类View
        $this->_initialize();
    }

    protected function _initialize()
    {

    }

    /*
     * 控制器默认入口方法
     * */
    public function index()
    {
    }

    /*
     * 调用指定模板进行输出
     * param string $templateFile     模板文件名
     * param string $templateContent  模板内容,如果制定了模板内容,将使用此
     *                                模板进行渲染,这对模板文件存储在数据库
     *                                中的方式来说比较有用
     * */
    public function display($templateFile = '', $templateContent = '')
    {
        $this->view->display($templateFile, $templateContent);
    }

    /*
     * 获取页面输出的HTML内容
     * param string $templateFile     模板文件名
     * param string $templateContent  模板内容,如果制定了模板内容,将使用此
     *                                模板进行渲染,这对模板文件存储在数据库
     *                                中的方式来说比较有用
     * */
    public function fetch($templateFile = '', $templateContent = '')
    {
        $this->view->fetch($templateFile, $templateContent);
    }

    /*
     * 生成静态HTML文件,存储在本地磁盘上
     * param $htmlFile                 html文件名
     * param $htmlPath                 html文件路径
     * param $templateFile            用于渲染的模板文件
     * */
    public function buildHtml($htmlFile = '', $htmlPath = '', $templateFile = '')
    {
        $content = $this->fetch($templateFile);
        $htmlPath = !empty($htmlPath) ? $htmlPath : HTML_PATH;
        $htmlFile = $htmlPath . $htmlFile . HTML_FILE_SUFFIX;
        file_put_contents($htmlFile, $content);
        return $content;
    }

    /*
     * 模板变量赋值函数
     * param $name 模板变量名
     * paran $val  模板变量值
     * */
    public function assign($name, $val = '')
    {
        $this->view->assign($name, $val);
    }

    /*
     * 获取模板变量的值
     * param $name 模板变量名
     * */
    public function get($name = '')
    {
        return $this->view->get($name);
    }

    /*
     * ajax返回数据
     * */
    public function ajaxReturn($data, $type = '')
    {
        if (empty($type)) $type = C('DEFAULT_AJAX_DATA_TYPE');
        switch (strtoupper($type)) {
            case 'XML':
                header('Content-Type:text/xml; charset=utf-8');
                exit(xml_encode($data));
            case 'JSON':
                header('Content-Type:application/json; charset=utf-8');
                exit(json_encode($data));
        }
    }


}
3.View
在View.class.php文件中

<?php
/**
 *          View(Frame\View.class.php)
 *
 *    功  能:视图控制器类,主要为用户渲染模板提供接口
 *
 *    作  者:李伯特
 *    完成时间:2015/08/14
 *
 */
namespace Core;
use Core\Tools;

class View{
    
    /*
     * 模板变量数组,主要存储模板赋值变量
     * */
    protected $tVar = array();
    
    /*
     * 调用指定模板进行输出
     * param string $templateFile     模板文件名
     * param string $templateContent  模板内容,如果制定了模板内容,将使用此
     *                                模板进行渲染,这对模板文件存储在数据库
     *                                中的方式来说比较有用
     * */
    public function display($templateFile='', $templateContent='', $contentType='', $charset=''){
        $content = $this->fetch($templateFile, $templateContent);
        $this->render($content, $contentType, $charset);
    }
    
    /*
     * 获取页面输出的HTML内容
     * param string $templateFile     模板文件名
     * param string $templateContent  模板内容,如果制定了模板内容,将使用此
     *                                模板进行渲染,这对模板文件存储在数据库
     *                                中的方式来说比较有用
     * param return 页面输出内容,为html文本
     * */
    public function fetch($templateFile='', $templateContent=''){
        //根据配置选择模板引擎,并实例化一个对象
        $template = Tools::instance('Frame\Driver\Template\\'.C('TMPL_ENGINE_TYPE'));
        //调用引擎,获取编译后的HTML文档
        $content = $template->fetch($templateFile, $this->tVar, $templateContent);
        //返回解析后的内容
        return $content;
    }
    /*
     * 模板变量赋值函数
     * param $name 模板变量名 数组或字符串
     * paran $val  模板变量值
     * */
    public function assign($name, $val=''){
        is_array($name)?$this->tVar = array_merge($this->tVar,$name):$this->tVar[$name] = $val;
    }
    
    /*
     * 获取模板变量的值
     * param $name 模板变量名
     * */
    public function get($name=''){
        if('' === $name){
            return $this->tVar;
        }
        return isset($this->tVar[$name])?$this->tVar[$name]:false;
    }
    
    /*
     * 向前台输出HTML文档
     * param $content    编译后的内容
     * */
    public function render($content, $contentType='', $charset=''){
        if(empty($charset))  $charset = C('DEFAULT_CHARSET');
        if(empty($contentType)) $contentType = C('TMPL_CONTENT_TYPE');
        // 网页字符编码
        header('Content-Type:'.$contentType.'; charset='.$charset);
        header('Cache-control: '.C('HTTP_CACHE_CONTROL'));  // 页面缓存控制
        header('X-Powered-By:LEOS');
        echo $content;        
    }
}
4.数据库驱动
<?php
/**
 *           Model(Frame\Model.class.php)
 *
 *    功  能:模型类,提供了数据CURD操作接口
 *
 *    作  者:李伯特
 *    完成时间:2015/08/14
 *
 */
namespace Core;
use Core\Db;

class Model{
    const MODEL_INSERT         = 1;
    const MODEL_UPDATE         = 2;
    const MODEL_BOTH           = 3;
    //模型名称
    protected $name            = '';
    //数据库名
    protected $dbName          = '';
    //数据库连接配置
    protected $connection      = '';
    //当前操作的数据库对象句柄
    protected $dbHandle        = null;
    //表名前缀
    protected $tablePrefix     = '';
    //表名
    protected $tableName       = '';
    //当前模型主键
    protected $pk              = 'id';
    //实际的表名 包括表名前缀
    protected $realTableName   = '';
    //当前错误信息
    protected $error           = '';
    //主键是否自动增长
    protected $autoinc         = false;
    //字段信息数组
    protected $fields          = array();
    //字段名-字段值  K-V 数组 
    protected $data            = array();
    //SQL语句暂存数组
    protected $options         = array();
    //字段名-字段别名 K-V 数组,用于存储用户自定义字段名和实际表名之间的映射关系
    protected $map             = array();
    //动态调用方法名数组
    protected $methods         = array('order','alias','having','group','lock','distinct','auto','filter','validate','result','token');
    
    public  function __construct($name='', $tablePrefix='', $connection=''){
        //初始化分离出去,继承此类时只需要用户对初始化方法重写即可
        $this->_initialize();

        if(!empty($name)){
            //分析 数据库名.表名
            if(strpos($name, '.')){
                list($this->dbName, $this->name) = explode('.', $name);
            }else{
                $this->name = $name;
            }
        }elseif(empty($this->name)){
            $this->getModelName();
        }

        //表名前缀设置
        if(is_null($tablePrefix)){
            $this->tablePrefix = '';
        }elseif('' != $tablePrefix){
            $this->tablePrefix = $tablePrefix;
        }elseif(!isset($this->$tablePrefix)){
            $this->tablePrefix = C('DB_PREFIX');
        }
        //初始化数据库
        $this->getDB(0,empty($this->connection)?$connection:$this->connection,true);
    }
    protected function _initialize(){ }
    /**
     * 重写set方法
     */
    public function __set($name, $value){
        $this->data[$name] = $value;
    }
    /**
     * 重写get方法
     */
    public function __get($name){
        return isset($this->data[$name])? $this->data[$name]:null;
    }
    /**
     * 检测对象值的情况
     */
    public function __isset($name){
        return isset($this->data[$name]);
    }
    /**
     * 销毁对象值
     */
    public function __unset($name){
        unset($this->data[$name]);
    }
    /**
     * 重写__call()方法,实现动态方法的调用
     */
    public function __call($method, $args){
        if(in_array(strtolower($method), $this->methods,true)){
            //连贯操作的实现
            $this->options[strtolower($method)] = $args[0];
            return $this;
        }elseif(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){
            //统计查询
            $field = isset($args[0])?$args[0]:'*';
            return $this->getField(strtolower($method).'('.$field.') AS leos_'.$method);
        }elseif(strtolower(substr($method,0,5)) == 'getby'){
            //根据某个字段获取记录
            $field = parse_name(substr($method, 5));
            $where[$field] = $args[0];
            return $this->where($where)->find();
        }elseif(strtolower(substr($method,0,5)) == 'getfieldby'){
            //根据某个字段获取记录的某个值
            $name = parse_name(substr($method,10));
            $where[$name] = $args[0];
            return $this->where($where)->getField($args[1]);
        }elseif(isset($this->_scope[$method])){
            return $this->scope($method,$args[0]);
        }else{
            E(__CLASS__.':'.$method.'方法不存在');
            return;
        }
    }
    /*
     * 获取并设置当前模型名
     */
    public function getModelName(){
        if(empty($this->name)){
            $name = substr(get_class($this),0,-5);
            $pos = strrpos($name,'\\');
            if($pos){
                $this->name = substr($name, $pos+1);
            }else{
                $this->name = $name;
            }
        }
        return $this->name;
    }
    /**
     * 获取模型对应的表全名:[数据库名].[表前缀].[实际表名]
     * @return string
     */
    public function getTableName(){
        if(empty($this->realTableName)){
            $tableName = empty($this->tablePrefix) ? '' : $this->tablePrefix;
            if(!empty($this->tableName)){
                $tableName .= $this->tableName;
            }else{
                $tableName .= parse_name($this->name);//默认使用模型名称
            }
            $this->realTableName = strtolower($tableName);
        }
        return (!empty($this->dbName)?$this->dbName.'.':'').$this->realTableName;
    }
    /**
     * 获取数据表字段信息
     * @return boolean|Ambigous <boolean, multitype:>|multitype:
     */
    public function getDbFields(){
        if(isset($this->options['table'])){
            if(is_array($this->options['table'])){
                $table = key($this->options['table']);
            }else{
                $table = $this->options['table'];
                if(strpos($table, ')')){
                    return false;
                }
            }
            $fields = $this->dbHandle->getFields($table);
            return $fields ? array_keys($fields) : false;
        }

        if($this->fields){
            $fields = $this->fields;
            unset($fields['_type'],$fields['_pk']);
            return $fields;
        }
        return false;
    }
    /**
     * 获取一条记录的指定的字段值
     * @param string  $field      要查询的字段名
     * @param string  $separate   字段之间的分隔符
     */
    public function getField($field,$separate=null){
        $options['field'] = $field;
        $options          = $this->_parseOptions($options);
        //缓存字段信息,此处未实现
        $field = trim($field);
        if(strpos($field, ',')){//多字段处理
            if(!isset($options['limit'])){
                $options['limit'] = is_numeric($separate)?$separate:'';
            }
            $resultSet = $this->dbHandle->select($options);
            if(!empty($resultSet)){
                $_field = explode(',',$field);
                $field  = array_keys($resultSet[0]);//第一条记录的全部字段名
                $key    = array_shift($field);//第一个字段名
                $key2   = array_shift($field);//第二个字段名
                $cols   = array();
                $count  = count($_field);//要查询的字段数量
                foreach($resultSet as $result){
                    $name = $result[$key];//每条记录的第一个字段值,为主键值
                    if(2==$count){
                        $cols[$name] = $result[$key2];//如果只有两个字段,那么第一个字段值作为键第二个字段值为键值
                    }else{
                        $cols[$name] = is_string($separate)?implode($separate,$result):$result;
                    }
                }
                return $cols;
            }
        }else{//一个字段
            if(true != $separate){
                $options['limit'] = is_numeric($separate)?$separate:1;
            }
            $result = $this->dbHandle->select($options);
            if(!empty($result)){
                if(true !== $separate && 1==$options['limit']){
                    $data = reset($result[0]);
                    return $data;
                }
                foreach($result as $val){
                    $arr[] = $val[$field];
                }
                return $arr;
            }
        }
        return null;
    }
    /**
     * 获取主键名
     * @return string
     */
    public function getPk(){
        return $this->pk;
    }
    /**
     * 获取最后一条插入数据的id
     */
    public function getLastInsertID(){
        return $this->dbHandle->getLastInsertID();
    }
    /**
     * 对写入数据的数据进行合法性校验、安全过滤
     * @param unknown $data
     * @return multitype:
     */
    protected function _facade($data){
        //字段合法性校验
        if(!empty($this->fields)){
            if(!empty($this->options['field'])){
                $fields = $this->options['field'];
                unset($this->options['field']);
                if(is_string($fields)){
                    $fields = explode(',', $fields);
                }
            }else{
                $fields = $this->fields;
            }
            foreach ($data as $key=>$val){
                if(in_array($key, $fields,true)){
                    unset($data[$key]);
                }elseif(is_scalar($val)){
                    $this->_parseType($data, $key);
                }
            }
        }
        //安全过滤
        if(!empty($this->options['filter'])){
            $data = array_map($this->options['filter'],$data);
            unset($this->options['filter']);
        }
        return $data;
    }
    /**
     * 从映射数组中读取对应字段信息,赋值数组中,删除原字段信息
     * @param unknown $data
     * @return unknown
     */
    protected function _read_data($data){
        if(!empty($this->map) && C('READ_DATA_MAP')){
            foreach($this->map as $key=>$val){
                if(isset($data[$val])){
                    $data[$key] = $data[$val];
                    unset($data[$val]);
                }
            }
        }
        return $data;
    }
    //SQL语句暂存数组过滤
    protected function _options_filter(&$options){
    
    }
    /**
     * 数据类型校验
     * @param mixed   $data   待校验数组
     * @param string  $key    字段名
     */
    protected function _parseType(&$data,$key){
        if(!isset($this->options['bind'][':'.$key]) && isset($this->fields['_type'][$key])){
            $fieldType = strtolower($this->fields['_type'][$key]);
            if(false !== strpos($fieldType,'enum')){
                
            }elseif(false === strpos($fieldType,'bigint') && false !== strpos($fieldType,'int')){
                $data[$key] = intval($data[$key]);                
            }elseif(false !== strpos($fieldType,'float') || false !== strpos($fieldType,'double')){
                $data[$key] = floatval($data[$key]);
            }elseif(false !== strpos($fieldType,'bool')){
                $data[$key] = (bool)($data[$key]);
            }
        }
    }
    /**
     * 解析SQL语句暂存数组
     * @param  array  $options
     * @return array  $options
     */
    protected function _parseOptions($options=array()){
        //获取参数
        if(is_array($options)){
            $options = array_merge($this->options,$options);
        }
        //表名
        if(!isset($options['table'])){
            $options['table'] = $this->getTableName();
            $fields           = $this->fields;
        }else{
            $fields = $this->getDbFields();
        }
        //表别名
        if(!empty($options['alias'])){
            $options['table'] .= ' '.$options['alias'];
        }
        //模型名
        $options['model'] = $this->name;
        //字段验证
        if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])){
            //检查查询语句条件中字段的类型合法性
            foreach($options['where'] as $key=>$val){
                $key = trim($key);
                if(in_array($key, $fields,true)){
                    if(is_scalar($val)){
                        $this->_parseType($options['where'],$key);
                    }
                }elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key, '.')){
                    if(!empty($this->options['strict'])){
                        E('查询表达式错误,表中不存在此字段'.':['.$key.']');
                    }
                    unset($options['where'][$key]);//销毁非法字段
                }
            }
        }
        //清空数组,避免影响下次sql组装数组
        $this->options = array();
        $this->_options_filter($options);
        return $options;
    }
    /**
     * 解析SQL语句
     * @param string   $sql    待解析的SQL语句
     * @param mixed    $parse  解析选项
     * @return string  $sql    解析后的SQL语句
     */
    protected function _parseSql($sql,$parse){
        if(true === $parse){
            $options = $this->_parseOptions();
            $sql     = $this->dbHandle->parseSql($sql,$options); 
        }elseif(is_array($parse)){//sql预处理
            $parse = array_map(array($this->dbHandle,'escapeString'), $parse);
            $sql   = vsprintf($sql,$parse);
        }else{
            $sql = strtr($sql, array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
        }
        $this->db->setModel($this->name);
        return $sql;
    }
    /**
     * 返回查询的结果集
     * @param array   $data  查询返回的结果数组
     * @param string  $type
     * @return mixed
     */
    protected function returnResult($data,$type=''){
        if($type){
            if(is_callable($type)){
                return call_user_func($type,$data);
            }
            switch (strtolower($type)){
                case 'json':
                    return json_encode($data);
                case 'xml':
                    return xml_encode($data);
            }
        }
    }
    /*
     * 实例化一个数据对象并获取其句柄
     * @param int     $linkNum           数据库对象句柄号
     * @param string  $connectionParam   数据库连接参数
     * */
    protected function getDB($linkNum='',$connection='',$force=''){
        //必须指定链接号

        /*
        if('' === $linkNum) return false;

        static $dbArray = array();
        if(isset($dbArray[$linkNum])){
            if($force){
                $dbArray[$linkNum]->close();
                unset($dbArray[$linkNum]);
                $dbArray[$linkNum] = Db::getInstance($connection);
            }
        }else{
            $dbArray[$linkNum] = Db::getInstance($connection);
        }
         */
        if(is_null($this->dbHandle)){
            $this->dbHandle = Db::getInstance($connection);
        }
        return $this->dbHandle;
    }
    /**
     * 增加记录
     * @param  array   $data      要增加的记录值数组,如果该参数为空,则使用对象data数组
     * @param  unknown $options
     * @param  string  $replace
     * @return boolean|unknown
     */
    public function add($data='',$options=array(),$replace=false){
        if(empty($data)){
            if(!empty($this->data)){
                $data = $this->data;
                $this->data = array();
            }else{
                $this->error = '请输入要添加的数据';
                return false;
            }
        }
        $options = $this->_parseOptions($options);
        $data    = $this->_facade($data);//合法性校验
        //写入至数据库
        $result = $this->dbHandle->insert($data,$options,$replace);
        if(false !== $result){
            $insertId = $this->getLastInsertID();
            if($insertId){
                $data[$this->getPk()] = $insertId;
                return $insertId;
            }
        }
        return $result;
    }
    public function selectAdd($fields='',$table='',$options=array()){
        $options = $this->_parseOptions($options);
        if(false === $result=$this->dbHandle->selectInsert($fields?$fields:$options['fields'],$table?$table:$this->getTableName(),$options)){
            $this->error = 'selectAdd操作失败';
            return false;
        }else{
            return $result;
        }
    }
    /**
     * 删除记录
     * @param array $options SQL数组
     * @return Ambigous <boolean, unknown>|boolean|unknown
     */
    public function delete($options=array()){
        if(empty($options) && empty($this->options['where'])){
            if(!empty($this->data) && isset($this->data[$this->getPk()])){
                return $this->delete($this->data[$this->getPk()]);
            }else{
                return false;
            }
        }
        $pk = $this->getPk();
        if(is_numeric($options) || is_string($options)){
            if(strpos($options, ',')){
                $where[$pk] = array('IN',$options);
            }else{
                $where[$pk] = $options;
            }
            $options          = array();
            $options['where'] = $where;
        }
        $options = $this->_parseOptions($options);
        if(is_array($options['where']) && isset($options['where'][$pk])){
            $pkValue = $options['where'][$pk];
        }
        $result = $this->dbHandle->delete($options);
        if(false !== $result){
            $data = array();
            if(isset($pkValue)) $data[$pk] = $pkValue;
        }
        return $result;
    }
    /**
     * 查询记录
     * @param unknown $options
     * @return string|boolean|NULL|multitype:
     */
    public function select($options=array()){
        $pk = $this->getPk();
        if(is_string($options) || is_numeric($options)){
            if(strpos($options,',')){
                $where[$pk]  = array('IN',$options);
            }else{
                $where[$pk]  = $options;
            }
            $options = array();
            $options['where'] = $where;
        }elseif(false === $options){
            $options = array();
            $options = $this->_parseOptions($options);
            return '( '.$this->dbHandle->buildSelectSql($options).' )';
        }
        $options    = $this->_parseOptions($options);
        $resultSet  = $this->dbHandle->select($options);
        if(false === $resultSet){
            return false;
        }
        if(empty($resultSet)){
            return null;
        }
        $resultSet = array_map(array($this,'_read_data'), $resultSet);
        return $resultSet;
    }
    /**
     * 查询一条记录
     * @param  array $options SQL语句暂存数组
     * @return boolean|NULL|unknown|Ambigous <multitype:, unknown>
     */
    public function find($options=array()){
        if(is_numeric($options) || is_string($options)){
            $where[$this->getPk()] = $options;
            $options               = array();
            $options['where']      = $where;
        }
        $pk = $this->getPk();//获取主键
        //复合主键查询
        if(is_array($options) && (count($options) > 0) && is_array($pk)){
            
        }
        $options['limit'] = 1;
        $options          = $this->_parseOptions($options);
        $resultSet        = $this->dbHandle->select($options);
        if(false === $resultSet){
            return false;
        }
        if(empty($resultSet)){
            return null;
        }
        if(is_string($resultSet)){
            return $resultSet;
        }
        //结果处理
        $data = $this->_read_data($resultSet[0]);
        if(!empty($this->options['result'])){
            return $this->returnResult($data,$this->options['result']);
        }
        $this->data = $data;
        return $this->data;
    }
    public function save($data='',$options=array()){
        if(empty($data)){
            if(empty($this->data)){
                $this->error = '请输入用于跟新的数据';
                return false;
            }else{
                $data = $this->data;
                $this->data = array();
            }  
        }
        $data    = $this->_facade($data);
        $options = $this->_parseOptions($options);
        $pk      = $this->getPk();//主键名
        if(!isset($options['where'])){
            if(isset($data[$pk])){
                $where[$pk] = $data[$pk];
                $options['where'] = $where;
                unset($data[$pk]);//主键不能参加更新,此处必须销毁
            }else{
                $this->error = '未输入记录的唯一性标识,系统无法获知要更新哪些记录';
                return false;
            }
        }
        if(is_array($options['where']) && isset($options['where'][$pk])){
            $pkValue = $options['where'][$pk];
        }
        $result = $this->dbHandle->update($data,$options);
        if(false !== $result){
            if(isset($pkValue)) $data[$pk] = $pkValue;
        }
        return $result;
    }
    public function join($join,$type='INNER'){
        $prefix = $this->tablePrefix;
        if(is_array($join)){
            foreach ($join as $key=>&$_join){
                $_join = preg_replace_callback('/__([A-Z_-]+)__/sU', function($matches) use($prefix){return $prefix.strtolower($matches[1]);}, $_join);
                $_join = false !== stripos($_join, 'JOIN')? $_join : $type.' JOIN '.$_join;
            }
            $this->options['join'] = $join;
        }elseif(!empty($join)){
            $join = preg_replace_callback('/__([A-Z_-]+)__/sU', function($matches)use($prefix){return $prefix.strtolower($matches[1]);}, $join);
            $this->options['join'][] = false !== stripos($join, 'JOIN')? $join : $type.' JOIN '.$join;
        }
        return $this;
    }
    public function union($union,$all=false){
        if(!empty($union)){
            if($all){
                $this->options['union']['_all'] = true;
            }
            if(is_object($union)){
                $union = get_object_vars($union);
            }
            if(is_string($union)){
                $prefix = $this->tablePrefix;
                $options = preg_replace('/__([A-Z_-]+)/sU', function($matches)use($prefix){return $prefix.strtolower($matches[1]);}, $union);
            }
            if(is_array($union)){
                if(isset($union[0])){
                    $this->options['union'] = array_merge($this->options['union'],$union);
                }else{
                    $options = $union;
                }
            }
            $this->options['union'][] = $options;
        }
        return $this;
    }
    /**
     * 赋值查询条件
     * @param mixed $where   条件表达式
     * @param mixed $param   参数
     * @return \Core\Model
     */
    public function where($where,$param=null){
        if(!is_null($param) && is_string($where)){
            if(!is_array($param)){
                $param = func_get_arg();
                array_shift($param);
            }
            $param = array_map(array($this->dbHandle,'escapeString'),$param);
            $where = vsprintf($where, $param);
        }elseif (is_object($where)){
            $where = get_object_vars($where);
        }
        if(is_string($where) && '' != $where){
            $map = array();
            $map['_string'] = $where;
            $where = $map;
        }
        if(isset($this->options['where'])){
            $this->options['where'] = array_merge($this->options['where'],$where);
        }else{
            $this->options['where'] = $where;
        }
        return $this;
    }
    /**
     * 指定SQL语句 字段名
     * @param unknown $field
     * @param string $except
     * @return \Core\Model
     */
    public function field($field, $except=false){
        if(true === $field){
            $fields = $this->getDbFields();
            $field  = $fields?$fields:'*';
        }elseif($except){
            if(is_string($field)){//先转换成数组
                $field = explode(',',$field);
            }
            $fields = $this->getDbFields();
            $field  = $fields?array_diff($fields, $field):$field;
        }
        $this->options['field'] = $field;
        return $this;
    }
    
    /**
     * 提取当前的数据表
     * @param unknown $table
     */
    public function table($table){
        $prefix = $this->tablePrefix;
        if(is_array($table)){
            $this->options['table'] = $table;
        }elseif(!empty($table)){
            // 完整表名:前缀+表名
            $table = preg_replace_callback('/__([A-Z0-9_-]+)__/sU', function($matches) use($prefix){return $prefix.strtolower($matches[1]);}, $table);
            $this->options['table'] = $table;
        }
        return $this;
    }
    /**
     * 指定查询数量
     * @param unknown $offset
     * @param string $length
     * @return \Core\Model
     */
    public function limit($offset,$length=null){
        $this->options['limit'] =   is_null($length)?$offset:$offset.','.$length;
        return $this;
    }
    /**
     * 指定分页
     * @param unknown $page
     * @param string $listRows
     * @return \Core\Model
     */
    public function page($page,$listRows=null){
        $this->options['page'] = is_null($listRows)? $page : $page.','.$listRows;
        return $this;
    }
    /**
     * 提交事务
     */
    public function commit(){
        return $this->dbHandle->commit();
    }
    /**
     * 回滚事务
     */
    public function rollback(){
        return $this->dbHandle->rollback();
    }
    /**
     * 启动事务
     */
    public function startTrans(){
        $this->dbHandle->commit();
        $this->dbHandle->startTrans();
        return ;
    } 
}
5.数据库驱动接口

接口

<?php
/**
 *          Db(Frame\Db.class.php)
 *
 *    功  能:数据库操作基础类,提供数据库操作公共接口,数据库访问适配器
 *
 *    作  者:李康
 *    完成时间:2015/08/14
 *
 */
namespace Core;

abstract  class Db{
    // 数据库操作对象资源池
    static private    $instance   = array();
    // 当前数据库操作对象
    static private    $_instance  = null;

    /**
     * 获取一个数据库操作实例 工厂方法创建一个数据库操作对象
     * @param array $config
     * @return null
     */
    static public function getInstance($config=array()){
        // 将配置信息作为与之对应的数据库操作对象唯一标识,进行md5混淆操作,配置信息发生改变时,自动创建一个新的数据库操作对象
        $id = md5(serialize($config));
        // 如果资源池中没有与之对应的实例,则进行创建
        if (!isset(self::$instance[$id])) {
            // 解析配置数组
            $option = self::parseConfig($config);
            // 得到类全名
            $class = 'Core\\Driver\\DB\\' . ucwords(strtolower($option['type']));
            // 类文件是否存在,系统自动调用auto_load函数去加载类文件
            if (class_exists($class)) {
                // 实例化一个对象
                self::$instance[$id] = new $class($option);
            } else {
                E('该类未定义');
            }
        }
        // 设置当前数据库操作对象
        self::$_instance = self::$instance[$id];
        // 返回当前数据库操作对象
        return self::$_instance;
    }
    
    static private function parseConfig($config){
        if(!empty($config)){
            if(is_string($config)){
                return self::parseDsn($config);
            }
            $config = array_change_key_case($config);
            $config = array(
                'type'          =>  $config['db_type'],
                'username'      =>  $config['db_user'],
                'password'      =>  $config['db_pwd'],
                'hostname'      =>  $config['db_host'],
                'hostport'      =>  $config['db_port'],
                'database'      =>  $config['db_name'],
                'dsn'           =>  isset($config['db_dsn'])?$config['db_dsn']:null,
                'params'        =>  isset($config['db_params'])?$config['db_params']:null,
                'charset'       =>  isset($config['db_charset'])?$config['db_charset']:'utf8',
            );
        }else{
            $config = array(
                'type'          =>  C('DB_TYPE'),
                'hostname'      =>  C('DB_HOST'),
                'username'      =>  C('DB_USER'),
                'password'      =>  C('DB_PWD'),
                'hostport'      =>  C('DB_PORT'),
                'database'      =>  C('DB_NAME'),
                'dsn'           =>  C('DB_DSN'),
                'params'        =>  C('DB_PARAMS'),
                'charset'       =>  C('DB_CHARSET',null,'utf8'),
            );
        }
        return $config;
    }
    static private function parseDsn($config){
        return $config;
    }
    /**
     * 绑定参数值至bind[]数组中
     * @param unknown $name
     * @param unknown $value
     */
    protected function bindParam($name,$value){
        $this->bind[':'.$name] = $value;
    }
    /**
     * 参数绑定分析
     * @param unknown $bind
     * @return multitype:
     */
    protected function parseBind($bind){
        $bind = array_merge($this->bind,$bind);
        $this->bind = array();
        return $bind;
    }
    /**
     * 设置锁
     * @param string $lock
     * @return string
     */
    protected function parseLock($lock=false){
        if(!$lock) return '';
        if('ORACLE' == $this->dbType){
            return ' FOR UPDATE NOWAIT ';
        }
        return ' FOR UPDATE ';
    }
    /**
     * 解析SQL语句中的字段值
     * 合法性处理
     * @param unknown $value
     * @return Ambigous <string, multitype:>
     */
    protected function parseValue($value){
        if(is_string($value)){
            $value = '\''.$this->escapeString($value).'\'';
        }elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){
            $value = $this->escapeString($value[1]);
        }elseif(is_array($value)){
            $value = array_map(array($this,'parseValue'), $value);
        }elseif(is_bool($value)){
            $value = $value?'1':'0';
        }elseif(is_null($value)){
            $value = 'null';
        }
        return $value;
    }
    /**
     * 解析SQL关键字 DINSTINCT
     * @param  string   $str
     * @return string
     */
    protected function parseDinstinct($TorF){
        return !empty($TorF)? ' DINSTINCT ' : '';
    }
    /**
     * 解析SQL语句中字段,定义别名时自动处理
     * @param unknown $fields
     * @return string
     */
    protected function parseField($fields){
        $fieldsStr = '';
        if(is_string($fields) && strpos($fields, ',')){
            $fields = explode(',', $fields);
        }
        if(is_array($fields)){
            $arr = array();
            foreach ($fields as $key=>$field){
                if(!is_numeric($key)){//字段别名
                    $arr[] = $this->parseKey($key).' AS '.$this->parseKey($field);
                }else{
                    $arr[] = $this->parseKey($field);
                }
            }
            $fieldsStr = implode(',', $arr);
        }elseif(is_string($fields) && !empty($fields)){
            $fieldsStr = $this->parseKey($fields);
        }else{
            $fieldsStr = '*';
        }
        return $fieldsStr;
    }
    /**
     * 解析SQL语句中关键字Join
     * @param unknown $join
     * @return string
     */
    protected function parseJoin($join){
        $joinStr = '';
        if(!empty($join)){
            $joinStr = ' '.implode(' ',$join).' ';
        }
        return $joinStr;
    }
    /**
     * 解析SQL语句关键字WHERE
     * @param unknown $where
     * @return string
     */
    protected function parseWhere($where){
        $whereStr = '';
        if(is_string($where)){
            $whereStr = $where;
        }else{
            $operate = isset($where['_logic'])?strtoupper($where['_logic']):'';
            if(in_array($operate, array('AND','OR','XOR'))){
                $operate = ' '.$operate.' ';
                unset($where['_logic']);
            }else{
                $operate = ' AND ';
            }
            foreach ($where as $key=>$val){
                $whereStr .= '( ';
                if(is_numeric($key)){
                    $key = '_complex';
                }
                if(0===strpos($key, '_')){
                    switch ($key){
                        case '_string':
                            $whereStr .= $val;
                            break;
                        case '_complex':
                            $whereStr .=is_string($val)? $val : substr($this->parseWhere($val), 6);
                            break;
                        case '_query':
                            parse_str($val,$where);
                            if(isset($where['_logic'])){
                                $op = ' '.strtoupper($where['_logic']).' ';
                                unset($where['_logic']);
                            }else{
                                $op = ' AND ';
                            }
                            $arr = array();
                            foreach($where as $field=>$data){
                                $arr[] = $this->parseKey($field).' = '.$this->parseValue($data);
                            }
                            $whereStr .= implode($op, $arr);
                    }
                }else{
                    if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/', trim($key))){
                        E('表达式错误:'.$key);
                    }
                    $multi = is_array($val) && isset($val['_multi']);//多条件
                    $key   = trim($key);
                    if(strpos($key, '|')){
                        $arr   = explode('|', $key);
                        $str   = array();
                        foreach($arr as $m=>$k){
                            $v = $multi?$val[$m]:$val;
                            $str[] = '('.$this->parseWhereItem($this->parseKey($k), $v).')';
                        }
                        $whereStr .= implode(' OR ', $str);
                    }elseif(strpos($key, '&')){
                        $arr = explode('&', $key);
                        $str = array();
                        foreach($arr as $m=>$k){
                            $v = $multi?$val[$m]:$val;
                            $str[] = '('.$this->parseWhereItem($this->parseKey($k), $v).')';
                        }
                        $whereStr .= implode(' AND ',$str);
                    }else{
                        $whereStr .= $this->parseWhereItem($this->parseKey($k), $val);
                    }
                }
                $whereStr .= ' )'.$operate;
            }//end foreach
            $whereStr = substr($whereStr,0,-strlen($operate));//去掉最后一个多余的$operate
        }
        return empty($whereStr)?'':' WHERE '.$whereStr;
    }
    protected function parseWhereItem($key,$val){
        $whereStr = '';
        if(is_array($val)){
            if(is_string($val[0])){
                if(preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i', $val[0])){
                    $whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);
                }elseif(preg_match('/^(NOTLIKE|LIKE)$/i', $val[0])){
                    if(is_array($val[1])){
                        $likeLogic = isset($val[2])?strtoupper($val[2]):'OR';
                        if(in_array($likeLogic, array('AND','OR','XOR'))){
                            $likeStr = $this->comparison[strtolower($val[0])];
                            $like    = array();
                            foreach ($val[1] as $item){
                                $like[] = $key.' '.$likeLogic.' '.$this->parseValue($item);
                            }
                            $whereStr .= '('.implode(' ', $likeLogic.' ',$like).')';
                        }
                    }else{
                        $whereStr .= $key.' '.$this->comparison[strtolower($val[0])].$this->parseValue($val[1]);
                    }
                }elseif('exp'==strtolower($val[0])){
                    $whereStr .= ' ('.$key.' '.$val[1].') ';
                }elseif(preg_match('/IN/i', $val[0])){
                    if(isset($val[2]) && 'exp'==$val[2]){
                        $whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];
                    }else{
                        if(is_string($val[1])){
                            $val[1] = explode(',', $val[1]);
                        }
                        $area = implode(',', $this->parseValue($val[1]));
                        $whereStr .= $key.' '.strtoupper($val[0]).' ('.$area.')';
                    }
                }elseif(preg_match('/BETWEEN/i', $val[0])){
                    $data = is_string($val[1])?explode(',', $val[1]):$val[1];
                    $whereStr .= ' ('.$key.' '.strtoupper($val[0]).' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]).') ';
                }else{
                    E('表达式错误:'.$val[0]);
                }
            }else{
                $count = count($val);
                $rule  = isset($val[$count-1])?(is_array($val[$count-1])?strtoupper($val[$count-1][0]):strtoupper($val[$count-1])):'';
                if(in_array($rule, array('AND','OR','XOR'))){
                    $count = $count-1;
                }else{
                    $rule = 'AND';
                }
                for($i=0;$i<$count;$i++){
                    $data = is_array($val[$i])?$val[$i][1]:$val[$i];
                    if('exp'==strtolower($val[$i][0])){
                        $whereStr .= '('.$key.' '.$data.')'.$rule.' ';
                    }else{
                        $whereStr .= '('.$this->parseWhereItem($key, $val[$i]).') '.$rule.' ';
                    }
                }
                $whereStr = substr($whereStr,0,-4);
            }
        }else{
            $whereStr .= $key.' = '.$this->parseValue($val);
        }
        return $whereStr;
    }
    /**
     * 解析SQL语句关键字GROUP
     * @param unknown $group
     * @return string
     */
    protected function parseGroup($group){
        return !empty($group)? ' GROUP BY '.$group:'';
    }
    /**
     * 解析SQL语句关键字HAVING
     * @param unknown $having
     * @return string
     */
    protected function parseHaving($having){
        return !empty($having)? ' HAVING '.$having:'';
    }
    protected function parseOrder($order){
        if(is_array($order)){
            $arr = array();
            foreach($order as $key=>$val){
                if(is_numeric($key)){
                    $array[] = $this->parseKey($val);
                }else{
                    $array[] = $this->parseKey($key).' '.$val;
                }
            }
            $order = implode(',', $arr);
        }
        return !empty($order)? ' ORDER BY '.$order:'';
    }
    /**
     * 解析SQL语句LIMIT关键字
     * @param unknown $limit
     * @return string
     */
    protected function parseLimit($limit){
        return !empty($limit)?' LIMIT '.$limit.' ':'';
    }
    /**
     * 解析SQL语句关键字UNION
     * @param unknown $union
     * @return string
     */
    protected function parseUnion($union){
        if(empty($union)) return'';
        if(isset($union['_all'])){
            $str = 'UNION ALL ';
            unset($union['_all']);
        }else{
            $str = 'UNION ';
        }
        $sql = array();
        foreach($union as $u){
            $sql[] = $str.(is_array($u)) ? $this->buildSelectSql($u) : $u;
        }
        return implode(' ', $sql);
    }
    /**
     * 解析SQL语句中的注释
     * @param unknown $comment
     * @return string
     */
    protected function parseComment($comment){
        return !empty($comment)?' /*'.$comment.' */':'';
    }
    /**
     *  表名分析
     * @param string|array $tables
     * @return string
     */
    protected function parseTable($tables){
        if(is_array($tables)){
            $arr = array();
            foreach ($tables as $table=>$alias){
                if(is_numeric($table)){
                    $arr[] = $this->parseKey($table).' '.$this->parseKey($alias);//别名表解析
                }else{
                    $arr[] = $this->parseKey($table);
                }
                $tables = $arr;
            }
        }elseif(is_string($tables)){
            $tables = explode(',', $tables);
            array_walk($tables, array(&$this,'parseKey'));
        }
        $tables = implode(',', $tables);
        return $tables;
    }
    /**
     * 解析SQL语句
     * @param string $sql
     * @param array $option
     * @return mixed
     */
    protected function parseSql($sql,$option){
        $sql = str_replace(
            array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%COMMENT%'),
            array(
                $this->parseTable($option['table']),
                $this->parseDinstinct(isset($option['distinct'])?$option['distinct']:false),
                $this->parseField(!empty($option['field'])?$option['field']:'*'),
                $this->parseJoin(!empty($option['join'])?$option['join']:''),
                $this->parseWhere(!empty($option['where'])?$option['where']:''),
                $this->parseGroup(!empty($option['group'])?$option['group']:''),
                $this->parseHaving(!empty($option['having'])?$option['having']:''),
                $this->parseOrder(!empty($option['order'])?$option['order']:''),
                $this->parseLimit(!empty($option['limit'])?$option['limit']:''),
                $this->parseUnion(!empty($option['union'])?$option['union']:''),
                $this->parseComment(!empty($option['comment'])?$option['comment']:''),
            ),$sql);
        return $sql;
    }

    /**
     * 增加记录
     * @access public
     * @param mixed $data 数据
     * @param array $options 参数表达式
     * @param boolean $replace 是否replace
     * @return false | integer
     */
    public function insert($data,$options=array(),$replace=false){
        $values = $fields = array();
        $this->modelName = $options['model'];
        foreach($data as $key=>$val){
            if(is_array($val) && 'exp' == $val[0]){
                $fields[] = $this->parseKey($key);
                $values[] = $val[1];
            }elseif(is_scalar($val || is_null($val))){
                $fields[] = $this->parseKey($key);//字段名处理
                if(C('DB_BIND_PARAM') && 0 !==strpos($val, ':')){//绑定参数
                    $name = md5($key);
                    $values[] = ':'.$name;
                    $this->bindParam($name,$val);
                }else{//未绑定参数
                    $values[] = $this->parseValue($val);
                }
            }
        }
        $sql  = ($replace?'REPLACE':'INSERT').' INTO '
            .$this->parseTable($options['table'])
            .' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')'
            .$this->parseLock(isset($options['lock'])?$options['lock']:false)
            .$this->parseComment(!empty($options['commnet'])?$options['commnet']:'');
        return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
    }
    /**
     * 删除记录
     * @param unknown $options
     */
    public function delete($options=array()){
        $this->modelName = $options['model'];
        $sql = 'DELETE FROM '
            .$this->parseTable($options['table'])
            .$this->parseWhere(!empty($options['where'])?$options['where']:'')
            .$this->parseOrder(!empty($options['oeder'])?$options['order']:'')
            .$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
            .$this->parseLock(isset($options['lock'])?$options['lock']:false)
            .$this->parseComment(!empty($options['comment'])?$options['comment']:'');
        return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
    }
    /**
     * 查询记录
     * @param unknown $options
     * @return unknown
     */
    public function select($options=array()){
        $this->modelName = $options['model'];
        $sql             = $this->buildSelectSql($options);
        $result          = $this->query($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
        return $result;
    }
    /**
     * 选择插入数据
     */
    public function selectInsert($fields,$table,$options=array()){
        $this->modelName = $options['model'];
        if(is_string($fields)) $fields = explode(',', $fields);
        array_walk($fields, array($this,'parseKey'));
        $sql  = 'INSERT INTO '.$this->parseTable($table).' ('.implode(',', $fields).') ';
        $sql .= $this->buildSelectSql($options);
        return $this->execute($sql,$this->parseBind(!empty($options['bind'])? $options['bind'] : array()));
    }
    /**
     * 更新记录
     * @param unknown $data
     * @param unknown $options
     */
    public function update($data,$options){
        $this->modelName = $options['model'];
        foreach ($data as $key=>$val){
            if(is_array($val) && 'exp' == $val[0]){
                $set[] = $this->parseKey($key).'='.$val[1];
            }elseif (is_scalar($val) || is_null($val)){
                if(C('DB_BIND_PARAM') && 0 !== strpos($val, ':')){
                    $name = md5($key);
                    $set[] = $this->parseKey($key).'=:'.$name;
                    $this->bindParam($name, $val);
                }else{
                    $set[] = $this->parseKey($key).'='.$this->parseValue($val);
                }
            }
        }
        $setStr = ' SET '.implode(',', $set);
        $sql = 'UPDATE '
            .$this->parseTable($options['table'])
            .$setStr
            .$this->parseWhere(!empty($options['where'])?$options['where']:'')
            .$this->parseOrder(!empty($options['order'])?$options['order']:'')
            .$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
            .$this->parseLock(!empty($options['lock'])?$options['lock']:false)
            .$this->parseComment(!empty($options['comment'])?$options['comment']:'');
        
        return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));    
    }
    /**
     * 生成SQLselect字符串
     * @param unknown $options
     * @return string
     */
    public function buildSelectSql($options=array()){
        //如果设置了分页,处理分页
        if(isset($options['page'])){
            if(strpos($options['page'], ',')){
                list($page,$listRows) = explode(',', $options['page']);
            }else{
                $page = $options['page'];
            }
            $page     = $page?$page:1;
            $listRows = isset($listRows)?$listRows:(is_numeric($options['limit'])?$options['limit']:20);
            $offset   = $listRows*((int)$page-1);
            $options['limit'] = $offset.','.$listRows;
        }
        $sql  = $this->parseSql($this->selectSql, $options);
        $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
        return $sql;
    }
    /**
     * 返回模型的错误信息
     * @access private
     * @return string
     */
    public function getError(){
        return $this->error;
    }
    /**
     * 返回最后插入的ID
     * @access private
     * @return string
     */
    public function getLastInsertID(){
        return $this->lastInsertID;
    }
    /**
     * 返回最后执行的sql语句
     * @access private
     * @return string
     */
    public function getLastSql(){
        return $this->queryStr;
    }
    /**
     * 设置模型名
     * @access private
     * @param  string $name
     */
    public function setModel($name){
        $this->modelName = $name;
    }
    /////////////////////////////// 接口约束 ////////////////////////////////////////
    //字段约束
    //数据库类型
    protected  $dbType       = null;
    //数据库连接句柄数组 
    protected  $linkID       = array();
    //当前数据库连接句柄
    protected  $_linkID      = null;
    //数据库连接状态
    protected  $connected    = false;
    //数据库连接参数配置数组
    protected  $config       =array();
    protected  $numRows      = '';
    protected  $numCols      = '';
    //当前执行的SQL语句
    protected  $queryStr     = '';
    //最后插入记录ID
    protected  $lastInsertID = null;
    //当前操作的模型名
    protected  $modelName    = '';
    //当前查询结果集
    protected  $queryResult  = null;
    //受影响的行数
    protected  $affectedRows = 0;
    //事务指令数
    protected  $transTimes   = 0;
    //数据库表达式
    protected  $comparison   = array('eq'=>'=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN');
    //查询SQL语句格式串
    protected  $selectSql    = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%';
    //绑定的参数数组
    protected  $bind         = array();
    //执行sql语句的错误信息
    protected  $error        = '';


    //方法约束,对子类的实现进行规约,这部分是与特定数据库关联比较紧密的部分,不同数据库实现方式不尽相同
    //abstract protected function initConnect();
    abstract protected function escapeString($str);

    /**
     * 解析SQL语句中的关键字
     * @param $key
     * @return mixed
     */
    abstract protected function parseKey(&$key);

    /**
     * 释放当前的结果集
     */
    abstract protected function free();

    /**
     * 关闭当前数据库连接
     */
    abstract protected function close();

    /**
     * 执行一条SQL语句
     * @param $sql
     * @return mixed 执行失败返回false,执行成功返回结果集(数组)
     */
    abstract protected function query($sql);

    /**
     * 执行一条SQL语句,返回执行情况
     * @param $sql
     * @return mixed 如果执行成功返回受影响的行数,如果执行失败返回False
     */
    abstract protected function execute($sql);

    /**
     *
     * @param $dataList
     * @param $options
     * @param $replace
     * @return mixed
     */
    abstract public    function insertAll($dataList,$options,$replace);

    /**
     * 提交事务
     * @return mixed
     */
    abstract public    function commit();

    /**
     * 回滚事务
     * @return mixed
     */
    abstract public    function rollback();

    /**
     * 启动事务传输
     * @return mixed
     */
    abstract public    function startTrans();

    /**
     * 获取表字段信息数组
     * @param $tableName
     * @return array
     */
    abstract public    function getFields($tableName);
    
    /**
     * 析构方法
     */
    public function __destruct(){
        //检查并释放结果集
        if($this->queryResult){
            $this->free();
        }
        //关闭数据库连接
        $this->close();
    }
}
实现:

<?php
/**
 *          Mysqli(Frame\Driver\DB\Mysqli.class.php)
 *
 *    功  能:MySQL数据库访问接口
 *
 *    作  者:李伯特
 *    完成时间:2015/08/14
 *    修改时间:2016/08/26
 *
 */
namespace Frame\Driver\DB;
use Frame\Db;

class Mysqli extends Db{
 
    public function __construct($config=''){
        if(!empty($config)){
            $this->config = $config;
            if(empty($this->config['params'])){
                $this->config['params'] = '';
            }
        }
    }

    /**
     * 创建并返回数据库连接
     * @param string $config          数据库配置参数
     * @param int    $linkNum         数据库连接资源号
     * @return mixed                  返回一个数据库连接资源
     */
    private function connect($config='',$linkNum=0){
        // 根据资源号检查数据库连接资源池中对应资源是否存在
        if(!isset($this->linkID[$linkNum])){
            // 如果数据库配置资源为空,则使用默认配置参数
            if(empty($config)) $config = $this->config;
            /**
             * 实例化:\mysqli()  前面的'\'不可去掉(表示根命名空间),不然默认是当前命名空间的类冲突
             * 此处的错误查找了半天时间才找出来,犯这样的低级错误还是基本功不扎实
             */
            $this->linkID[$linkNum] = new \mysqli($config['hostname'],$config['username'],$config['password'],$config['database'],$config['hostport']?$config['hostport']:3306);
            // 检测并显示数据库错误信息
            if(mysqli_connect_error()) E('[数据库连接错误]'.mysqli_connect_errno().':'.mysqli_connect_error());
            // 设置数据库编码
            $this->linkID[$linkNum]->query("SET NAMES '".C('DB_CHARSET')."'");
            // 当前连接状态为正常
            $this->connected = true;
        }
        // 返回资源号对应的数据库连接资源
        return $this->linkID[$linkNum];
    }

    /**
     * 以关联数组的格式从结果集中获取全部的数据
     * @return array 结果数组
     */
    private function getAll(){
        $result = array();
        if($this->numRows>=0){
            // 循环读取数据并存入数组中
            for($i=0;$i<$this->numRows;$i++){
                $result[$i] = $this->queryResult->fetch_assoc();//关联数组 【K:字段名】<-->【V:数据】
            }
            // 复位数据指针
            $this->queryResult->data_seek(0);
        }
        // 返回结果数组
        return $result;
    }

    /**
     * 返回执行SQL错误信息
     * @return string
     */
    private function error(){
        $this->error = $this->_linkID->errno.':'.$this->_linkID->error;
        if('' != $this->queryStr){
            $this->error .= "\n [SQL语句]:".$this->queryStr;
        }
        return $this->error;
    }
    protected function escapeString($str){
        if($this->_linkID){
            return $this->_linkID->real_escape_string($str);
        }else{
            return addslashes($str);
        }
    }
    /**
     * 释放当前暂存的结果集
     */
    protected function free(){
        $this->queryResult->free_result();
        $this->queryResult = null;
    }
    /**
     * 关闭当前数据库连接
     * @see \Frame\Db::close()
     */
    protected function close(){
        if($this->_linkID){
            $this->_linkID->close();
        }
        $this->_linkID = null;
    }
    //接口方法
    /**
     * 执行一条SQL语句并返回结果
     * @param $sql
     * @return array|bool 如果执行失败返回false,执行成功返回结果集
     */
    public function query($sql){
        // 创建数据库连接
        $this->_linkID =$this->Connect();
        // 当前数据库连接资源不存在,返回False
        if(!$this->_linkID) return false;
        // 记录当前正在执行的SQL
        $this->queryStr = $sql;
        // 销毁之前的结果集
        if($this->queryResult) $this->free();
        // 执行SQL,保存最新结果集
        $this->queryResult = $this->_linkID->query($sql);
        // 释放其余结果集
        if($this->_linkID->more_results()){
            while(($results = $this->_linkID->next_result()) != NULL){
                $results->free_result();
            }
        }
        // 如果当前结果集为false,输出错误信息
        if(false === $this->queryResult){
            E($this->error());
            return false;
        }else{// 返回全部结果
            $this->numRows = $this->queryResult->num_rows;    // 暂存记录数量
            $this->numCols = $this->queryResult->field_count; // 暂存字段数量
            return $this->getAll();
        }
    }
    /**
     * 执行一条sql语句
     * @param  string $sql    sql语句字符串
     * @return int            返回受影响的行数
     * @see                   \Frame\Db::execute()
     */
    public function execute($sql){
        $this->_linkID = $this->Connect();
        if(!$this->_linkID) return false;
        $this->queryStr = $sql;
        if($this->queryResult) $this->free();
        $result = $this->_linkID->query($sql);
        if(false === $result){
            $this->error();
            return false;
        }else{
            $this->affectedRows = $this->_linkID->affected_rows;
            $this->lastInsertID = $this->_linkID->insert_id;//记录id,每次插入后都会更新
            return $this->affectedRows;
        }
    }
    public function insertAll($dataList,$options,$replace){
        
    }
    /**
     * 提交事务
     * @access public
     * @return boolean 提交成功返回true,失败返回false
     */
    public function commit(){
        //有事务时进行提交
        if($this->transTimes > 0){
            $result = $this->_linkID->commit();
            $this->_linkID->autocommit(true);
            $this->transTimes = 0;//提交后清空标识
            if(!$result){
                $this->error();
                return false;
            }
        }
        return true;
    }
    /**
     * 事务回滚
     * @access public
     * @return boolean
     * @see \Frame\Db::rollback()
     */
    public function rollback(){
        if($this->transTimes > 0){//有事务时进行回滚
            $result = $this->_linkID->rollback();
            $this->_linkID->autocommit(true);//开启自动提交
            $this->transTimes = 0;
            if(!result){
                $this->error();
                return false;
            }
        }
        return true;
    }
    /**
     * 启动事务
     * @access public
     * @return void
     */
    public function startTrans(){
        $this->_linkID = $this->connect();//连接数据库
        if($this->transTimes == 0){
            $this->_linkID->autocommit(false);//关闭自动提交
        }
        $this->transTimes++;
        return;
    }
    /**
     * 获取字段信息数组
     * @param string $tableName
     * @return array
     */
    public function getFields($tableName){
        $result = $this->query('SHOW COLUMNS FROM '.$this->parseKey($tableName));
        $info   = array();
        if($result){
            foreach ($result as $key => $val){
                $info[$val['Field']] = array(
                    'name'    => $val['Field'],
                    'type'    => $val['Type'],
                    'notnull' => (bool)($val['Null'] === ''),
                    'primary' => (strtolower($val['Key']) == 'pri'),
                    'autoinc' => (strtolower($val['Extra']) == 'auto_increment')
                );
            }
        }
        return $info;
    }

    /**
     * 解析SQL中的关键字
     * @param $key
     * @return string
     */
    protected function parseKey(&$key){
        $key = trim($key);// 过滤空白字符

        if(!preg_match('/[,\'\"\*\(\)`.\s]/',$key)){
            $key = '`'.$key.'`'; // MYSQL数据库中的字段需要使用`[字段名]`括起来
        }
        return $key;
    }

}






阅读更多
换一批

没有更多推荐了,返回首页