├── Core ├── Exception.class.php ├── Storage.class.php ├── Controller.class.php ├── Log │ └── Driver │ │ └── File.class.php ├── Cache │ └── Driver │ │ ├── Memcached.class.php │ │ ├── Memcache.class.php │ │ ├── Redis.class.php │ │ └── File.class.php ├── Route.class.php ├── Storage │ └── Driver │ │ └── File.class.php ├── Cache.class.php ├── Log.class.php ├── App.class.php ├── Session │ └── Driver │ │ └── Db.class.php ├── Dispatcher.class.php ├── Db │ └── Driver │ │ ├── Sqlite.class.php │ │ ├── Pdo.class.php │ │ └── Mongo.class.php ├── Core.class.php ├── Model │ └── MongoModel.class.php ├── Crypt │ └── Driver │ │ └── Des.class.php └── Db.class.php ├── Mode └── common.php ├── Conf ├── debug.php └── convention.php ├── README.md └── index.php /Core/Exception.class.php: -------------------------------------------------------------------------------- 1 | array( 9 | CORE_PATH.'Conf/convention.php', // 系统惯例配置 10 | CONF_PATH.'config'.CONF_EXT, // 应用公共配置 11 | ), 12 | 13 | // 函数和类文件 14 | 'core' => array( 15 | CORE_PATH.'Common/functions.php', 16 | COMMON_PATH.'Common/function.php', 17 | LIB_PATH . 'App'.EXT, 18 | LIB_PATH . 'Dispatcher'.EXT, 19 | LIB_PATH . 'Log'.EXT, 20 | LIB_PATH . 'Route'.EXT, 21 | LIB_PATH . 'Controller'.EXT, 22 | ), 23 | ); 24 | -------------------------------------------------------------------------------- /Conf/debug.php: -------------------------------------------------------------------------------- 1 | true, // 进行日志记录 8 | 'LOG_EXCEPTION_RECORD' => true, // 是否记录异常信息日志 9 | 'LOG_LEVEL' => 'EMERG,ALERT,CRIT,ERR,WARN,NOTIC,INFO,DEBUG,SQL', // 允许记录的日志级别 10 | 'DB_FIELDS_CACHE' => false, // 字段缓存信息 11 | 'DB_SQL_LOG' => true, // 记录SQL信息 12 | 'TMPL_CACHE_ON' => false, // 是否开启模板编译缓存,设为false则每次都会重新编译 13 | 'TMPL_STRIP_SPACE' => false, // 是否去除模板文件里面的html空格与换行 14 | 'SHOW_ERROR_MSG' => true, // 显示错误信息 15 | 'URL_CASE_INSENSITIVE' => false, // URL区分大小写 16 | ); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 精简了框架内容,删除了很多日常api项目用不到的东西 3 | 4 | 路由配置 5 | 6 | #### map 模式 7 | 8 | Home\Route\route.php 定义: 9 | 10 | return array( 11 | array( 12 | 13 | 'prefix' => '', //前缀 14 | 'suffix' => '', //后缀 15 | 'namespace' => 'n1\n2\n3',//目标类namespace 16 | 'maps' => array( //路由对应 17 | 'x1/x2/test' => 'class@method' 18 | ) 19 | ) 20 | ); 21 | 22 | 23 | 访问 http://xxxx.com/x1/x2/test ,将执行 24 | n1\n2\n3\class -> method() 25 | 26 | 27 | 28 | #### auto 模式 29 | 30 | Controller(任意层级) 31 | 32 | Home/Controller/x1/x2Controller.class.php 33 | 34 | 访问 http://xxxx.com/x1/x2/test ,将执行 35 | 36 | Home\Controller\x1\x2 -> test() 37 | 38 | ##### 优先判断是否存在map,若不存在则执行auto模式,使用$_SERVER['PATH_INFO']来查找相应的类 39 | -------------------------------------------------------------------------------- /Core/Storage.class.php: -------------------------------------------------------------------------------- 1 | _initialize(); 30 | } 31 | 32 | 33 | public function __get($name) { 34 | return $this->get($name); 35 | } 36 | 37 | /** 38 | * 魔术方法 有不存在的操作的时候执行 39 | * @access public 40 | * @param string $method 方法名 41 | * @param array $args 参数 42 | * @return mixed 43 | */ 44 | public function __call($method,$args) { 45 | if( 0 === strcasecmp($method,ACTION_NAME.C('ACTION_SUFFIX'))) { 46 | if(method_exists($this,'_empty')) { 47 | // 如果定义了_empty操作 则调用 48 | $this->_empty($method,$args); 49 | }else{ 50 | E(L('_ERROR_ACTION_').':'.ACTION_NAME); 51 | } 52 | }else{ 53 | E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_')); 54 | return; 55 | } 56 | } 57 | 58 | 59 | /** 60 | * 析构方法 61 | * @access public 62 | */ 63 | public function __destruct() { 64 | // 执行后续操作 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Core/Log/Driver/File.class.php: -------------------------------------------------------------------------------- 1 | ' c ', 8 | 'log_file_size' => 2097152, 9 | 'log_path' => '', 10 | ); 11 | 12 | // 实例化并传入参数 13 | public function __construct($config=array()){ 14 | $this->config = array_merge($this->config,$config); 15 | } 16 | 17 | /** 18 | * 日志写入接口 19 | * @access public 20 | * @param string $log 日志信息 21 | * @param string $destination 写入目标 22 | * @return void 23 | */ 24 | public function write($log,$destination='') { 25 | $now = date($this->config['log_time_format']); 26 | if(empty($destination)) 27 | $destination = $this->config['log_path'].date('y_m_d').'.log'; 28 | if(!is_dir($this->config['log_path'])) { 29 | mkdir($this->config['log_path'],0755,true); 30 | } 31 | //检测日志文件大小,超过配置大小则备份日志文件重新生成 32 | if(is_file($destination) && floor($this->config['log_file_size']) <= filesize($destination) ) 33 | rename($destination,dirname($destination).'/'.basename($destination,'.log').'-'.date('H_i_s').'.log'); 34 | //检查日志目录的最近一层是否存在 2014年12月1日13:33:12 add by madong 35 | chdir(APP_PATH); 36 | if(!is_dir(dirname($destination))){ 37 | mkdirs(dirname($destination)); 38 | } 39 | //G('LOG_START'); 40 | error_log("[{$now}] ".$_SERVER['REMOTE_ADDR'].' '.gethostname().' '.$_SERVER['REQUEST_URI']."\r\n{$log}\r\n", 3,$destination); 41 | //G('LOG_END'); 42 | //$log = '【日志写入时间】'.G('LOG_START','LOG_END'); 43 | //error_log("[{$now}] ".$_SERVER['REMOTE_ADDR'].' '.gethostname().' '.$_SERVER['REQUEST_URI']."\r\n{$log}\r\n", 3,$destination); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Core/Cache/Driver/Memcached.class.php: -------------------------------------------------------------------------------- 1 | C('MEMCACHED_SERVER') ? C('MEMCACHED_SERVER') : null, 26 | 'lib_options' => C('MEMCACHED_LIB') ? C('MEMCACHED_LIB') : null 27 | ), $options); 28 | 29 | $this->options = $options; 30 | $this->options['expire'] = isset($options['expire'])? $options['expire'] : C('DATA_CACHE_TIME'); 31 | $this->options['prefix'] = isset($options['prefix'])? $options['prefix'] : C('DATA_CACHE_PREFIX'); 32 | $this->options['length'] = isset($options['length'])? $options['length'] : 0; 33 | 34 | $this->handler = new MemcachedResource; 35 | $options['servers'] && $this->handler->addServers($options['servers']); 36 | $options['lib_options'] && $this->handler->setOptions($options['lib_options']); 37 | } 38 | 39 | /** 40 | * 读取缓存 41 | * @access public 42 | * @param string $name 缓存变量名 43 | * @return mixed 44 | */ 45 | public function get($name) 46 | { 47 | N('cache_read',1); 48 | $this->handler->get($this->options['prefix'].$name); 49 | return $this->handler->get($this->options['prefix'].$name); 50 | } 51 | 52 | /** 53 | * 写入缓存 54 | * @access public 55 | * @param string $name 缓存变量名 56 | * @param mixed $value 存储数据 57 | * @param integer $expire 有效时间(秒) 58 | * @return boolean 59 | */ 60 | public function set($name, $value, $expire = null) 61 | { 62 | N('cache_write',1); 63 | if(is_null($expire)) { 64 | $expire = $this->options['expire']; 65 | } 66 | $name = $this->options['prefix'].$name; 67 | if($this->handler->set($name, $value, time() + $expire)) { 68 | if($this->options['length']>0) { 69 | // 记录缓存队列 70 | $this->queue($name); 71 | } 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | /** 78 | * 删除缓存 79 | * @access public 80 | * @param string $name 缓存变量名 81 | * @return boolean 82 | */ 83 | public function rm($name, $ttl = false) { 84 | $name = $this->options['prefix'].$name; 85 | return $ttl === false ? 86 | $this->handler->delete($name) : 87 | $this->handler->delete($name, $ttl); 88 | } 89 | 90 | /** 91 | * 清除缓存 92 | * @access public 93 | * @return boolean 94 | */ 95 | public function clear() { 96 | return $this->handler->flush(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | '', //前缀 12 | 'suffix' => '', //后缀 13 | 'namespace' => 'n1\n2\n3',//目标类namespace 14 | 'maps' => array( //路由对应 15 | 'x1/x2/test' => 'Controller@method' 16 | ) 17 | ) 18 | ); 19 | */ 20 | class Route { 21 | /** 22 | * @var array 23 | * 24 | */ 25 | static private $route_maps = array(); 26 | 27 | static $route_type = 1;//1,map类型;2,自动路由 28 | 29 | static function parseRoute(){ 30 | 31 | if (!self::parseMap()){ 32 | self::parseAuto(); 33 | self::$route_type = 2; 34 | } 35 | } 36 | 37 | static function importRoute($route_file){ 38 | $route_groups = include $route_file; 39 | foreach ($route_groups as $route_group_item){ 40 | $prefix = trim($route_group_item['prefix']); 41 | $suffix = trim($route_group_item['suffix']); 42 | $namespace = trim($route_group_item['namespace'],'\\'); 43 | foreach ($route_group_item['maps'] as $_route =>$_target){ 44 | $_route = $prefix.$_route.$suffix; 45 | $_targets = explode('@', $_target); 46 | $controller = $_targets[0]?$_targets[0]:''; 47 | $action = $_targets[1]?$_targets[1]:''; 48 | 49 | if ($controller){ 50 | $controller = $namespace.'\\'.$controller; 51 | } 52 | self::$route_maps[$_route] = array( 53 | 'controller' => $controller, 54 | 'action' => $action 55 | ); 56 | } 57 | } 58 | } 59 | 60 | //TODO 解析预定义路由 61 | private static function parseMap(){ 62 | $path_info = $_SERVER['PATH_INFO']; 63 | if (self::$route_maps[$path_info]){ 64 | $route_info = self::$route_maps[$path_info]; 65 | define('CONTROLLER_NAME', $route_info['controller']); 66 | define('ACTION_NAME', $route_info['action']); 67 | return true; 68 | } 69 | return false; 70 | } 71 | 72 | /** 73 | * 自动路由 74 | * 规则 NameSpace/Controller/Action 75 | */ 76 | private static function parseAuto(){ 77 | $depr = C('URL_PATHINFO_DEPR'); 78 | $pat_info = $_SERVER['PATH_INFO']; 79 | $controller = ''; 80 | $action = ''; 81 | $paths = explode($depr, $pat_info); 82 | if (count($paths) > 1){ 83 | $action = array_pop($paths); 84 | $controller = implode('\\', $paths); 85 | } 86 | 87 | if ($controller){ 88 | $controller = MODULE_NAME.'\\'.C('DEFAULT_C_LAYER').'\\'.$controller; 89 | } 90 | 91 | define('CONTROLLER_NAME', $controller); 92 | define('ACTION_NAME', $action); 93 | 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /Core/Cache/Driver/Memcache.class.php: -------------------------------------------------------------------------------- 1 | C('MEMCACHE_HOST') ? C('MEMCACHE_HOST') : '127.0.0.1', 21 | 'port' => C('MEMCACHE_PORT') ? C('MEMCACHE_PORT') : 11211, 22 | 'timeout' => C('DATA_CACHE_TIMEOUT') ? C('DATA_CACHE_TIMEOUT') : false, 23 | 'persistent' => false, 24 | ),$options); 25 | 26 | $this->options = $options; 27 | $this->options['expire'] = isset($options['expire'])? $options['expire'] : C('DATA_CACHE_TIME'); 28 | $this->options['prefix'] = isset($options['prefix'])? $options['prefix'] : C('DATA_CACHE_PREFIX'); 29 | $this->options['length'] = isset($options['length'])? $options['length'] : 0; 30 | $func = $options['persistent'] ? 'pconnect' : 'connect'; 31 | $this->handler = new \Memcache; 32 | $options['timeout'] === false ? 33 | $this->handler->$func($options['host'], $options['port']) : 34 | $this->handler->$func($options['host'], $options['port'], $options['timeout']); 35 | } 36 | 37 | /** 38 | * 读取缓存 39 | * @access public 40 | * @param string $name 缓存变量名 41 | * @return mixed 42 | */ 43 | public function get($name) { 44 | N('cache_read',1); 45 | return $this->handler->get($this->options['prefix'].$name); 46 | } 47 | 48 | /** 49 | * 写入缓存 50 | * @access public 51 | * @param string $name 缓存变量名 52 | * @param mixed $value 存储数据 53 | * @param integer $expire 有效时间(秒) 54 | * @return boolean 55 | */ 56 | public function set($name, $value, $expire = null) { 57 | N('cache_write',1); 58 | if(is_null($expire)) { 59 | $expire = $this->options['expire']; 60 | } 61 | $name = $this->options['prefix'].$name; 62 | if($this->handler->set($name, $value, 0, $expire)) { 63 | if($this->options['length']>0) { 64 | // 记录缓存队列 65 | $this->queue($name); 66 | } 67 | return true; 68 | } 69 | return false; 70 | } 71 | 72 | /** 73 | * 删除缓存 74 | * @access public 75 | * @param string $name 缓存变量名 76 | * @return boolean 77 | */ 78 | public function rm($name, $ttl = false) { 79 | $name = $this->options['prefix'].$name; 80 | return $ttl === false ? 81 | $this->handler->delete($name) : 82 | $this->handler->delete($name, $ttl); 83 | } 84 | 85 | /** 86 | * 清除缓存 87 | * @access public 88 | * @return boolean 89 | */ 90 | public function clear() { 91 | return $this->handler->flush(); 92 | } 93 | } -------------------------------------------------------------------------------- /Core/Storage/Driver/File.class.php: -------------------------------------------------------------------------------- 1 | get($filename,'content',$type); 24 | } 25 | 26 | /** 27 | * 文件写入 28 | * @access public 29 | * @param string $filename 文件名 30 | * @param string $content 文件内容 31 | * @return boolean 32 | */ 33 | public function put($filename,$content,$type=''){ 34 | $dir = dirname($filename); 35 | if(!is_dir($dir)) 36 | mkdir($dir,0755,true); 37 | if(false === file_put_contents($filename,$content)){ 38 | E(L('_STORAGE_WRITE_ERROR_').':'.$filename); 39 | }else{ 40 | $this->contents[$filename]=$content; 41 | return true; 42 | } 43 | } 44 | 45 | /** 46 | * 文件追加写入 47 | * @access public 48 | * @param string $filename 文件名 49 | * @param string $content 追加的文件内容 50 | * @return boolean 51 | */ 52 | public function append($filename,$content,$type=''){ 53 | if(is_file($filename)){ 54 | $content = $this->read($filename,$type).$content; 55 | } 56 | return $this->put($filename,$content,$type); 57 | } 58 | 59 | /** 60 | * 加载文件 61 | * @access public 62 | * @param string $filename 文件名 63 | * @param array $vars 传入变量 64 | * @return void 65 | */ 66 | public function load($_filename,$vars=null){ 67 | if(!is_null($vars)) 68 | extract($vars, EXTR_OVERWRITE); 69 | include $_filename; 70 | } 71 | 72 | /** 73 | * 文件是否存在 74 | * @access public 75 | * @param string $filename 文件名 76 | * @return boolean 77 | */ 78 | public function has($filename,$type=''){ 79 | return is_file($filename); 80 | } 81 | 82 | /** 83 | * 文件删除 84 | * @access public 85 | * @param string $filename 文件名 86 | * @return boolean 87 | */ 88 | public function unlink($filename,$type=''){ 89 | unset($this->contents[$filename]); 90 | return is_file($filename) ? unlink($filename) : false; 91 | } 92 | 93 | /** 94 | * 读取文件信息 95 | * @access public 96 | * @param string $filename 文件名 97 | * @param string $name 信息名 mtime或者content 98 | * @return boolean 99 | */ 100 | public function get($filename,$name,$type=''){ 101 | if(!isset($this->contents[$filename])){ 102 | if(!is_file($filename)) return false; 103 | $this->contents[$filename]=file_get_contents($filename); 104 | } 105 | $content=$this->contents[$filename]; 106 | $info = array( 107 | 'mtime' => filemtime($filename), 108 | 'content' => $content 109 | ); 110 | return $info[$name]; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Core/Cache.class.php: -------------------------------------------------------------------------------- 1 | connect($type,$options); 51 | } 52 | return $_instance[$guid]; 53 | } 54 | 55 | public function __get($name) { 56 | return $this->get($name); 57 | } 58 | 59 | public function __set($name,$value) { 60 | return $this->set($name,$value); 61 | } 62 | 63 | public function __unset($name) { 64 | $this->rm($name); 65 | } 66 | public function setOptions($name,$value) { 67 | $this->options[$name] = $value; 68 | } 69 | 70 | public function getOptions($name) { 71 | return $this->options[$name]; 72 | } 73 | 74 | /** 75 | * 队列缓存 76 | * @access protected 77 | * @param string $key 队列名 78 | * @return mixed 79 | */ 80 | // 81 | protected function queue($key) { 82 | static $_handler = array( 83 | 'file' => array('F','F'), 84 | 'xcache'=> array('xcache_get','xcache_set'), 85 | 'apc' => array('apc_fetch','apc_store'), 86 | ); 87 | $queue = isset($this->options['queue'])?$this->options['queue']:'file'; 88 | $fun = isset($_handler[$queue])?$_handler[$queue]:$_handler['file']; 89 | $queue_name = isset($this->options['queue_name'])?$this->options['queue_name']:'think_queue'; 90 | $value = $fun[0]($queue_name); 91 | if(!$value) { 92 | $value = array(); 93 | } 94 | // 进列 95 | if(false===array_search($key, $value)) array_push($value,$key); 96 | if(count($value) > $this->options['length']) { 97 | // 出列 98 | $key = array_shift($value); 99 | // 删除缓存 100 | $this->rm($key); 101 | if(APP_DEBUG){ 102 | //调试模式下,记录出列次数 103 | N($queue_name.'_out_times',1,true); 104 | } 105 | } 106 | return $fun[1]($queue_name,$value); 107 | } 108 | 109 | public function __call($method,$args){ 110 | //调用缓存类型自己的方法 111 | if(method_exists($this->handler, $method)){ 112 | return call_user_func_array(array($this->handler,$method), $args); 113 | }else{ 114 | E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_')); 115 | return; 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /Core/Log.class.php: -------------------------------------------------------------------------------- 1 | 200) {// 未防止溢出,每200条log写入一次文件 madong 47 | self::$log[] = "--------------每200条写入一次文件,防止内存溢出--------------\r\n"; 48 | $uuid = uniqid('log-'); 49 | self::$log[] = "↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓下接相同的uuid:".$uuid."↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\r\n"; 50 | self::save(); 51 | self::$log = array(); 52 | self::$log[] = "↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑上接相同的uuid:".$uuid."↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\r\n"; 53 | } 54 | } 55 | } 56 | 57 | /** 58 | * 日志保存 59 | * @static 60 | * @access public 61 | * @param integer $type 日志记录方式 62 | * @param string $destination 写入目标 63 | * @param bool $clear_log 写入后是否清除旧数据 64 | * @return void 65 | */ 66 | static function save($type='',$destination='',$clear_log=true) { 67 | if(empty(self::$log)) return ; 68 | if(empty($destination)) 69 | $destination = C('LOG_PATH').date('y_m_d').'.log'; 70 | if(!self::$storage){ 71 | $type = $type?:C('LOG_TYPE'); 72 | $class = 'Core\\Log\\Driver\\'. ucwords($type); 73 | self::$storage = new $class(); 74 | } 75 | $message = implode('',self::$log); 76 | self::$storage->write($message,$destination); 77 | // 保存后清空日志缓存 78 | self::$log = array(); 79 | if (PHP_SAPI === 'cli')//如果cli模式下运行的,则有可能是root用户,此时进行一次文件的 用户名、用户组修改,改为 www www 80 | { 81 | chown($destination, 'www'); 82 | chgrp($destination, 'www'); 83 | } 84 | } 85 | 86 | /** 87 | * 日志直接写入 88 | * @static 89 | * @access public 90 | * @param string $message 日志信息 91 | * @param string $level 日志级别 92 | * @param integer $type 日志记录方式 93 | * @param string $destination 写入目标 94 | * @return void 95 | */ 96 | static function write($message,$level=self::ERR,$type='',$destination='') { 97 | if(!self::$storage){ 98 | $type = $type?:C('LOG_TYPE'); 99 | $class = 'Core\\Log\\Driver\\'. ucwords($type); 100 | self::$storage = new $class(); 101 | } 102 | if(empty($destination)) 103 | $destination = C('LOG_PATH').date('y_m_d').'.log'; 104 | self::$storage->write("{$level}: {$message}", $destination); 105 | } 106 | } -------------------------------------------------------------------------------- /Core/App.class.php: -------------------------------------------------------------------------------- 1 | isPublic() && !$method->isStatic())||Route::$route_type==1) { 68 | $class = new \ReflectionClass($module); 69 | // 前置操作 70 | if($class->hasMethod('_before_'.$action)) { 71 | $before = $class->getMethod('_before_'.$action); 72 | if($before->isPublic()) { 73 | $before->invoke($module); 74 | } 75 | } 76 | 77 | //执行 78 | 79 | $method->invoke($module); 80 | 81 | // 后置操作 82 | if($class->hasMethod('_after_'.$action)) { 83 | $after = $class->getMethod('_after_'.$action); 84 | if($after->isPublic()) { 85 | $after->invoke($module); 86 | } 87 | } 88 | }else{ 89 | // 操作方法不是Public 抛出异常 90 | throw new \ReflectionException(); 91 | } 92 | } catch (\ReflectionException $e) { 93 | // 方法调用发生异常后 引导到__call方法处理 94 | $method = new \ReflectionMethod($module,'__call'); 95 | $method->invokeArgs($module,array($action,'')); 96 | } 97 | return ; 98 | } 99 | 100 | /** 101 | * 运行应用实例 入口文件使用的快捷方法 102 | * @access public 103 | * @return void 104 | */ 105 | static public function run() { 106 | G('T_START'); 107 | // 应用初始化 108 | App::init(); 109 | G('T_END2'); 110 | Log::record("【app_init运行时间 】".G('T_START','T_END2')); 111 | // 应用开始 112 | // Session初始化 113 | if(!IS_CLI){ 114 | session(C('SESSION_OPTIONS')); 115 | } 116 | G('T_END2-2'); 117 | Log::record("【应用运行时间 T_END2】".G('T_START','T_END2-2')); 118 | // 记录应用初始化时间 119 | G('initTime'); 120 | App::exec(); 121 | // 应用结束 122 | G('T_END3'); 123 | Log::record("【应用运行时间 T_END3】".G('T_START','T_END3')); 124 | G('T_END1'); 125 | Log::record("【应用运行时间T_END1】".G('T_START','T_END1')); 126 | return ; 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /Core/Cache/Driver/Redis.class.php: -------------------------------------------------------------------------------- 1 | C('REDIS_HOST') ? C('REDIS_HOST') : '127.0.0.1', 23 | 'port' => C('REDIS_PORT') ? C('REDIS_PORT') : 6379, 24 | 'timeout' => C('DATA_CACHE_TIMEOUT') ? C('DATA_CACHE_TIMEOUT') : 1, 25 | 'auth' => C('REDIS_AUTH') ? C('REDIS_AUTH'):'', 26 | 'db_idx' => C('REDIS_DB_IDX') ? C('REDIS_DB_IDX'):0, 27 | 'persistent' => false, 28 | ); 29 | } 30 | $this->options = $options; 31 | $this->options['expire'] = isset($options['expire'])? $options['expire'] : C('DATA_CACHE_TIME'); 32 | $this->options['prefix'] = isset($options['prefix'])? $options['prefix'] : C('DATA_CACHE_PREFIX'); 33 | $this->options['length'] = isset($options['length'])? $options['length'] : 0; 34 | $func = $options['persistent'] ? 'pconnect' : 'connect'; 35 | try { 36 | 37 | $this->handler = new \Redis; 38 | $options['timeout'] === false ? 39 | $this->handler->$func($options['host'], $options['port']) : 40 | $this->handler->$func($options['host'], $options['port'], $options['timeout']); 41 | if($options['auth']){ 42 | $this->handler->auth($options['auth']); 43 | } 44 | $this->handler->select($options['db_idx']); 45 | Log::record('【redis 连接成功】'.json_encode($options)); 46 | } catch (\RedisException $e) { 47 | Log::record('【redis 连接异常】'.$e->getMessage()); 48 | } 49 | 50 | } 51 | 52 | /** 53 | * 读取缓存 54 | * @access public 55 | * @param string $name 缓存变量名 56 | * @return mixed 57 | */ 58 | public function get($name) { 59 | N('cache_read',1); 60 | $value = $this->handler->get($this->options['prefix'].$name); 61 | $jsonData = json_decode( $value, true ); 62 | return ($jsonData === NULL) ? $value : $jsonData; //检测是否为JSON数据 true 返回JSON解析数组, false返回源数据 63 | } 64 | 65 | /** 66 | * 写入缓存 67 | * @access public 68 | * @param string $name 缓存变量名 69 | * @param mixed $value 存储数据 70 | * @param integer $expire 有效时间(秒) 71 | * @return boolean 72 | */ 73 | public function set($name, $value, $expire = null) { 74 | N('cache_write',1); 75 | if(is_null($expire)) { 76 | $expire = $this->options['expire']; 77 | } 78 | $name = $this->options['prefix'].$name; 79 | //对数组/对象数据进行缓存处理,保证数据完整性 80 | $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value; 81 | if(is_numeric($expire) && $expire > 0) { 82 | $result = $this->handler->setex($name, $expire, $value); 83 | }else{ 84 | $result = $this->handler->set($name, $value); 85 | } 86 | if($result && $this->options['length']>0) { 87 | // 记录缓存队列 88 | $this->queue($name); 89 | } 90 | return $result; 91 | } 92 | 93 | /** 94 | * 删除缓存 95 | * @access public 96 | * @param string $name 缓存变量名 97 | * @return boolean 98 | */ 99 | public function rm($name) { 100 | return $this->handler->delete($this->options['prefix'].$name); 101 | } 102 | 103 | /** 104 | * 清除缓存 105 | * @access public 106 | * @return boolean 107 | */ 108 | public function clear() { 109 | return $this->handler->flushDB(); 110 | } 111 | 112 | /** 113 | * 表头插入 114 | * @param unknown $list_name 115 | * @param unknown $list_item 116 | */ 117 | public function lpush($list_name,$list_item){ 118 | $ret = $this->handler->lPush($list_name,$list_item); 119 | return $ret > 0; 120 | } 121 | 122 | /** 123 | * 弹出队列第一个元素 124 | * @param unknown $list_name 125 | * @return unknown 126 | */ 127 | public function blpop($list_name){ 128 | return $this->handler->blPop($list_name); 129 | } 130 | 131 | 132 | } 133 | -------------------------------------------------------------------------------- /Core/Cache/Driver/File.class.php: -------------------------------------------------------------------------------- 1 | options = $options; 16 | } 17 | $this->options['temp'] = !empty($options['temp'])? $options['temp'] : C('DATA_CACHE_PATH'); 18 | $this->options['prefix'] = isset($options['prefix'])? $options['prefix'] : C('DATA_CACHE_PREFIX'); 19 | $this->options['expire'] = isset($options['expire'])? $options['expire'] : C('DATA_CACHE_TIME'); 20 | $this->options['length'] = isset($options['length'])? $options['length'] : 0; 21 | if(substr($this->options['temp'], -1) != '/') $this->options['temp'] .= '/'; 22 | $this->init(); 23 | } 24 | 25 | /** 26 | * 初始化检查 27 | * @access private 28 | * @return boolean 29 | */ 30 | private function init() { 31 | // 创建应用缓存目录 32 | if (!is_dir($this->options['temp'])) { 33 | mkdir($this->options['temp']); 34 | } 35 | } 36 | 37 | /** 38 | * 取得变量的存储文件名 39 | * @access private 40 | * @param string $name 缓存变量名 41 | * @return string 42 | */ 43 | private function filename($name) { 44 | $name = md5($name); 45 | if(C('DATA_CACHE_SUBDIR')) { 46 | // 使用子目录 47 | $dir =''; 48 | for($i=0;$ioptions['temp'].$dir)) { 52 | mkdir($this->options['temp'].$dir,0755,true); 53 | } 54 | $filename = $dir.$this->options['prefix'].$name.'.php'; 55 | }else{ 56 | $filename = $this->options['prefix'].$name.'.php'; 57 | } 58 | return $this->options['temp'].$filename; 59 | } 60 | 61 | /** 62 | * 读取缓存 63 | * @access public 64 | * @param string $name 缓存变量名 65 | * @return mixed 66 | */ 67 | public function get($name) { 68 | $filename = $this->filename($name); 69 | if (!is_file($filename)) { 70 | return false; 71 | } 72 | N('cache_read',1); 73 | $content = file_get_contents($filename); 74 | if( false !== $content) { 75 | $expire = (int)substr($content,8, 12); 76 | if($expire != 0 && time() > filemtime($filename) + $expire) { 77 | //缓存过期删除缓存文件 78 | unlink($filename); 79 | return false; 80 | } 81 | if(C('DATA_CACHE_CHECK')) {//开启数据校验 82 | $check = substr($content,20, 32); 83 | $content = substr($content,52, -3); 84 | if($check != md5($content)) {//校验错误 85 | return false; 86 | } 87 | }else { 88 | $content = substr($content,20, -3); 89 | } 90 | if(C('DATA_CACHE_COMPRESS') && function_exists('gzcompress')) { 91 | //启用数据压缩 92 | $content = gzuncompress($content); 93 | } 94 | $content = unserialize($content); 95 | return $content; 96 | } 97 | else { 98 | return false; 99 | } 100 | } 101 | 102 | /** 103 | * 写入缓存 104 | * @access public 105 | * @param string $name 缓存变量名 106 | * @param mixed $value 存储数据 107 | * @param int $expire 有效时间 0为永久 108 | * @return boolean 109 | */ 110 | public function set($name,$value,$expire=null) { 111 | N('cache_write',1); 112 | if(is_null($expire)) { 113 | $expire = $this->options['expire']; 114 | } 115 | $filename = $this->filename($name); 116 | $data = serialize($value); 117 | if( C('DATA_CACHE_COMPRESS') && function_exists('gzcompress')) { 118 | //数据压缩 119 | $data = gzcompress($data,3); 120 | } 121 | if(C('DATA_CACHE_CHECK')) {//开启数据校验 122 | $check = md5($data); 123 | }else { 124 | $check = ''; 125 | } 126 | $data = ""; 127 | $result = file_put_contents($filename,$data); 128 | if($result) { 129 | if($this->options['length']>0) { 130 | // 记录缓存队列 131 | $this->queue($name); 132 | } 133 | clearstatcache(); 134 | return true; 135 | }else { 136 | return false; 137 | } 138 | } 139 | 140 | /** 141 | * 删除缓存 142 | * @access public 143 | * @param string $name 缓存变量名 144 | * @return boolean 145 | */ 146 | public function rm($name) { 147 | return unlink($this->filename($name)); 148 | } 149 | 150 | /** 151 | * 清除缓存 152 | * @access public 153 | * @param string $name 缓存变量名 154 | * @return boolean 155 | */ 156 | public function clear() { 157 | $path = $this->options['temp']; 158 | $files = scandir($path); 159 | if($files){ 160 | foreach($files as $file){ 161 | if ($file != '.' && $file != '..' && is_dir($path.$file) ){ 162 | array_map( 'unlink', glob( $path.$file.'/*.*' ) ); 163 | }elseif(is_file($path.$file)){ 164 | unlink( $path . $file ); 165 | } 166 | } 167 | return true; 168 | } 169 | return false; 170 | } 171 | } -------------------------------------------------------------------------------- /Core/Session/Driver/Db.class.php: -------------------------------------------------------------------------------- 1 | lifeTime = C('SESSION_EXPIRE')?C('SESSION_EXPIRE'):ini_get('session.gc_maxlifetime'); 37 | $this->sessionTable = C('SESSION_TABLE')?C('SESSION_TABLE'):C("DB_PREFIX")."session"; 38 | //分布式数据库 39 | $host = explode(',',C('DB_HOST')); 40 | $port = explode(',',C('DB_PORT')); 41 | $name = explode(',',C('DB_NAME')); 42 | $user = explode(',',C('DB_USER')); 43 | $pwd = explode(',',C('DB_PWD')); 44 | if(1 == C('DB_DEPLOY_TYPE')){ 45 | //读写分离 46 | if(C('DB_RW_SEPARATE')){ 47 | $w = floor(mt_rand(0,C('DB_MASTER_NUM')-1)); 48 | if(is_numeric(C('DB_SLAVE_NO'))){//指定服务器读 49 | $r = C('DB_SLAVE_NO'); 50 | }else{ 51 | $r = floor(mt_rand(C('DB_MASTER_NUM'),count($host)-1)); 52 | } 53 | //主数据库链接 54 | $hander = mysql_connect( 55 | $host[$w].(isset($port[$w])?':'.$port[$w]:':'.$port[0]), 56 | isset($user[$w])?$user[$w]:$user[0], 57 | isset($pwd[$w])?$pwd[$w]:$pwd[0] 58 | ); 59 | $dbSel = mysql_select_db( 60 | isset($name[$w])?$name[$w]:$name[0] 61 | ,$hander); 62 | if(!$hander || !$dbSel) 63 | return false; 64 | $this->hander[0] = $hander; 65 | //从数据库链接 66 | $hander = mysql_connect( 67 | $host[$r].(isset($port[$r])?':'.$port[$r]:':'.$port[0]), 68 | isset($user[$r])?$user[$r]:$user[0], 69 | isset($pwd[$r])?$pwd[$r]:$pwd[0] 70 | ); 71 | $dbSel = mysql_select_db( 72 | isset($name[$r])?$name[$r]:$name[0] 73 | ,$hander); 74 | if(!$hander || !$dbSel) 75 | return false; 76 | $this->hander[1] = $hander; 77 | return true; 78 | } 79 | } 80 | //从数据库链接 81 | $r = floor(mt_rand(0,count($host)-1)); 82 | $hander = mysql_connect( 83 | $host[$r].(isset($port[$r])?':'.$port[$r]:':'.$port[0]), 84 | isset($user[$r])?$user[$r]:$user[0], 85 | isset($pwd[$r])?$pwd[$r]:$pwd[0] 86 | ); 87 | $dbSel = mysql_select_db( 88 | isset($name[$r])?$name[$r]:$name[0] 89 | ,$hander); 90 | if(!$hander || !$dbSel) 91 | return false; 92 | $this->hander = $hander; 93 | return true; 94 | } 95 | 96 | /** 97 | * 关闭Session 98 | * @access public 99 | */ 100 | public function close() { 101 | if(is_array($this->hander)){ 102 | $this->gc($this->lifeTime); 103 | return (mysql_close($this->hander[0]) && mysql_close($this->hander[1])); 104 | } 105 | $this->gc($this->lifeTime); 106 | return mysql_close($this->hander); 107 | } 108 | 109 | /** 110 | * 读取Session 111 | * @access public 112 | * @param string $sessID 113 | */ 114 | public function read($sessID) { 115 | $hander = is_array($this->hander)?$this->hander[1]:$this->hander; 116 | $res = mysql_query("SELECT session_data AS data FROM ".$this->sessionTable." WHERE session_id = '$sessID' AND session_expire >".time(),$hander); 117 | if($res) { 118 | $row = mysql_fetch_assoc($res); 119 | return $row['data']; 120 | } 121 | return ""; 122 | } 123 | 124 | /** 125 | * 写入Session 126 | * @access public 127 | * @param string $sessID 128 | * @param String $sessData 129 | */ 130 | public function write($sessID,$sessData) { 131 | $hander = is_array($this->hander)?$this->hander[0]:$this->hander; 132 | $expire = time() + $this->lifeTime; 133 | mysql_query("REPLACE INTO ".$this->sessionTable." ( session_id, session_expire, session_data) VALUES( '$sessID', '$expire', '$sessData')",$hander); 134 | if(mysql_affected_rows($hander)) 135 | return true; 136 | return false; 137 | } 138 | 139 | /** 140 | * 删除Session 141 | * @access public 142 | * @param string $sessID 143 | */ 144 | public function destroy($sessID) { 145 | $hander = is_array($this->hander)?$this->hander[0]:$this->hander; 146 | mysql_query("DELETE FROM ".$this->sessionTable." WHERE session_id = '$sessID'",$hander); 147 | if(mysql_affected_rows($hander)) 148 | return true; 149 | return false; 150 | } 151 | 152 | /** 153 | * Session 垃圾回收 154 | * @access public 155 | * @param string $sessMaxLifeTime 156 | */ 157 | public function gc($sessMaxLifeTime) { 158 | $hander = is_array($this->hander)?$this->hander[0]:$this->hander; 159 | mysql_query("DELETE FROM ".$this->sessionTable." WHERE session_expire < ".time(),$hander); 160 | return mysql_affected_rows($hander); 161 | } 162 | 163 | } -------------------------------------------------------------------------------- /Conf/convention.php: -------------------------------------------------------------------------------- 1 | true, // 应用类库是否使用命名空间 12 | 'APP_SUB_DOMAIN_DEPLOY' => false, // 是否开启子域名部署 13 | 'APP_SUB_DOMAIN_RULES' => array(), // 子域名部署规则 14 | 'APP_DOMAIN_SUFFIX' => '', // 域名后缀 如果是com.cn net.cn 之类的后缀必须设置 15 | 'ACTION_SUFFIX' => '', // 操作方法后缀 16 | 'CONTROLLER_SUFFIX' => 'Controller', 17 | 'MULTI_MODULE' => true, // 是否允许多模块 如果为false 则必须设置 DEFAULT_MODULE 18 | 'MODULE_DENY_LIST' => array('Common','Runtime'), 19 | 'CONTROLLER_LEVEL' => 1, 20 | 'APP_AUTOLOAD_LAYER' => 'Controller,Model', // 自动加载的应用类库层 关闭APP_USE_NAMESPACE后有效 21 | 'APP_AUTOLOAD_PATH' => '', // 自动加载的路径 关闭APP_USE_NAMESPACE后有效 22 | 23 | /* Cookie设置 */ 24 | 'COOKIE_EXPIRE' => 0, // Cookie有效期 25 | 'COOKIE_DOMAIN' => '', // Cookie有效域名 26 | 'COOKIE_PATH' => '/', // Cookie路径 27 | 'COOKIE_PREFIX' => '', // Cookie前缀 避免冲突 28 | 'COOKIE_HTTPONLY' => '', // Cookie httponly设置 29 | 30 | /* 默认设定 */ 31 | 'DEFAULT_M_LAYER' => 'Model', // 默认的模型层目录名称 32 | 'DEFAULT_C_LAYER' => 'Controller', // 默认的控制器层目录名称 33 | 'DEFAULT_V_LAYER' => 'View', // 默认的视图层名称 34 | 'DEFAULT_LANG' => 'zh-cn', // 默认语言 35 | 'DEFAULT_MODULE' => 'Home', // 默认模块 36 | 'DEFAULT_CONTROLLER' => 'Index', // 默认控制器名称 37 | 'DEFAULT_ACTION' => 'index', // 默认操作名称 38 | 'DEFAULT_CHARSET' => 'utf-8', // 默认输出编码 39 | 'DEFAULT_TIMEZONE' => 'PRC', // 默认时区 40 | 'DEFAULT_AJAX_RETURN' => 'JSON', // 默认AJAX 数据返回格式,可选JSON XML ... 41 | 'DEFAULT_JSONP_HANDLER' => 'jsonpReturn', // 默认JSONP格式返回的处理方法 42 | 'DEFAULT_FILTER' => 'htmlspecialchars', // 默认参数过滤方法 用于I函数... 43 | 44 | /* 数据库设置 */ 45 | 'DB_TYPE' => '', // 数据库类型 46 | 'DB_HOST' => '', // 服务器地址 47 | 'DB_NAME' => '', // 数据库名 48 | 'DB_USER' => '', // 用户名 49 | 'DB_PWD' => '', // 密码 50 | 'DB_PORT' => '', // 端口 51 | 'DB_PREFIX' => '', // 数据库表前缀 52 | 'DB_FIELDTYPE_CHECK' => false, // 是否进行字段类型检查 53 | 'DB_FIELDS_CACHE' => true, // 启用字段缓存 54 | 'DB_CHARSET' => 'utf8', // 数据库编码默认采用utf8 55 | 'DB_DEPLOY_TYPE' => 0, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) 56 | 'DB_RW_SEPARATE' => false, // 数据库读写是否分离 主从式有效 57 | 'DB_MASTER_NUM' => 1, // 读写分离后 主服务器数量 58 | 'DB_SLAVE_NO' => '', // 指定从服务器序号 59 | 'DB_SQL_BUILD_CACHE' => false, // 数据库查询的SQL创建缓存 60 | 'DB_SQL_BUILD_QUEUE' => 'file', // SQL缓存队列的缓存方式 支持 file xcache和apc 61 | 'DB_SQL_BUILD_LENGTH' => 20, // SQL缓存的队列长度 62 | 'DB_SQL_LOG' => false, // SQL执行日志记录 63 | 'DB_BIND_PARAM' => false, // 数据库写入数据自动参数绑定 64 | 65 | /* 数据缓存设置 */ 66 | 'DATA_CACHE_TIME' => 0, // 数据缓存有效期 0表示永久缓存 67 | 'DATA_CACHE_COMPRESS' => false, // 数据缓存是否压缩缓存 68 | 'DATA_CACHE_CHECK' => false, // 数据缓存是否校验缓存 69 | 'DATA_CACHE_PREFIX' => '', // 缓存前缀 70 | 'DATA_CACHE_TYPE' => 'File', // 数据缓存类型,支持:File|Db|Apc|Memcache|Shmop|Sqlite|Xcache|Apachenote|Eaccelerator 71 | 'DATA_CACHE_PATH' => TEMP_PATH,// 缓存路径设置 (仅对File方式缓存有效) 72 | 'DATA_CACHE_SUBDIR' => false, // 使用子目录缓存 (自动根据缓存标识的哈希创建子目录) 73 | 'DATA_PATH_LEVEL' => 1, // 子目录缓存级别 74 | 75 | /* 错误设置 */ 76 | 'ERROR_MESSAGE' => '页面错误!请稍后再试~',//错误显示信息,非调试模式有效 77 | 'ERROR_PAGE' => '', // 错误定向页面 78 | 'SHOW_ERROR_MSG' => false, // 显示错误信息 79 | 'TRACE_MAX_RECORD' => 100, // 每个级别的错误信息 最大记录数 80 | 81 | /* 日志设置 */ 82 | 'LOG_RECORD' => false, // 默认不记录日志 83 | 'LOG_TYPE' => 'File', // 日志记录类型 默认为文件方式 84 | 'LOG_LEVEL' => 'EMERG,ALERT,CRIT,ERR',// 允许记录的日志级别 85 | 'LOG_FILE_SIZE' => 2097152, // 日志文件大小限制 86 | 'LOG_EXCEPTION_RECORD' => false, // 是否记录异常信息日志 87 | 88 | /* SESSION设置 */ 89 | 'SESSION_AUTO_START' => true, // 是否自动开启Session 90 | 'SESSION_OPTIONS' => array(), // session 配置数组 支持type name id path expire domain 等参数 91 | 'SESSION_TYPE' => '', // session hander类型 默认无需设置 除非扩展了session hander驱动 92 | 'SESSION_PREFIX' => '', // session 前缀 93 | //'VAR_SESSION_ID' => 'session_id', //sessionID的提交变量 94 | 95 | 96 | /* URL设置 */ 97 | 'URL_CASE_INSENSITIVE' => true, // 默认false 表示URL区分大小写 true则表示不区分大小写 98 | 'URL_MODEL' => 1, // URL访问模式,可选参数0、1、2、3,代表以下四种模式: 99 | // 0 (普通模式); 1 (PATHINFO 模式); 2 (REWRITE 模式); 3 (兼容模式) 默认为PATHINFO 模式 100 | 'URL_PATHINFO_DEPR' => '/', // PATHINFO模式下,各参数之间的分割符号 101 | 'URL_PATHINFO_FETCH' => 'ORIG_PATH_INFO,REDIRECT_PATH_INFO,REDIRECT_URL', // 用于兼容判断PATH_INFO 参数的SERVER替代变量列表 102 | 'URL_REQUEST_URI' => 'REQUEST_URI', // 获取当前页面地址的系统变量 默认为REQUEST_URI 103 | 'URL_HTML_SUFFIX' => 'html', // URL伪静态后缀设置 104 | 'URL_DENY_SUFFIX' => 'ico|png|gif|jpg', // URL禁止访问的后缀设置 105 | 'URL_PARAMS_BIND' => true, // URL变量绑定到Action方法参数 106 | 'URL_PARAMS_BIND_TYPE' => 0, // URL变量绑定的类型 0 按变量名绑定 1 按变量顺序绑定 107 | 'URL_PARAMS_FILTER' => false, // URL变量绑定过滤 108 | 'URL_PARAMS_FILTER_TYPE'=> '', // URL变量绑定过滤方法 如果为空 调用DEFAULT_FILTER 109 | 'URL_ROUTER_ON' => false, // 是否开启URL路由 110 | 'URL_ROUTE_RULES' => array(), // 默认路由规则 针对模块 111 | 'URL_MAP_RULES' => array(), // URL映射定义规则 112 | 113 | /* 系统变量名称设置 */ 114 | 'VAR_MODULE' => 'm', // 默认模块获取变量 115 | 'VAR_ADDON' => 'addon', // 默认的插件控制器命名空间变量 116 | 'VAR_CONTROLLER' => 'c', // 默认控制器获取变量 117 | 'VAR_ACTION' => 'a', // 默认操作获取变量 118 | 'VAR_AJAX_SUBMIT' => 'ajax', // 默认的AJAX提交变量 119 | 'VAR_JSONP_HANDLER' => 'callback', 120 | 'VAR_PATHINFO' => 's', // 兼容模式PATHINFO获取变量例如 ?s=/module/action/id/1 后面的参数取决于URL_PATHINFO_DEPR 121 | 'VAR_TEMPLATE' => 't', // 默认模板切换变量 122 | 123 | 'HTTP_CACHE_CONTROL' => 'private', // 网页缓存控制 124 | 'CHECK_APP_DIR' => true, // 是否检查应用目录是否创建 125 | 'FILE_UPLOAD_TYPE' => 'Local', // 文件上传方式 126 | 'DATA_CRYPT_TYPE' => '', // 数据加密方式 127 | 'LOAD_EXT_FILE' => 'functions', //需要加载的应用根目录Common下的文件名,以逗号隔开,纯名字,不带后缀php 128 | 'LOAD_EXT_CONFIG'=>'', //同上,需要加载的公共配置文件 129 | 130 | ); 131 | -------------------------------------------------------------------------------- /Core/Dispatcher.class.php: -------------------------------------------------------------------------------- 1 | 1){// 控制器层次 63 | // $_GET[$varController] = implode('/',array_slice($paths,0,C('CONTROLLER_LEVEL'))); 64 | // $paths = array_slice($paths, C('CONTROLLER_LEVEL')); 65 | // }else{ 66 | // $_GET[$varController] = array_shift($paths); 67 | // } 68 | 69 | // // 获取操作 70 | // if(!defined('BIND_ACTION')){ 71 | // $_GET[$varAction] = array_shift($paths); 72 | // } 73 | // } 74 | // // 获取控制器的命名空间(路径) 75 | // define('CONTROLLER_PATH', self::getSpace($varAddon,$urlCase)); 76 | // // 获取控制器和操作名 77 | // define('CONTROLLER_NAME', self::getController($varController,$urlCase)); 78 | // define('ACTION_NAME', self::getAction($varAction,$urlCase)); 79 | 80 | // // 当前控制器的UR地址 81 | // $controllerName = defined('CONTROLLER_ALIAS')? CONTROLLER_ALIAS : CONTROLLER_NAME; 82 | 83 | // //保证$_REQUEST正常取值 84 | // $_REQUEST = array_merge($_POST,$_GET); 85 | } 86 | 87 | /** 88 | * 获得控制器的命名空间路径 便于插件机制访问 89 | */ 90 | static private function getSpace($var,$urlCase) { 91 | $space = !empty($_GET[$var])?ucfirst($var).'\\'.strip_tags($_GET[$var]):''; 92 | unset($_GET[$var]); 93 | return $space; 94 | } 95 | 96 | /** 97 | * 获得实际的控制器名称 98 | */ 99 | static private function getController($var,$urlCase) { 100 | $controller = (!empty($_GET[$var])? $_GET[$var]:C('DEFAULT_CONTROLLER')); 101 | unset($_GET[$var]); 102 | if($maps = C('URL_CONTROLLER_MAP')) { 103 | if(isset($maps[strtolower($controller)])) { 104 | // 记录当前别名 105 | define('CONTROLLER_ALIAS',strtolower($controller)); 106 | // 获取实际的控制器名 107 | return ucfirst($maps[CONTROLLER_ALIAS]); 108 | }elseif(array_search(strtolower($controller),$maps)){ 109 | // 禁止访问原始控制器 110 | return ''; 111 | } 112 | } 113 | if($urlCase) { 114 | // URL地址不区分大小写 115 | // 智能识别方式 user_type 识别到 UserTypeController 控制器 116 | $controller = parse_name($controller,1); 117 | } 118 | return strip_tags(ucfirst($controller)); 119 | } 120 | 121 | /** 122 | * 获得实际的操作名称 123 | */ 124 | static private function getAction($var,$urlCase) { 125 | $action = !empty($_POST[$var]) ? 126 | $_POST[$var] : 127 | (!empty($_GET[$var])?$_GET[$var]:C('DEFAULT_ACTION')); 128 | unset($_POST[$var],$_GET[$var]); 129 | if($maps = C('URL_ACTION_MAP')) { 130 | if(isset($maps[strtolower(CONTROLLER_NAME)])) { 131 | $maps = $maps[strtolower(CONTROLLER_NAME)]; 132 | if(isset($maps[strtolower($action)])) { 133 | // 记录当前别名 134 | define('ACTION_ALIAS',strtolower($action)); 135 | // 获取实际的操作名 136 | if(is_array($maps[ACTION_ALIAS])){ 137 | parse_str($maps[ACTION_ALIAS][1],$vars); 138 | $_GET = array_merge($_GET,$vars); 139 | return $maps[ACTION_ALIAS][0]; 140 | }else{ 141 | return $maps[ACTION_ALIAS]; 142 | } 143 | 144 | }elseif(array_search(strtolower($action),$maps)){ 145 | // 禁止访问原始操作 146 | return ''; 147 | } 148 | } 149 | } 150 | return strip_tags( $urlCase? strtolower($action) : $action ); 151 | } 152 | 153 | /** 154 | * 获得实际的模块名称 155 | */ 156 | static private function getModule($var) { 157 | $module = (!empty($_GET[$var])?$_GET[$var]:C('DEFAULT_MODULE')); 158 | unset($_GET[$var]); 159 | if($maps = C('URL_MODULE_MAP')) { 160 | if(isset($maps[strtolower($module)])) { 161 | // 记录当前别名 162 | define('MODULE_ALIAS',strtolower($module)); 163 | // 获取实际的模块名 164 | return ucfirst($maps[MODULE_ALIAS]); 165 | }elseif(array_search(strtolower($module),$maps)){ 166 | // 禁止访问原始模块 167 | return ''; 168 | } 169 | } 170 | return strip_tags(ucfirst($module)); 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /Core/Db/Driver/Sqlite.class.php: -------------------------------------------------------------------------------- 1 | config = $config; 23 | if(empty($this->config['params'])) { 24 | $this->config['params'] = array(); 25 | } 26 | } 27 | } 28 | 29 | /** 30 | * 连接数据库方法 31 | * @access public 32 | */ 33 | public function connect($config='',$linkNum=0) { 34 | if ( !isset($this->linkID[$linkNum]) ) { 35 | if(empty($config)) $config = $this->config; 36 | $pconnect = !empty($config['params']['persist'])? $config['params']['persist']:$this->pconnect; 37 | $conn = $pconnect ? 'sqlite_popen':'sqlite_open'; 38 | $this->linkID[$linkNum] = $conn($config['database'],$config['mode']); 39 | if ( !$this->linkID[$linkNum]) { 40 | E(sqlite_error_string()); 41 | } 42 | // 标记连接成功 43 | $this->connected = true; 44 | //注销数据库安全信息 45 | if(1 != C('DB_DEPLOY_TYPE')) unset($this->config); 46 | } 47 | return $this->linkID[$linkNum]; 48 | } 49 | 50 | /** 51 | * 释放查询结果 52 | * @access public 53 | */ 54 | public function free() { 55 | $this->queryID = null; 56 | } 57 | 58 | /** 59 | * 执行查询 返回数据集 60 | * @access public 61 | * @param string $str sql指令 62 | * @return mixed 63 | */ 64 | public function query($str) { 65 | $this->initConnect(false); 66 | if ( !$this->_linkID ) return false; 67 | $this->queryStr = $str; 68 | //释放前次的查询结果 69 | if ( $this->queryID ) $this->free(); 70 | N('db_query',1); 71 | // 记录开始执行时间 72 | G('queryStartTime'); 73 | $this->queryID = sqlite_query($this->_linkID,$str); 74 | $this->debug(); 75 | if ( false === $this->queryID ) { 76 | $this->error(); 77 | return false; 78 | } else { 79 | $this->numRows = sqlite_num_rows($this->queryID); 80 | return $this->getAll(); 81 | } 82 | } 83 | 84 | /** 85 | * 执行语句 86 | * @access public 87 | * @param string $str sql指令 88 | * @return integer 89 | */ 90 | public function execute($str) { 91 | $this->initConnect(true); 92 | if ( !$this->_linkID ) return false; 93 | $this->queryStr = $str; 94 | //释放前次的查询结果 95 | if ( $this->queryID ) $this->free(); 96 | N('db_write',1); 97 | // 记录开始执行时间 98 | G('queryStartTime'); 99 | $result = sqlite_exec($this->_linkID,$str); 100 | $this->debug(); 101 | if ( false === $result ) { 102 | $this->error(); 103 | return false; 104 | } else { 105 | $this->numRows = sqlite_changes($this->_linkID); 106 | $this->lastInsID = sqlite_last_insert_rowid($this->_linkID); 107 | return $this->numRows; 108 | } 109 | } 110 | 111 | /** 112 | * 启动事务 113 | * @access public 114 | * @return void 115 | */ 116 | public function startTrans() { 117 | $this->initConnect(true); 118 | if ( !$this->_linkID ) return false; 119 | //数据rollback 支持 120 | if ($this->transTimes == 0) { 121 | sqlite_query($this->_linkID,'BEGIN TRANSACTION'); 122 | } 123 | $this->transTimes++; 124 | return ; 125 | } 126 | 127 | /** 128 | * 用于非自动提交状态下面的查询提交 129 | * @access public 130 | * @return boolen 131 | */ 132 | public function commit() { 133 | if ($this->transTimes > 0) { 134 | $result = sqlite_query($this->_linkID,'COMMIT TRANSACTION'); 135 | if(!$result){ 136 | $this->error(); 137 | return false; 138 | } 139 | $this->transTimes = 0; 140 | } 141 | return true; 142 | } 143 | 144 | /** 145 | * 事务回滚 146 | * @access public 147 | * @return boolen 148 | */ 149 | public function rollback() { 150 | if ($this->transTimes > 0) { 151 | $result = sqlite_query($this->_linkID,'ROLLBACK TRANSACTION'); 152 | if(!$result){ 153 | $this->error(); 154 | return false; 155 | } 156 | $this->transTimes = 0; 157 | } 158 | return true; 159 | } 160 | 161 | /** 162 | * 获得所有的查询数据 163 | * @access private 164 | * @return array 165 | */ 166 | private function getAll() { 167 | //返回数据集 168 | $result = array(); 169 | if($this->numRows >0) { 170 | for($i=0;$i<$this->numRows ;$i++ ){ 171 | // 返回数组集 172 | $result[$i] = sqlite_fetch_array($this->queryID,SQLITE_ASSOC); 173 | } 174 | sqlite_seek($this->queryID,0); 175 | } 176 | return $result; 177 | } 178 | 179 | /** 180 | * 取得数据表的字段信息 181 | * @access public 182 | * @return array 183 | */ 184 | public function getFields($tableName) { 185 | $result = $this->query('PRAGMA table_info( '.$tableName.' )'); 186 | $info = array(); 187 | if($result){ 188 | foreach ($result as $key => $val) { 189 | $info[$val['Field']] = array( 190 | 'name' => $val['Field'], 191 | 'type' => $val['Type'], 192 | 'notnull' => (bool) ($val['Null'] === ''), // not null is empty, null is yes 193 | 'default' => $val['Default'], 194 | 'primary' => (strtolower($val['Key']) == 'pri'), 195 | 'autoinc' => (strtolower($val['Extra']) == 'auto_increment'), 196 | ); 197 | } 198 | } 199 | return $info; 200 | } 201 | 202 | /** 203 | * 取得数据库的表信息 204 | * @access public 205 | * @return array 206 | */ 207 | public function getTables($dbName='') { 208 | $result = $this->query("SELECT name FROM sqlite_master WHERE type='table' " 209 | . "UNION ALL SELECT name FROM sqlite_temp_master " 210 | . "WHERE type='table' ORDER BY name"); 211 | $info = array(); 212 | foreach ($result as $key => $val) { 213 | $info[$key] = current($val); 214 | } 215 | return $info; 216 | } 217 | 218 | /** 219 | * 关闭数据库 220 | * @access public 221 | */ 222 | public function close() { 223 | if ($this->_linkID){ 224 | sqlite_close($this->_linkID); 225 | } 226 | $this->_linkID = null; 227 | } 228 | 229 | /** 230 | * 数据库错误信息 231 | * 并显示当前的SQL语句 232 | * @access public 233 | * @return string 234 | */ 235 | public function error() { 236 | $code = sqlite_last_error($this->_linkID); 237 | $this->error = $code.':'.sqlite_error_string($code); 238 | if('' != $this->queryStr){ 239 | $this->error .= "\n [ SQL语句 ] : ".$this->queryStr; 240 | } 241 | trace($this->error,'','ERR'); 242 | return $this->error; 243 | } 244 | 245 | /** 246 | * SQL指令安全过滤 247 | * @access public 248 | * @param string $str SQL指令 249 | * @return string 250 | */ 251 | public function escapeString($str) { 252 | return sqlite_escape_string($str); 253 | } 254 | 255 | /** 256 | * limit 257 | * @access public 258 | * @return string 259 | */ 260 | public function parseLimit($limit) { 261 | $limitStr = ''; 262 | if(!empty($limit)) { 263 | $limit = explode(',',$limit); 264 | if(count($limit)>1) { 265 | $limitStr .= ' LIMIT '.$limit[1].' OFFSET '.$limit[0].' '; 266 | }else{ 267 | $limitStr .= ' LIMIT '.$limit[0].' '; 268 | } 269 | } 270 | return $limitStr; 271 | } 272 | } -------------------------------------------------------------------------------- /Core/Core.class.php: -------------------------------------------------------------------------------- 1 | $file){ 57 | is_numeric($key)?C(load_config($file)):C($key,load_config($file)); 58 | } 59 | 60 | // 读取当前应用模式对应的配置文件 61 | if('common' != APP_MODE && is_file(CONF_PATH.'config_'.APP_MODE.CONF_EXT)) 62 | C(load_config(CONF_PATH.'config_'.APP_MODE.CONF_EXT)); 63 | 64 | if(!APP_DEBUG){ 65 | $content .= "\nnamespace { Core\Core::addMap(".var_export(self::$_map,true).");"; 66 | $content .= "\nL(".var_export(L(),true).");\nC(".var_export(C(),true).');}'; 67 | Storage::put($runtimefile,strip_whitespace('getMessage(); 190 | $trace = $e->getTrace(); 191 | if('E'==$trace[0]['function']) { 192 | $error['file'] = $trace[0]['file']; 193 | $error['line'] = $trace[0]['line']; 194 | }else{ 195 | $error['file'] = $e->getFile(); 196 | $error['line'] = $e->getLine(); 197 | } 198 | $error['trace'] = $e->getTraceAsString(); 199 | Log::record($error['message'],Log::ERR); 200 | // 发送404信息 201 | header('HTTP/1.1 404 Not Found'); 202 | header('Status:404 Not Found'); 203 | self::halt($error); 204 | } 205 | 206 | /** 207 | * 自定义错误处理 208 | * @access public 209 | * @param int $errno 错误类型 210 | * @param string $errstr 错误信息 211 | * @param string $errfile 错误文件 212 | * @param int $errline 错误行数 213 | * @return void 214 | */ 215 | static public function appError($errno, $errstr, $errfile, $errline) { 216 | switch ($errno) { 217 | case E_ERROR: 218 | case E_PARSE: 219 | case E_CORE_ERROR: 220 | case E_COMPILE_ERROR: 221 | case E_USER_ERROR: 222 | ob_end_clean(); 223 | $errorStr = "$errstr ".$errfile." 第 $errline 行."; 224 | if(C('LOG_RECORD')) Log::write("[$errno] ".$errorStr,Log::ERR); 225 | self::halt($errorStr); 226 | break; 227 | default: 228 | $errorStr = "[$errno] $errstr ".$errfile." 第 $errline 行."; 229 | self::trace($errorStr,'','NOTIC'); 230 | break; 231 | } 232 | } 233 | 234 | // 致命错误捕获 235 | static public function fatalError() { 236 | Log::save(); 237 | if ($e = error_get_last()) { 238 | switch($e['type']){ 239 | case E_ERROR: 240 | case E_PARSE: 241 | case E_CORE_ERROR: 242 | case E_COMPILE_ERROR: 243 | case E_USER_ERROR: 244 | ob_end_clean(); 245 | self::halt($e); 246 | break; 247 | } 248 | } 249 | } 250 | 251 | /** 252 | * 错误输出 253 | * @param mixed $error 错误 254 | * @return void 255 | */ 256 | static public function halt($error) { 257 | $e = array(); 258 | //将致命错误记录到log中 add by madong 259 | Log::write('致命错误:'.json_encode($error,JSON_UNESCAPED_UNICODE)); 260 | if (APP_DEBUG || IS_CLI) { 261 | //调试模式下输出错误信息 262 | if (!is_array($error)) { 263 | $trace = debug_backtrace(); 264 | $e['message'] = $error; 265 | $e['file'] = $trace[0]['file']; 266 | $e['line'] = $trace[0]['line']; 267 | ob_start(); 268 | debug_print_backtrace(); 269 | $e['trace'] = ob_get_clean(); 270 | } else { 271 | $e = $error; 272 | } 273 | if(IS_CLI){ 274 | exit(iconv('UTF-8','gbk',$e['message']).PHP_EOL.'FILE: '.$e['file'].'('.$e['line'].')'.PHP_EOL.$e['trace']); 275 | } 276 | } 277 | echo '{"data":"","errcode":-20000,"errmsg":"未知异常"}'; 278 | exit; 279 | } 280 | 281 | /** 282 | * 添加和获取页面Trace记录 283 | * @param string $value 变量 284 | * @param string $label 标签 285 | * @param string $level 日志级别(或者页面Trace的选项卡) 286 | * @param boolean $record 是否记录日志 287 | * @return void 288 | */ 289 | static public function trace($value='[core]',$label='',$level='DEBUG',$record=false) { 290 | trace($value,$label,$level,$record); 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /Core/Model/MongoModel.class.php: -------------------------------------------------------------------------------- 1 | methods,true)) { 34 | // 连贯操作的实现 35 | $this->options[strtolower($method)] = $args[0]; 36 | return $this; 37 | }elseif(strtolower(substr($method,0,5))=='getby') { 38 | // 根据某个字段获取记录 39 | $field = parse_name(substr($method,5)); 40 | $where[$field] =$args[0]; 41 | return $this->where($where)->find(); 42 | }elseif(strtolower(substr($method,0,10))=='getfieldby') { 43 | // 根据某个字段获取记录的某个值 44 | $name = parse_name(substr($method,10)); 45 | $where[$name] =$args[0]; 46 | return $this->where($where)->getField($args[1]); 47 | }else{ 48 | E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_')); 49 | return; 50 | } 51 | } 52 | 53 | /** 54 | * 获取字段信息并缓存 主键和自增信息直接配置 55 | * @access public 56 | * @return void 57 | */ 58 | public function flush() { 59 | // 缓存不存在则查询数据表信息 60 | $fields = $this->db->getFields(); 61 | if(!$fields) { // 暂时没有数据无法获取字段信息 下次查询 62 | return false; 63 | } 64 | $this->fields = array_keys($fields); 65 | foreach ($fields as $key=>$val){ 66 | // 记录字段类型 67 | $type[$key] = $val['type']; 68 | } 69 | // 记录字段类型信息 70 | if(C('DB_FIELDTYPE_CHECK')) $this->fields['_type'] = $type; 71 | 72 | // 2008-3-7 增加缓存开关控制 73 | if(C('DB_FIELDS_CACHE')){ 74 | // 永久缓存数据表信息 75 | $db = $this->dbName?$this->dbName:C('DB_NAME'); 76 | F('_fields/'.$db.'.'.$this->name,$this->fields); 77 | } 78 | } 79 | 80 | // 写入数据前的回调方法 包括新增和更新 81 | protected function _before_write(&$data) { 82 | $pk = $this->getPk(); 83 | // 根据主键类型处理主键数据 84 | if(isset($data[$pk]) && $this->_idType == self::TYPE_OBJECT) { 85 | $data[$pk] = new \MongoId($data[$pk]); 86 | } 87 | } 88 | 89 | /** 90 | * count统计 配合where连贯操作 91 | * @access public 92 | * @return integer 93 | */ 94 | public function count(){ 95 | // 分析表达式 96 | $options = $this->_parseOptions(); 97 | return $this->db->count($options); 98 | } 99 | 100 | /** 101 | * 获取下一ID 用于自动增长型 102 | * @access public 103 | * @param string $pk 字段名 默认为主键 104 | * @return mixed 105 | */ 106 | public function getMongoNextId($pk=''){ 107 | if(empty($pk)) { 108 | $pk = $this->getPk(); 109 | } 110 | return $this->db->mongo_next_id($pk); 111 | } 112 | 113 | /** 114 | * 新增数据 115 | * @access public 116 | * @param mixed $data 数据 117 | * @param array $options 表达式 118 | * @param boolean $replace 是否replace 119 | * @return mixed 120 | */ 121 | public function add($data='',$options=array(),$replace=false) { 122 | if(empty($data)) { 123 | // 没有传递数据,获取当前数据对象的值 124 | if(!empty($this->data)) { 125 | $data = $this->data; 126 | // 重置数据 127 | $this->data = array(); 128 | }else{ 129 | $this->error = L('_DATA_TYPE_INVALID_'); 130 | return false; 131 | } 132 | } 133 | // 分析表达式 134 | $options = $this->_parseOptions($options); 135 | // 数据处理 136 | $data = $this->_facade($data); 137 | if(false === $this->_before_insert($data,$options)) { 138 | return false; 139 | } 140 | // 写入数据到数据库 141 | $result = $this->db->insert($data,$options,$replace); 142 | if(false !== $result ) { 143 | $this->_after_insert($data,$options); 144 | if(isset($data[$this->getPk()])){ 145 | return $data[$this->getPk()]; 146 | } 147 | } 148 | return $result; 149 | } 150 | 151 | // 插入数据前的回调方法 152 | protected function _before_insert(&$data,$options) { 153 | // 写入数据到数据库 154 | if($this->_autoinc && $this->_idType== self::TYPE_INT) { // 主键自动增长 155 | $pk = $this->getPk(); 156 | if(!isset($data[$pk])) { 157 | $data[$pk] = $this->db->mongo_next_id($pk); 158 | } 159 | } 160 | } 161 | 162 | public function clear(){ 163 | return $this->db->clear(); 164 | } 165 | 166 | // 查询成功后的回调方法 167 | protected function _after_select(&$resultSet,$options) { 168 | array_walk($resultSet,array($this,'checkMongoId')); 169 | } 170 | 171 | /** 172 | * 获取MongoId 173 | * @access protected 174 | * @param array $result 返回数据 175 | * @return array 176 | */ 177 | protected function checkMongoId(&$result){ 178 | if(is_object($result['_id'])) { 179 | $result['_id'] = $result['_id']->__toString(); 180 | } 181 | return $result; 182 | } 183 | 184 | // 表达式过滤回调方法 185 | protected function _options_filter(&$options) { 186 | $id = $this->getPk(); 187 | if(isset($options['where'][$id]) && is_scalar($options['where'][$id]) && $this->_idType== self::TYPE_OBJECT) { 188 | $options['where'][$id] = new \MongoId($options['where'][$id]); 189 | } 190 | } 191 | 192 | /** 193 | * 查询数据 194 | * @access public 195 | * @param mixed $options 表达式参数 196 | * @return mixed 197 | */ 198 | public function find($options=array()) { 199 | if( is_numeric($options) || is_string($options)) { 200 | $id = $this->getPk(); 201 | $where[$id] = $options; 202 | $options = array(); 203 | $options['where'] = $where; 204 | } 205 | // 分析表达式 206 | $options = $this->_parseOptions($options); 207 | $result = $this->db->find($options); 208 | if(false === $result) { 209 | return false; 210 | } 211 | if(empty($result)) {// 查询结果为空 212 | return null; 213 | }else{ 214 | $this->checkMongoId($result); 215 | } 216 | $this->data = $result; 217 | $this->_after_find($this->data,$options); 218 | return $this->data; 219 | } 220 | 221 | /** 222 | * 字段值增长 223 | * @access public 224 | * @param string $field 字段名 225 | * @param integer $step 增长值 226 | * @return boolean 227 | */ 228 | public function setInc($field,$step=1) { 229 | return $this->setField($field,array('inc',$step)); 230 | } 231 | 232 | /** 233 | * 字段值减少 234 | * @access public 235 | * @param string $field 字段名 236 | * @param integer $step 减少值 237 | * @return boolean 238 | */ 239 | public function setDec($field,$step=1) { 240 | return $this->setField($field,array('inc','-'.$step)); 241 | } 242 | 243 | /** 244 | * 获取一条记录的某个字段值 245 | * @access public 246 | * @param string $field 字段名 247 | * @param string $spea 字段数据间隔符号 248 | * @return mixed 249 | */ 250 | public function getField($field,$sepa=null) { 251 | $options['field'] = $field; 252 | $options = $this->_parseOptions($options); 253 | if(strpos($field,',')) { // 多字段 254 | if(is_numeric($sepa)) {// 限定数量 255 | $options['limit'] = $sepa; 256 | $sepa = null;// 重置为null 返回数组 257 | } 258 | $resultSet = $this->db->select($options); 259 | if(!empty($resultSet)) { 260 | $_field = explode(',', $field); 261 | $field = array_keys($resultSet[0]); 262 | $key = array_shift($field); 263 | $key2 = array_shift($field); 264 | $cols = array(); 265 | $count = count($_field); 266 | foreach ($resultSet as $result){ 267 | $name = $result[$key]; 268 | if(2==$count) { 269 | $cols[$name] = $result[$key2]; 270 | }else{ 271 | $cols[$name] = is_null($sepa)?$result:implode($sepa,$result); 272 | } 273 | } 274 | return $cols; 275 | } 276 | }else{ 277 | // 返回数据个数 278 | if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据 279 | $options['limit'] = is_numeric($sepa)?$sepa:1; 280 | } // 查找一条记录 281 | $result = $this->db->find($options); 282 | if(!empty($result)) { 283 | if(1==$options['limit']) return reset($result[0]); 284 | foreach ($result as $val){ 285 | $array[] = $val[$field]; 286 | } 287 | return $array; 288 | } 289 | } 290 | return null; 291 | } 292 | 293 | /** 294 | * 执行Mongo指令 295 | * @access public 296 | * @param array $command 指令 297 | * @return mixed 298 | */ 299 | public function command($command) { 300 | return $this->db->command($command); 301 | } 302 | 303 | /** 304 | * 执行MongoCode 305 | * @access public 306 | * @param string $code MongoCode 307 | * @param array $args 参数 308 | * @return mixed 309 | */ 310 | public function mongoCode($code,$args=array()) { 311 | return $this->db->execute($code,$args); 312 | } 313 | 314 | // 数据库切换后回调方法 315 | protected function _after_db() { 316 | // 切换Collection 317 | $this->db->switchCollection($this->getTableName(),$this->dbName?$this->dbName:C('db_name')); 318 | } 319 | 320 | /** 321 | * 得到完整的数据表名 Mongo表名不带dbName 322 | * @access public 323 | * @return string 324 | */ 325 | public function getTableName() { 326 | if(empty($this->trueTableName)) { 327 | $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : ''; 328 | if(!empty($this->tableName)) { 329 | $tableName .= $this->tableName; 330 | }else{ 331 | $tableName .= parse_name($this->name); 332 | } 333 | $this->trueTableName = strtolower($tableName); 334 | } 335 | return $this->trueTableName; 336 | } 337 | } -------------------------------------------------------------------------------- /Core/Crypt/Driver/Des.class.php: -------------------------------------------------------------------------------- 1 | 0 && $expire < time()) { 41 | return ''; 42 | } 43 | $data = substr($data,10); 44 | return $data; 45 | } 46 | 47 | /** 48 | * Des算法 49 | * @param string $str 字符串 50 | * @param string $key 加密key 51 | * @return string 52 | */ 53 | private static function _des($key, $message, $encrypt, $mode=0, $iv=null) { 54 | //declaring this locally speeds things up a bit 55 | $spfunction1 = array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004); 56 | $spfunction2 = array (-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000); 57 | $spfunction3 = array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200); 58 | $spfunction4 = array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080); 59 | $spfunction5 = array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100); 60 | $spfunction6 = array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010); 61 | $spfunction7 = array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002); 62 | $spfunction8 = array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000); 63 | $masks = array (4294967295,2147483647,1073741823,536870911,268435455,134217727,67108863,33554431,16777215,8388607,4194303,2097151,1048575,524287,262143,131071,65535,32767,16383,8191,4095,2047,1023,511,255,127,63,31,15,7,3,1,0); 64 | 65 | //create the 16 or 48 subkeys we will need 66 | $keys = self::_createKeys ($key); 67 | $m=0; 68 | $len = strlen($message); 69 | $chunk = 0; 70 | //set up the loops for single and triple des 71 | $iterations = ((count($keys) == 32) ? 3 : 9); //single or triple des 72 | if ($iterations == 3) {$looping = (($encrypt) ? array (0, 32, 2) : array (30, -2, -2));} 73 | else {$looping = (($encrypt) ? array (0, 32, 2, 62, 30, -2, 64, 96, 2) : array (94, 62, -2, 32, 64, 2, 30, -2, -2));} 74 | 75 | $message .= (chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0)); //pad the message out with null bytes 76 | //store the result here 77 | $result = ""; 78 | $tempresult = ""; 79 | 80 | if ($mode == 1) { //CBC mode 81 | $cbcleft = (ord($iv{$m++}) << 24) | (ord($iv{$m++}) << 16) | (ord($iv{$m++}) << 8) | ord($iv{$m++}); 82 | $cbcright = (ord($iv{$m++}) << 24) | (ord($iv{$m++}) << 16) | (ord($iv{$m++}) << 8) | ord($iv{$m++}); 83 | $m=0; 84 | } 85 | 86 | //loop through each 64 bit chunk of the message 87 | while ($m < $len) { 88 | $left = (ord($message{$m++}) << 24) | (ord($message{$m++}) << 16) | (ord($message{$m++}) << 8) | ord($message{$m++}); 89 | $right = (ord($message{$m++}) << 24) | (ord($message{$m++}) << 16) | (ord($message{$m++}) << 8) | ord($message{$m++}); 90 | 91 | //for Cipher Block Chaining mode, xor the message with the previous result 92 | if ($mode == 1) {if ($encrypt) {$left ^= $cbcleft; $right ^= $cbcright;} else {$cbcleft2 = $cbcleft; $cbcright2 = $cbcright; $cbcleft = $left; $cbcright = $right;}} 93 | 94 | //first each 64 but chunk of the message must be permuted according to IP 95 | $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f; $right ^= $temp; $left ^= ($temp << 4); 96 | $temp = (($left >> 16 & $masks[16]) ^ $right) & 0x0000ffff; $right ^= $temp; $left ^= ($temp << 16); 97 | $temp = (($right >> 2 & $masks[2]) ^ $left) & 0x33333333; $left ^= $temp; $right ^= ($temp << 2); 98 | $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff; $left ^= $temp; $right ^= ($temp << 8); 99 | $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555; $right ^= $temp; $left ^= ($temp << 1); 100 | 101 | $left = (($left << 1) | ($left >> 31 & $masks[31])); 102 | $right = (($right << 1) | ($right >> 31 & $masks[31])); 103 | 104 | //do this either 1 or 3 times for each chunk of the message 105 | for ($j=0; $j<$iterations; $j+=3) { 106 | $endloop = $looping[$j+1]; 107 | $loopinc = $looping[$j+2]; 108 | //now go through and perform the encryption or decryption 109 | for ($i=$looping[$j]; $i!=$endloop; $i+=$loopinc) { //for efficiency 110 | $right1 = $right ^ $keys[$i]; 111 | $right2 = (($right >> 4 & $masks[4]) | ($right << 28)) ^ $keys[$i+1]; 112 | //the result is attained by passing these bytes through the S selection functions 113 | $temp = $left; 114 | $left = $right; 115 | $right = $temp ^ ($spfunction2[($right1 >> 24 & $masks[24]) & 0x3f] | $spfunction4[($right1 >> 16 & $masks[16]) & 0x3f] 116 | | $spfunction6[($right1 >> 8 & $masks[8]) & 0x3f] | $spfunction8[$right1 & 0x3f] 117 | | $spfunction1[($right2 >> 24 & $masks[24]) & 0x3f] | $spfunction3[($right2 >> 16 & $masks[16]) & 0x3f] 118 | | $spfunction5[($right2 >> 8 & $masks[8]) & 0x3f] | $spfunction7[$right2 & 0x3f]); 119 | } 120 | $temp = $left; $left = $right; $right = $temp; //unreverse left and right 121 | } //for either 1 or 3 iterations 122 | 123 | //move then each one bit to the right 124 | $left = (($left >> 1 & $masks[1]) | ($left << 31)); 125 | $right = (($right >> 1 & $masks[1]) | ($right << 31)); 126 | 127 | //now perform IP-1, which is IP in the opposite direction 128 | $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555; $right ^= $temp; $left ^= ($temp << 1); 129 | $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff; $left ^= $temp; $right ^= ($temp << 8); 130 | $temp = (($right >> 2 & $masks[2]) ^ $left) & 0x33333333; $left ^= $temp; $right ^= ($temp << 2); 131 | $temp = (($left >> 16 & $masks[16]) ^ $right) & 0x0000ffff; $right ^= $temp; $left ^= ($temp << 16); 132 | $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f; $right ^= $temp; $left ^= ($temp << 4); 133 | 134 | //for Cipher Block Chaining mode, xor the message with the previous result 135 | if ($mode == 1) {if ($encrypt) {$cbcleft = $left; $cbcright = $right;} else {$left ^= $cbcleft2; $right ^= $cbcright2;}} 136 | $tempresult .= (chr($left>>24 & $masks[24]) . chr(($left>>16 & $masks[16]) & 0xff) . chr(($left>>8 & $masks[8]) & 0xff) . chr($left & 0xff) . chr($right>>24 & $masks[24]) . chr(($right>>16 & $masks[16]) & 0xff) . chr(($right>>8 & $masks[8]) & 0xff) . chr($right & 0xff)); 137 | 138 | $chunk += 8; 139 | if ($chunk == 512) {$result .= $tempresult; $tempresult = ""; $chunk = 0;} 140 | } //for every 8 characters, or 64 bits in the message 141 | 142 | //return the result as an array 143 | return ($result . $tempresult); 144 | } //end of des 145 | 146 | /** 147 | * createKeys 148 | * this takes as input a 64 bit key (even though only 56 bits are used) 149 | * as an array of 2 integers, and returns 16 48 bit keys 150 | * @param string $key 加密key 151 | * @return string 152 | */ 153 | private static function _createKeys ($key) { 154 | //declaring this locally speeds things up a bit 155 | $pc2bytes0 = array (0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204); 156 | $pc2bytes1 = array (0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101); 157 | $pc2bytes2 = array (0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808); 158 | $pc2bytes3 = array (0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000); 159 | $pc2bytes4 = array (0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010); 160 | $pc2bytes5 = array (0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420); 161 | $pc2bytes6 = array (0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002); 162 | $pc2bytes7 = array (0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800); 163 | $pc2bytes8 = array (0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002); 164 | $pc2bytes9 = array (0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408); 165 | $pc2bytes10 = array (0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020); 166 | $pc2bytes11 = array (0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200); 167 | $pc2bytes12 = array (0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010); 168 | $pc2bytes13 = array (0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105); 169 | $masks = array (4294967295,2147483647,1073741823,536870911,268435455,134217727,67108863,33554431,16777215,8388607,4194303,2097151,1048575,524287,262143,131071,65535,32767,16383,8191,4095,2047,1023,511,255,127,63,31,15,7,3,1,0); 170 | 171 | //how many iterations (1 for des, 3 for triple des) 172 | $iterations = ((strlen($key) >= 24) ? 3 : 1); 173 | //stores the return keys 174 | $keys = array (); // size = 32 * iterations but you don't specify this in php 175 | //now define the left shifts which need to be done 176 | $shifts = array (0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0); 177 | //other variables 178 | $m=0; 179 | $n=0; 180 | 181 | for ($j=0; $j<$iterations; $j++) { //either 1 or 3 iterations 182 | $left = (ord($key{$m++}) << 24) | (ord($key{$m++}) << 16) | (ord($key{$m++}) << 8) | ord($key{$m++}); 183 | $right = (ord($key{$m++}) << 24) | (ord($key{$m++}) << 16) | (ord($key{$m++}) << 8) | ord($key{$m++}); 184 | 185 | $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f; $right ^= $temp; $left ^= ($temp << 4); 186 | $temp = (($right >> 16 & $masks[16]) ^ $left) & 0x0000ffff; $left ^= $temp; $right ^= ($temp << -16); 187 | $temp = (($left >> 2 & $masks[2]) ^ $right) & 0x33333333; $right ^= $temp; $left ^= ($temp << 2); 188 | $temp = (($right >> 16 & $masks[16]) ^ $left) & 0x0000ffff; $left ^= $temp; $right ^= ($temp << -16); 189 | $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555; $right ^= $temp; $left ^= ($temp << 1); 190 | $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff; $left ^= $temp; $right ^= ($temp << 8); 191 | $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555; $right ^= $temp; $left ^= ($temp << 1); 192 | 193 | //the right side needs to be shifted and to get the last four bits of the left side 194 | $temp = ($left << 8) | (($right >> 20 & $masks[20]) & 0x000000f0); 195 | //left needs to be put upside down 196 | $left = ($right << 24) | (($right << 8) & 0xff0000) | (($right >> 8 & $masks[8]) & 0xff00) | (($right >> 24 & $masks[24]) & 0xf0); 197 | $right = $temp; 198 | 199 | //now go through and perform these shifts on the left and right keys 200 | for ($i=0; $i < count($shifts); $i++) { 201 | //shift the keys either one or two bits to the left 202 | if ($shifts[$i] > 0) { 203 | $left = (($left << 2) | ($left >> 26 & $masks[26])); 204 | $right = (($right << 2) | ($right >> 26 & $masks[26])); 205 | } else { 206 | $left = (($left << 1) | ($left >> 27 & $masks[27])); 207 | $right = (($right << 1) | ($right >> 27 & $masks[27])); 208 | } 209 | $left = $left & -0xf; 210 | $right = $right & -0xf; 211 | 212 | //now apply PC-2, in such a way that E is easier when encrypting or decrypting 213 | //this conversion will look like PC-2 except only the last 6 bits of each byte are used 214 | //rather than 48 consecutive bits and the order of lines will be according to 215 | //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7 216 | $lefttemp = $pc2bytes0[$left >> 28 & $masks[28]] | $pc2bytes1[($left >> 24 & $masks[24]) & 0xf] 217 | | $pc2bytes2[($left >> 20 & $masks[20]) & 0xf] | $pc2bytes3[($left >> 16 & $masks[16]) & 0xf] 218 | | $pc2bytes4[($left >> 12 & $masks[12]) & 0xf] | $pc2bytes5[($left >> 8 & $masks[8]) & 0xf] 219 | | $pc2bytes6[($left >> 4 & $masks[4]) & 0xf]; 220 | $righttemp = $pc2bytes7[$right >> 28 & $masks[28]] | $pc2bytes8[($right >> 24 & $masks[24]) & 0xf] 221 | | $pc2bytes9[($right >> 20 & $masks[20]) & 0xf] | $pc2bytes10[($right >> 16 & $masks[16]) & 0xf] 222 | | $pc2bytes11[($right >> 12 & $masks[12]) & 0xf] | $pc2bytes12[($right >> 8 & $masks[8]) & 0xf] 223 | | $pc2bytes13[($right >> 4 & $masks[4]) & 0xf]; 224 | $temp = (($righttemp >> 16 & $masks[16]) ^ $lefttemp) & 0x0000ffff; 225 | $keys[$n++] = $lefttemp ^ $temp; $keys[$n++] = $righttemp ^ ($temp << 16); 226 | } 227 | } //for each iterations 228 | //return the keys we've created 229 | return $keys; 230 | } //end of des_createKeys 231 | 232 | } -------------------------------------------------------------------------------- /Core/Db/Driver/Pdo.class.php: -------------------------------------------------------------------------------- 1 | config = $config; 21 | if(empty($this->config['params'])) { 22 | $this->config['params'] = array(); 23 | } 24 | $this->dbType = $this->_getDsnType($config['dsn']); 25 | } 26 | 27 | } 28 | 29 | /** 30 | * 连接数据库方法 31 | * @access public 32 | */ 33 | public function connect($config='',$linkNum=0) { 34 | if ( !isset($this->linkID[$linkNum]) ) { 35 | if(empty($config)) $config = $this->config; 36 | if($this->pconnect) { 37 | $config['params'][\PDO::ATTR_PERSISTENT] = true; 38 | } 39 | if(version_compare(PHP_VERSION,'5.3.6','<=')){ //禁用模拟预处理语句 40 | $config['params'][\PDO::ATTR_EMULATE_PREPARES] = false; 41 | } 42 | //$config['params'][PDO::ATTR_CASE] = C("DB_CASE_LOWER")?PDO::CASE_LOWER:PDO::CASE_UPPER; 43 | try{ 44 | $this->linkID[$linkNum] = new \PDO( $config['dsn'], $config['username'], $config['password'],$config['params']); 45 | }catch (\PDOException $e) { 46 | E($e->getMessage()); 47 | } 48 | // 因为PDO的连接切换可能导致数据库类型不同,因此重新获取下当前的数据库类型 49 | $this->dbType = $this->_getDsnType($config['dsn']); 50 | if(in_array($this->dbType,array('MSSQL','ORACLE','IBASE','OCI'))) { 51 | // 由于PDO对于以上的数据库支持不够完美,所以屏蔽了 如果仍然希望使用PDO 可以注释下面一行代码 52 | E('由于目前PDO暂时不能完美支持'.$this->dbType.' 请使用官方的'.$this->dbType.'驱动'); 53 | } 54 | $this->linkID[$linkNum]->exec('SET NAMES '.$config['charset']); 55 | // 标记连接成功 56 | $this->connected = true; 57 | // 注销数据库连接配置信息 58 | if(1 != C('DB_DEPLOY_TYPE')) unset($this->config); 59 | Log::record('db connect'); 60 | } 61 | 62 | return $this->linkID[$linkNum]; 63 | } 64 | 65 | /** 66 | * 释放查询结果 67 | * @access public 68 | */ 69 | public function free() { 70 | $this->PDOStatement = null; 71 | } 72 | 73 | /** 74 | * 执行查询 返回数据集 75 | * @access public 76 | * @param string $str sql指令 77 | * @param array $bind 参数绑定 78 | * @return mixed 79 | */ 80 | public function query($str,$bind=array()) { 81 | $this->initConnect(false); 82 | if ( !$this->_linkID ) return false; 83 | $this->queryStr = $str; 84 | if(!empty($bind)){ 85 | $this->queryStr .= '[ '.print_r($bind,true).' ]'; 86 | } 87 | //释放前次的查询结果 88 | if ( !empty($this->PDOStatement) ) $this->free(); 89 | N('db_query',1); 90 | // 记录开始执行时间 91 | G('queryStartTime'); 92 | $this->PDOStatement = $this->_linkID->prepare($str); 93 | if(false === $this->PDOStatement) 94 | E($this->error()); 95 | // 参数绑定 96 | $this->bindPdoParam($bind); 97 | $result = $this->PDOStatement->execute(); 98 | 99 | if ( false === $result ) { 100 | $this->error(); 101 | return false; 102 | } else { 103 | return $this->getAll(); 104 | } 105 | } 106 | 107 | /** 108 | * 执行语句 109 | * @access public 110 | * @param string $str sql指令 111 | * @param array $bind 参数绑定 112 | * @return integer 113 | */ 114 | public function execute($str,$bind=array()) { 115 | $this->initConnect(true); 116 | if ( !$this->_linkID ) return false; 117 | $this->queryStr = $str; 118 | if(!empty($bind)){ 119 | $this->queryStr .= '[ '.print_r($bind,true).' ]'; 120 | } 121 | $flag = false; 122 | if($this->dbType == 'OCI') { 123 | if(preg_match("/^\s*(INSERT\s+INTO)\s+(\w+)\s+/i", $this->queryStr, $match)) { 124 | $this->table = C("DB_SEQUENCE_PREFIX").str_ireplace(C("DB_PREFIX"), "", $match[2]); 125 | $flag = (boolean)$this->query("SELECT * FROM user_sequences WHERE sequence_name='" . strtoupper($this->table) . "'"); 126 | } 127 | } 128 | //释放前次的查询结果 129 | if ( !empty($this->PDOStatement) ) $this->free(); 130 | N('db_write',1); 131 | // 记录开始执行时间 132 | G('queryStartTime'); 133 | $this->PDOStatement = $this->_linkID->prepare($str); 134 | if(false === $this->PDOStatement) { 135 | E($this->error()); 136 | } 137 | // 参数绑定 138 | $this->bindPdoParam($bind); 139 | $result = $this->PDOStatement->execute(); 140 | $this->debug(); 141 | if ( false === $result) { 142 | $this->error(); 143 | return false; 144 | } else { 145 | $this->numRows = $this->PDOStatement->rowCount(); 146 | if($flag || preg_match("/^\s*(INSERT\s+INTO|REPLACE\s+INTO)\s+/i", $str)) { 147 | $this->lastInsID = $this->getLastInsertId(); 148 | } 149 | return $this->numRows; 150 | } 151 | } 152 | 153 | /** 154 | * 参数绑定 155 | * @access protected 156 | * @return void 157 | */ 158 | protected function bindPdoParam($bind){ 159 | // 参数绑定 160 | foreach($bind as $key=>$val){ 161 | if(is_array($val)){ 162 | array_unshift($val,$key); 163 | }else{ 164 | $val = array($key,$val); 165 | } 166 | call_user_func_array(array($this->PDOStatement,'bindValue'),$val); 167 | } 168 | } 169 | 170 | /** 171 | * 启动事务 172 | * @access public 173 | * @return void 174 | */ 175 | public function startTrans() { 176 | $this->initConnect(true); 177 | if ( !$this->_linkID ) return false; 178 | //数据rollback 支持 179 | if ($this->transTimes == 0) { 180 | $this->_linkID->beginTransaction(); 181 | } 182 | $this->transTimes++; 183 | return ; 184 | } 185 | 186 | /** 187 | * 用于非自动提交状态下面的查询提交 188 | * @access public 189 | * @return boolen 190 | */ 191 | public function commit() { 192 | if ($this->transTimes > 0) { 193 | $result = $this->_linkID->commit(); 194 | $this->transTimes = 0; 195 | if(!$result){ 196 | $this->error(); 197 | return false; 198 | } 199 | } 200 | return true; 201 | } 202 | 203 | /** 204 | * 事务回滚 205 | * @access public 206 | * @return boolen 207 | */ 208 | public function rollback() { 209 | if ($this->transTimes > 0) { 210 | $result = $this->_linkID->rollback(); 211 | $this->transTimes = 0; 212 | if(!$result){ 213 | $this->error(); 214 | return false; 215 | } 216 | } 217 | return true; 218 | } 219 | 220 | /** 221 | * 获得所有的查询数据 222 | * @access private 223 | * @return array 224 | */ 225 | private function getAll() { 226 | //返回数据集 227 | $result = $this->PDOStatement->fetchAll(\PDO::FETCH_ASSOC); 228 | $this->numRows = count( $result ); 229 | $this->debug(); 230 | return $result; 231 | } 232 | 233 | /** 234 | * 取得数据表的字段信息 235 | * @access public 236 | */ 237 | public function getFields($tableName) { 238 | $this->initConnect(true); 239 | if(C('DB_DESCRIBE_TABLE_SQL')) { 240 | // 定义特殊的字段查询SQL 241 | $sql = str_replace('%table%',$tableName,C('DB_DESCRIBE_TABLE_SQL')); 242 | }else{ 243 | switch($this->dbType) { 244 | case 'MSSQL': 245 | case 'SQLSRV': 246 | $sql = "SELECT column_name as 'Name', data_type as 'Type', column_default as 'Default', is_nullable as 'Null' 247 | FROM information_schema.tables AS t 248 | JOIN information_schema.columns AS c 249 | ON t.table_catalog = c.table_catalog 250 | AND t.table_schema = c.table_schema 251 | AND t.table_name = c.table_name 252 | WHERE t.table_name = '$tableName'"; 253 | break; 254 | case 'SQLITE': 255 | $sql = 'PRAGMA table_info ('.$tableName.') '; 256 | break; 257 | case 'ORACLE': 258 | case 'OCI': 259 | $sql = "SELECT a.column_name \"Name\",data_type \"Type\",decode(nullable,'Y',0,1) notnull,data_default \"Default\",decode(a.column_name,b.column_name,1,0) \"pk\" " 260 | ."FROM user_tab_columns a,(SELECT column_name FROM user_constraints c,user_cons_columns col " 261 | ."WHERE c.constraint_name=col.constraint_name AND c.constraint_type='P' and c.table_name='".strtoupper($tableName) 262 | ."') b where table_name='".strtoupper($tableName)."' and a.column_name=b.column_name(+)"; 263 | break; 264 | case 'PGSQL': 265 | $sql = 'select fields_name as "Name",fields_type as "Type",fields_not_null as "Null",fields_key_name as "Key",fields_default as "Default",fields_default as "Extra" from table_msg('.$tableName.');'; 266 | break; 267 | case 'IBASE': 268 | break; 269 | case 'MYSQL': 270 | default: 271 | $sql = 'DESCRIBE '.$tableName;//备注: 驱动类不只针对mysql,不能加`` 272 | } 273 | } 274 | $result = $this->query($sql); 275 | $info = array(); 276 | if($result) { 277 | foreach ($result as $key => $val) { 278 | $val = array_change_key_case($val); 279 | $val['name'] = isset($val['name'])?$val['name']:""; 280 | $val['type'] = isset($val['type'])?$val['type']:""; 281 | $name = isset($val['field'])?$val['field']:$val['name']; 282 | $info[$name] = array( 283 | 'name' => $name , 284 | 'type' => $val['type'], 285 | 'notnull' => (bool)(((isset($val['null'])) && ($val['null'] === '')) || ((isset($val['notnull'])) && ($val['notnull'] === ''))), // not null is empty, null is yes 286 | 'default' => isset($val['default'])? $val['default'] :(isset($val['dflt_value'])?$val['dflt_value']:""), 287 | 'primary' => isset($val['key'])?strtolower($val['key']) == 'pri':(isset($val['pk'])?$val['pk']:false), 288 | 'autoinc' => isset($val['extra'])?strtolower($val['extra']) == 'auto_increment':(isset($val['key'])?$val['key']:false), 289 | ); 290 | } 291 | } 292 | return $info; 293 | } 294 | 295 | /** 296 | * 取得数据库的表信息 297 | * @access public 298 | */ 299 | public function getTables($dbName='') { 300 | if(C('DB_FETCH_TABLES_SQL')) { 301 | // 定义特殊的表查询SQL 302 | $sql = str_replace('%db%',$dbName,C('DB_FETCH_TABLES_SQL')); 303 | }else{ 304 | switch($this->dbType) { 305 | case 'ORACLE': 306 | case 'OCI': 307 | $sql = 'SELECT table_name FROM user_tables'; 308 | break; 309 | case 'MSSQL': 310 | case 'SQLSRV': 311 | $sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'"; 312 | break; 313 | case 'PGSQL': 314 | $sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'"; 315 | break; 316 | case 'IBASE': 317 | // 暂时不支持 318 | E(L('_NOT_SUPPORT_DB_').':IBASE'); 319 | break; 320 | case 'SQLITE': 321 | $sql = "SELECT name FROM sqlite_master WHERE type='table' " 322 | . "UNION ALL SELECT name FROM sqlite_temp_master " 323 | . "WHERE type='table' ORDER BY name"; 324 | break; 325 | case 'MYSQL': 326 | default: 327 | if(!empty($dbName)) { 328 | $sql = 'SHOW TABLES FROM '.$dbName; 329 | }else{ 330 | $sql = 'SHOW TABLES '; 331 | } 332 | } 333 | } 334 | $result = $this->query($sql); 335 | $info = array(); 336 | foreach ($result as $key => $val) { 337 | $info[$key] = current($val); 338 | } 339 | return $info; 340 | } 341 | 342 | /** 343 | * limit分析 344 | * @access protected 345 | * @param mixed $lmit 346 | * @return string 347 | */ 348 | protected function parseLimit($limit) { 349 | $limitStr = ''; 350 | if(!empty($limit)) { 351 | switch($this->dbType){ 352 | case 'PGSQL': 353 | case 'SQLITE': 354 | $limit = explode(',',$limit); 355 | if(count($limit)>1) { 356 | $limitStr .= ' LIMIT '.$limit[1].' OFFSET '.$limit[0].' '; 357 | }else{ 358 | $limitStr .= ' LIMIT '.$limit[0].' '; 359 | } 360 | break; 361 | case 'MSSQL': 362 | case 'SQLSRV': 363 | break; 364 | case 'IBASE': 365 | // 暂时不支持 366 | break; 367 | case 'ORACLE': 368 | case 'OCI': 369 | break; 370 | case 'MYSQL': 371 | default: 372 | $limitStr .= ' LIMIT '.$limit.' '; 373 | } 374 | } 375 | return $limitStr; 376 | } 377 | 378 | /** 379 | * 字段和表名处理 380 | * @access protected 381 | * @param string $key 382 | * @return string 383 | */ 384 | protected function parseKey(&$key) { 385 | if(!is_numeric($key) && $this->dbType=='MYSQL'){ 386 | $key = trim($key); 387 | if(!preg_match('/[,\'\"\*\(\)`.\s]/',$key)) { 388 | $key = '`'.$key.'`'; 389 | } 390 | return $key; 391 | }else{ 392 | return parent::parseKey($key); 393 | } 394 | 395 | } 396 | 397 | /** 398 | * 关闭数据库 399 | * @access public 400 | */ 401 | public function close() { 402 | Log::record('【数据表关闭】'); 403 | $this->_linkID = null; 404 | } 405 | 406 | /** 407 | * 数据库错误信息 408 | * 并显示当前的SQL语句 409 | * @access public 410 | * @return string 411 | */ 412 | public function error() { 413 | if($this->PDOStatement) { 414 | $error = $this->PDOStatement->errorInfo(); 415 | $this->error = $error[1].':'.$error[2]; 416 | }else{ 417 | $this->error = ''; 418 | } 419 | if('' != $this->queryStr){ 420 | $this->error .= "\n [ SQL语句 ] : ".$this->queryStr; 421 | } 422 | trace($this->error,'','ERR'); 423 | return $this->error; 424 | } 425 | 426 | /** 427 | * SQL指令安全过滤 428 | * @access public 429 | * @param string $str SQL指令 430 | * @return string 431 | */ 432 | public function escapeString($str) { 433 | switch($this->dbType) { 434 | case 'MSSQL': 435 | case 'SQLSRV': 436 | case 'MYSQL': 437 | return addslashes($str); 438 | case 'PGSQL': 439 | case 'IBASE': 440 | case 'SQLITE': 441 | case 'ORACLE': 442 | case 'OCI': 443 | return str_ireplace("'", "''", $str); 444 | } 445 | } 446 | 447 | /** 448 | * value分析 449 | * @access protected 450 | * @param mixed $value 451 | * @return string 452 | */ 453 | protected function parseValue($value) { 454 | if(is_string($value)) { 455 | $value = strpos($value,':') === 0 && in_array($value, array_keys($this->bind))? $this->escapeString($value) : '\''.$this->escapeString($value).'\''; 456 | }elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){ 457 | $value = $this->escapeString($value[1]); 458 | }elseif(is_array($value)) { 459 | $value = array_map(array($this, 'parseValue'),$value); 460 | }elseif(is_bool($value)){ 461 | $value = $value ? '1' : '0'; 462 | }elseif(is_null($value)){ 463 | $value = 'null'; 464 | } 465 | return $value; 466 | } 467 | 468 | /** 469 | * 获取最后插入id 470 | * @access public 471 | * @return integer 472 | */ 473 | public function getLastInsertId() { 474 | switch($this->dbType) { 475 | case 'PGSQL': 476 | case 'SQLITE': 477 | case 'MSSQL': 478 | case 'SQLSRV': 479 | case 'IBASE': 480 | case 'MYSQL': 481 | return $this->_linkID->lastInsertId(); 482 | case 'ORACLE': 483 | case 'OCI': 484 | $sequenceName = $this->table; 485 | $vo = $this->query("SELECT {$sequenceName}.currval currval FROM dual"); 486 | return $vo?$vo[0]["currval"]:0; 487 | } 488 | } 489 | 490 | /** 491 | * 批量插入,原类不支持批量插入,此为自定义,从其他mysqli驱动中复制过来的…… 492 | * @author madong 493 | * @access public 494 | * @param mixed $datas 数据 495 | * @param array $options 参数表达式 496 | * @param boolean $replace 是否replace 497 | * @return false | integer 498 | * 499 | */ 500 | public function insertAll($datas,$options=array(),$replace=false) { 501 | if(!is_array($datas[0])) return false; 502 | $fields = array_keys($datas[0]); 503 | array_walk($fields, array($this, 'parseKey')); 504 | $values = array(); 505 | foreach ($datas as $data){ 506 | $value = array(); 507 | foreach ($data as $key=>$val){ 508 | $val = $this->parseValue($val); 509 | if(is_scalar($val)) { // 过滤非标量数据 510 | $value[] = $val; 511 | } 512 | } 513 | $values[] = '('.implode(',', $value).')'; 514 | } 515 | $sql = ($replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES '.implode(',',$values); 516 | return $this->execute($sql); 517 | } 518 | } -------------------------------------------------------------------------------- /Core/Db/Driver/Mongo.class.php: -------------------------------------------------------------------------------- 1 | 'ne','ne'=>'ne','gt'=>'gt','egt'=>'gte','gte'=>'gte','lt'=>'lt','elt'=>'lte','lte'=>'lte','in'=>'in','not in'=>'nin','nin'=>'nin'); 15 | 16 | /** 17 | * 架构函数 读取数据库配置信息 18 | * @access public 19 | * @param array $config 数据库配置数组 20 | */ 21 | public function __construct($config=''){ 22 | if ( !class_exists('mongoClient') ) { 23 | E(L('_NOT_SUPPERT_').':mongoClient'); 24 | } 25 | if(!empty($config)) { 26 | $this->config = $config; 27 | if(empty($this->config['params'])) { 28 | $this->config['params'] = array(); 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * 连接数据库方法 35 | * @access public 36 | */ 37 | public function connect($config='',$linkNum=0) { 38 | if ( !isset($this->linkID[$linkNum]) ) { 39 | if(empty($config)) $config = $this->config; 40 | $host = 'mongodb://'.($config['username']?"{$config['username']}":'').($config['password']?":{$config['password']}@":'').$config['hostname'].($config['hostport']?":{$config['hostport']}":'').'/'.($config['database']?"{$config['database']}":''); 41 | try{ 42 | $this->linkID[$linkNum] = new \mongoClient( $host,$config['params']); 43 | }catch (\MongoConnectionException $e){ 44 | E($e->getmessage()); 45 | } 46 | // 标记连接成功 47 | $this->connected = true; 48 | // 注销数据库连接配置信息 49 | if(1 != C('DB_DEPLOY_TYPE')) unset($this->config); 50 | } 51 | return $this->linkID[$linkNum]; 52 | } 53 | 54 | /** 55 | * 切换当前操作的Db和Collection 56 | * @access public 57 | * @param string $collection collection 58 | * @param string $db db 59 | * @param boolean $master 是否主服务器 60 | * @return void 61 | */ 62 | public function switchCollection($collection,$db='',$master=true){ 63 | // 当前没有连接 则首先进行数据库连接 64 | if ( !$this->_linkID ) $this->initConnect($master); 65 | try{ 66 | if(!empty($db)) { // 传人Db则切换数据库 67 | // 当前MongoDb对象 68 | $this->_dbName = $db; 69 | $this->_mongo = $this->_linkID->selectDb($db); 70 | } 71 | // 当前MongoCollection对象 72 | if(C('DB_SQL_LOG')) { 73 | $this->queryStr = $this->_dbName.'.getCollection('.$collection.')'; 74 | } 75 | if($this->_collectionName != $collection) { 76 | N('db_read',1); 77 | // 记录开始执行时间 78 | G('queryStartTime'); 79 | $this->_collection = $this->_mongo->selectCollection($collection); 80 | $this->debug(); 81 | $this->_collectionName = $collection; // 记录当前Collection名称 82 | } 83 | }catch (\MongoException $e){ 84 | E($e->getMessage()); 85 | } 86 | } 87 | 88 | /** 89 | * 释放查询结果 90 | * @access public 91 | */ 92 | public function free() { 93 | $this->_cursor = null; 94 | } 95 | 96 | /** 97 | * 执行命令 98 | * @access public 99 | * @param array $command 指令 100 | * @return array 101 | */ 102 | public function command($command=array()) { 103 | N('db_write',1); 104 | $this->queryStr = 'command:'.json_encode($command); 105 | // 记录开始执行时间 106 | G('queryStartTime'); 107 | $result = $this->_mongo->command($command); 108 | $this->debug(); 109 | if(!$result['ok']) { 110 | E($result['errmsg']); 111 | } 112 | return $result; 113 | } 114 | 115 | /** 116 | * 执行语句 117 | * @access public 118 | * @param string $code sql指令 119 | * @param array $args 参数 120 | * @return mixed 121 | */ 122 | public function execute($code,$args=array()) { 123 | N('db_write',1); 124 | $this->queryStr = 'execute:'.$code; 125 | // 记录开始执行时间 126 | G('queryStartTime'); 127 | $result = $this->_mongo->execute($code,$args); 128 | $this->debug(); 129 | if($result['ok']) { 130 | return $result['retval']; 131 | }else{ 132 | E($result['errmsg']); 133 | } 134 | } 135 | 136 | /** 137 | * 关闭数据库 138 | * @access public 139 | */ 140 | public function close() { 141 | if($this->_linkID) { 142 | $this->_linkID->close(); 143 | $this->_linkID = null; 144 | $this->_mongo = null; 145 | $this->_collection = null; 146 | $this->_cursor = null; 147 | } 148 | } 149 | 150 | /** 151 | * 数据库错误信息 152 | * @access public 153 | * @return string 154 | */ 155 | public function error() { 156 | $this->error = $this->_mongo->lastError(); 157 | trace($this->error,'','ERR'); 158 | return $this->error; 159 | } 160 | 161 | /** 162 | * 插入记录 163 | * @access public 164 | * @param mixed $data 数据 165 | * @param array $options 参数表达式 166 | * @param boolean $replace 是否replace 167 | * @return false | integer 168 | */ 169 | public function insert($data,$options=array(),$replace=false) { 170 | if(isset($options['table'])) { 171 | $this->switchCollection($options['table']); 172 | } 173 | $this->model = $options['model']; 174 | N('db_write',1); 175 | if(C('DB_SQL_LOG')) { 176 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.insert('; 177 | $this->queryStr .= $data?json_encode($data):'{}'; 178 | $this->queryStr .= ')'; 179 | } 180 | try{ 181 | // 记录开始执行时间 182 | G('queryStartTime'); 183 | $result = $replace? $this->_collection->save($data): $this->_collection->insert($data); 184 | $this->debug(); 185 | if($result) { 186 | $_id = $data['_id']; 187 | if(is_object($_id)) { 188 | $_id = $_id->__toString(); 189 | } 190 | $this->lastInsID = $_id; 191 | } 192 | return $result; 193 | } catch (\MongoCursorException $e) { 194 | E($e->getMessage()); 195 | } 196 | } 197 | 198 | /** 199 | * 插入多条记录 200 | * @access public 201 | * @param array $dataList 数据 202 | * @param array $options 参数表达式 203 | * @return bool 204 | */ 205 | public function insertAll($dataList,$options=array()) { 206 | if(isset($options['table'])) { 207 | $this->switchCollection($options['table']); 208 | } 209 | $this->model = $options['model']; 210 | N('db_write',1); 211 | try{ 212 | // 记录开始执行时间 213 | G('queryStartTime'); 214 | $result = $this->_collection->batchInsert($dataList); 215 | $this->debug(); 216 | return $result; 217 | } catch (\MongoCursorException $e) { 218 | E($e->getMessage()); 219 | } 220 | } 221 | 222 | /** 223 | * 生成下一条记录ID 用于自增非MongoId主键 224 | * @access public 225 | * @param string $pk 主键名 226 | * @return integer 227 | */ 228 | public function mongo_next_id($pk) { 229 | N('db_read',1); 230 | if(C('DB_SQL_LOG')) { 231 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.find({},{'.$pk.':1}).sort({'.$pk.':-1}).limit(1)'; 232 | } 233 | try{ 234 | // 记录开始执行时间 235 | G('queryStartTime'); 236 | $result = $this->_collection->find(array(),array($pk=>1))->sort(array($pk=>-1))->limit(1); 237 | $this->debug(); 238 | } catch (\MongoCursorException $e) { 239 | E($e->getMessage()); 240 | } 241 | $data = $result->getNext(); 242 | return isset($data[$pk])?$data[$pk]+1:1; 243 | } 244 | 245 | /** 246 | * 更新记录 247 | * @access public 248 | * @param mixed $data 数据 249 | * @param array $options 表达式 250 | * @return bool 251 | */ 252 | public function update($data,$options) { 253 | if(isset($options['table'])) { 254 | $this->switchCollection($options['table']); 255 | } 256 | $this->model = $options['model']; 257 | N('db_write',1); 258 | $query = $this->parseWhere($options['where']); 259 | $set = $this->parseSet($data); 260 | if(C('DB_SQL_LOG')) { 261 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.update('; 262 | $this->queryStr .= $query?json_encode($query):'{}'; 263 | $this->queryStr .= ','.json_encode($set).')'; 264 | } 265 | try{ 266 | // 记录开始执行时间 267 | G('queryStartTime'); 268 | if(isset($options['limit']) && $options['limit'] == 1) { 269 | $multiple = array("multiple" => false); 270 | }else{ 271 | $multiple = array("multiple" => true); 272 | } 273 | $result = $this->_collection->update($query,$set,$multiple); 274 | $this->debug(); 275 | return $result; 276 | } catch (\MongoCursorException $e) { 277 | E($e->getMessage()); 278 | } 279 | } 280 | 281 | /** 282 | * 删除记录 283 | * @access public 284 | * @param array $options 表达式 285 | * @return false | integer 286 | */ 287 | public function delete($options=array()) { 288 | if(isset($options['table'])) { 289 | $this->switchCollection($options['table']); 290 | } 291 | $query = $this->parseWhere($options['where']); 292 | $this->model = $options['model']; 293 | N('db_write',1); 294 | if(C('DB_SQL_LOG')) { 295 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.remove('.json_encode($query).')'; 296 | } 297 | try{ 298 | // 记录开始执行时间 299 | G('queryStartTime'); 300 | $result = $this->_collection->remove($query); 301 | $this->debug(); 302 | return $result; 303 | } catch (\MongoCursorException $e) { 304 | E($e->getMessage()); 305 | } 306 | } 307 | 308 | /** 309 | * 清空记录 310 | * @access public 311 | * @param array $options 表达式 312 | * @return false | integer 313 | */ 314 | public function clear($options=array()){ 315 | if(isset($options['table'])) { 316 | $this->switchCollection($options['table']); 317 | } 318 | $this->model = $options['model']; 319 | N('db_write',1); 320 | if(C('DB_SQL_LOG')) { 321 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.remove({})'; 322 | } 323 | try{ 324 | // 记录开始执行时间 325 | G('queryStartTime'); 326 | $result = $this->_collection->drop(); 327 | $this->debug(); 328 | return $result; 329 | } catch (\MongoCursorException $e) { 330 | E($e->getMessage()); 331 | } 332 | } 333 | 334 | /** 335 | * 查找记录 336 | * @access public 337 | * @param array $options 表达式 338 | * @return iterator 339 | */ 340 | public function select($options=array()) { 341 | if(isset($options['table'])) { 342 | $this->switchCollection($options['table'],'',false); 343 | } 344 | $cache = isset($options['cache'])?$options['cache']:false; 345 | if($cache) { // 查询缓存检测 346 | $key = is_string($cache['key'])?$cache['key']:md5(serialize($options)); 347 | $value = S($key,'','',$cache['type']); 348 | if(false !== $value) { 349 | return $value; 350 | } 351 | } 352 | $this->model = $options['model']; 353 | N('db_query',1); 354 | $query = $this->parseWhere($options['where']); 355 | $field = $this->parseField($options['field']); 356 | try{ 357 | if(C('DB_SQL_LOG')) { 358 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.find('; 359 | $this->queryStr .= $query? json_encode($query):'{}'; 360 | $this->queryStr .= $field? ','.json_encode($field):''; 361 | $this->queryStr .= ')'; 362 | } 363 | // 记录开始执行时间 364 | G('queryStartTime'); 365 | $_cursor = $this->_collection->find($query,$field); 366 | if($options['order']) { 367 | $order = $this->parseOrder($options['order']); 368 | if(C('DB_SQL_LOG')) { 369 | $this->queryStr .= '.sort('.json_encode($order).')'; 370 | } 371 | $_cursor = $_cursor->sort($order); 372 | } 373 | if(isset($options['page'])) { // 根据页数计算limit 374 | if(strpos($options['page'],',')) { 375 | list($page,$length) = explode(',',$options['page']); 376 | }else{ 377 | $page = $options['page']; 378 | } 379 | $page = $page?$page:1; 380 | $length = isset($length)?$length:(is_numeric($options['limit'])?$options['limit']:20); 381 | $offset = $length*((int)$page-1); 382 | $options['limit'] = $offset.','.$length; 383 | } 384 | if(isset($options['limit'])) { 385 | list($offset,$length) = $this->parseLimit($options['limit']); 386 | if(!empty($offset)) { 387 | if(C('DB_SQL_LOG')) { 388 | $this->queryStr .= '.skip('.intval($offset).')'; 389 | } 390 | $_cursor = $_cursor->skip(intval($offset)); 391 | } 392 | if(C('DB_SQL_LOG')) { 393 | $this->queryStr .= '.limit('.intval($length).')'; 394 | } 395 | $_cursor = $_cursor->limit(intval($length)); 396 | } 397 | $this->debug(); 398 | $this->_cursor = $_cursor; 399 | $resultSet = iterator_to_array($_cursor); 400 | if($cache && $resultSet ) { // 查询缓存写入 401 | S($key,$resultSet,$cache['expire'],$cache['type']); 402 | } 403 | return $resultSet; 404 | } catch (\MongoCursorException $e) { 405 | E($e->getMessage()); 406 | } 407 | } 408 | 409 | /** 410 | * 查找某个记录 411 | * @access public 412 | * @param array $options 表达式 413 | * @return array 414 | */ 415 | public function find($options=array()){ 416 | if(isset($options['table'])) { 417 | $this->switchCollection($options['table'],'',false); 418 | } 419 | $cache = isset($options['cache'])?$options['cache']:false; 420 | if($cache) { // 查询缓存检测 421 | $key = is_string($cache['key'])?$cache['key']:md5(serialize($options)); 422 | $value = S($key,'','',$cache['type']); 423 | if(false !== $value) { 424 | return $value; 425 | } 426 | } 427 | $this->model = $options['model']; 428 | N('db_query',1); 429 | $query = $this->parseWhere($options['where']); 430 | $fields = $this->parseField($options['field']); 431 | if(C('DB_SQL_LOG')) { 432 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.findOne('; 433 | $this->queryStr .= $query?json_encode($query):'{}'; 434 | $this->queryStr .= $fields?','.json_encode($fields):''; 435 | $this->queryStr .= ')'; 436 | } 437 | try{ 438 | // 记录开始执行时间 439 | G('queryStartTime'); 440 | $result = $this->_collection->findOne($query,$fields); 441 | $this->debug(); 442 | if($cache && $result ) { // 查询缓存写入 443 | S($key,$result,$cache['expire'],$cache['type']); 444 | } 445 | return $result; 446 | } catch (\MongoCursorException $e) { 447 | E($e->getMessage()); 448 | } 449 | } 450 | 451 | /** 452 | * 统计记录数 453 | * @access public 454 | * @param array $options 表达式 455 | * @return iterator 456 | */ 457 | public function count($options=array()){ 458 | if(isset($options['table'])) { 459 | $this->switchCollection($options['table'],'',false); 460 | } 461 | $this->model = $options['model']; 462 | N('db_query',1); 463 | $query = $this->parseWhere($options['where']); 464 | if(C('DB_SQL_LOG')) { 465 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName; 466 | $this->queryStr .= $query?'.find('.json_encode($query).')':''; 467 | $this->queryStr .= '.count()'; 468 | } 469 | try{ 470 | // 记录开始执行时间 471 | G('queryStartTime'); 472 | $count = $this->_collection->count($query); 473 | $this->debug(); 474 | return $count; 475 | } catch (\MongoCursorException $e) { 476 | E($e->getMessage()); 477 | } 478 | } 479 | 480 | public function group($keys,$initial,$reduce,$options=array()){ 481 | $this->_collection->group($keys,$initial,$reduce,$options); 482 | } 483 | 484 | /** 485 | * 取得数据表的字段信息 486 | * @access public 487 | * @return array 488 | */ 489 | public function getFields($collection=''){ 490 | if(!empty($collection) && $collection != $this->_collectionName) { 491 | $this->switchCollection($collection,'',false); 492 | } 493 | N('db_query',1); 494 | if(C('DB_SQL_LOG')) { 495 | $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.findOne()'; 496 | } 497 | try{ 498 | // 记录开始执行时间 499 | G('queryStartTime'); 500 | $result = $this->_collection->findOne(); 501 | $this->debug(); 502 | } catch (\MongoCursorException $e) { 503 | E($e->getMessage()); 504 | } 505 | if($result) { // 存在数据则分析字段 506 | $info = array(); 507 | foreach ($result as $key=>$val){ 508 | $info[$key] = array( 509 | 'name'=>$key, 510 | 'type'=>getType($val), 511 | ); 512 | } 513 | return $info; 514 | } 515 | // 暂时没有数据 返回false 516 | return false; 517 | } 518 | 519 | /** 520 | * 取得当前数据库的collection信息 521 | * @access public 522 | */ 523 | public function getTables(){ 524 | if(C('DB_SQL_LOG')) { 525 | $this->queryStr = $this->_dbName.'.getCollenctionNames()'; 526 | } 527 | N('db_query',1); 528 | // 记录开始执行时间 529 | G('queryStartTime'); 530 | $list = $this->_mongo->listCollections(); 531 | $this->debug(); 532 | $info = array(); 533 | foreach ($list as $collection){ 534 | $info[] = $collection->getName(); 535 | } 536 | return $info; 537 | } 538 | 539 | /** 540 | * set分析 541 | * @access protected 542 | * @param array $data 543 | * @return string 544 | */ 545 | protected function parseSet($data) { 546 | $result = array(); 547 | foreach ($data as $key=>$val){ 548 | if(is_array($val)) { 549 | switch($val[0]) { 550 | case 'inc': 551 | $result['$inc'][$key] = (int)$val[1]; 552 | break; 553 | case 'set': 554 | case 'unset': 555 | case 'push': 556 | case 'pushall': 557 | case 'addtoset': 558 | case 'pop': 559 | case 'pull': 560 | case 'pullall': 561 | $result['$'.$val[0]][$key] = $val[1]; 562 | break; 563 | default: 564 | $result['$set'][$key] = $val; 565 | } 566 | }else{ 567 | $result['$set'][$key] = $val; 568 | } 569 | } 570 | return $result; 571 | } 572 | 573 | /** 574 | * order分析 575 | * @access protected 576 | * @param mixed $order 577 | * @return array 578 | */ 579 | protected function parseOrder($order) { 580 | if(is_string($order)) { 581 | $array = explode(',',$order); 582 | $order = array(); 583 | foreach ($array as $key=>$val){ 584 | $arr = explode(' ',trim($val)); 585 | if(isset($arr[1])) { 586 | $arr[1] = $arr[1]=='asc'?1:-1; 587 | }else{ 588 | $arr[1] = 1; 589 | } 590 | $order[$arr[0]] = $arr[1]; 591 | } 592 | } 593 | return $order; 594 | } 595 | 596 | /** 597 | * limit分析 598 | * @access protected 599 | * @param mixed $limit 600 | * @return array 601 | */ 602 | protected function parseLimit($limit) { 603 | if(strpos($limit,',')) { 604 | $array = explode(',',$limit); 605 | }else{ 606 | $array = array(0,$limit); 607 | } 608 | return $array; 609 | } 610 | 611 | /** 612 | * field分析 613 | * @access protected 614 | * @param mixed $fields 615 | * @return array 616 | */ 617 | public function parseField($fields){ 618 | if(empty($fields)) { 619 | $fields = array(); 620 | } 621 | if(is_string($fields)) { 622 | $fields = explode(',',$fields); 623 | } 624 | return $fields; 625 | } 626 | 627 | /** 628 | * where分析 629 | * @access protected 630 | * @param mixed $where 631 | * @return array 632 | */ 633 | public function parseWhere($where){ 634 | $query = array(); 635 | foreach ($where as $key=>$val){ 636 | if('_id' != $key && 0===strpos($key,'_')) { 637 | // 解析特殊条件表达式 638 | $query = $this->parseThinkWhere($key,$val); 639 | }else{ 640 | // 查询字段的安全过滤 641 | if(!preg_match('/^[A-Z_\|\&\-.a-z0-9]+$/',trim($key))){ 642 | E(L('_ERROR_QUERY_').':'.$key); 643 | } 644 | $key = trim($key); 645 | if(strpos($key,'|')) { 646 | $array = explode('|',$key); 647 | $str = array(); 648 | foreach ($array as $k){ 649 | $str[] = $this->parseWhereItem($k,$val); 650 | } 651 | $query['$or'] = $str; 652 | }elseif(strpos($key,'&')){ 653 | $array = explode('&',$key); 654 | $str = array(); 655 | foreach ($array as $k){ 656 | $str[] = $this->parseWhereItem($k,$val); 657 | } 658 | $query = array_merge($query,$str); 659 | }else{ 660 | $str = $this->parseWhereItem($key,$val); 661 | $query = array_merge($query,$str); 662 | } 663 | } 664 | } 665 | return $query; 666 | } 667 | 668 | /** 669 | * 特殊条件分析 670 | * @access protected 671 | * @param string $key 672 | * @param mixed $val 673 | * @return string 674 | */ 675 | protected function parseThinkWhere($key,$val) { 676 | $query = array(); 677 | switch($key) { 678 | case '_query': // 字符串模式查询条件 679 | parse_str($val,$query); 680 | if(isset($query['_logic']) && strtolower($query['_logic']) == 'or' ) { 681 | unset($query['_logic']); 682 | $query['$or'] = $query; 683 | } 684 | break; 685 | case '_string':// MongoCode查询 686 | $query['$where'] = new \MongoCode($val); 687 | break; 688 | } 689 | return $query; 690 | } 691 | 692 | /** 693 | * where子单元分析 694 | * @access protected 695 | * @param string $key 696 | * @param mixed $val 697 | * @return array 698 | */ 699 | protected function parseWhereItem($key,$val) { 700 | $query = array(); 701 | if(is_array($val)) { 702 | if(is_string($val[0])) { 703 | $con = strtolower($val[0]); 704 | if(in_array($con,array('neq','ne','gt','egt','gte','lt','lte','elt'))) { // 比较运算 705 | $k = '$'.$this->comparison[$con]; 706 | $query[$key] = array($k=>$val[1]); 707 | }elseif('like'== $con){ // 模糊查询 采用正则方式 708 | $query[$key] = new \MongoRegex("/".$val[1]."/"); 709 | }elseif('mod'==$con){ // mod 查询 710 | $query[$key] = array('$mod'=>$val[1]); 711 | }elseif('regex'==$con){ // 正则查询 712 | $query[$key] = new \MongoRegex($val[1]); 713 | }elseif(in_array($con,array('in','nin','not in'))){ // IN NIN 运算 714 | $data = is_string($val[1])? explode(',',$val[1]):$val[1]; 715 | $k = '$'.$this->comparison[$con]; 716 | $query[$key] = array($k=>$data); 717 | }elseif('all'==$con){ // 满足所有指定条件 718 | $data = is_string($val[1])? explode(',',$val[1]):$val[1]; 719 | $query[$key] = array('$all'=>$data); 720 | }elseif('between'==$con){ // BETWEEN运算 721 | $data = is_string($val[1])? explode(',',$val[1]):$val[1]; 722 | $query[$key] = array('$gte'=>$data[0],'$lte'=>$data[1]); 723 | }elseif('not between'==$con){ 724 | $data = is_string($val[1])? explode(',',$val[1]):$val[1]; 725 | $query[$key] = array('$lt'=>$data[0],'$gt'=>$data[1]); 726 | }elseif('exp'==$con){ // 表达式查询 727 | $query['$where'] = new \MongoCode($val[1]); 728 | }elseif('exists'==$con){ // 字段是否存在 729 | $query[$key] =array('$exists'=>(bool)$val[1]); 730 | }elseif('size'==$con){ // 限制属性大小 731 | $query[$key] =array('$size'=>intval($val[1])); 732 | }elseif('type'==$con){ // 限制字段类型 1 浮点型 2 字符型 3 对象或者MongoDBRef 5 MongoBinData 7 MongoId 8 布尔型 9 MongoDate 10 NULL 15 MongoCode 16 32位整型 17 MongoTimestamp 18 MongoInt64 如果是数组的话判断元素的类型 733 | $query[$key] =array('$type'=>intval($val[1])); 734 | }else{ 735 | $query[$key] = $val; 736 | } 737 | return $query; 738 | } 739 | } 740 | $query[$key] = $val; 741 | return $query; 742 | } 743 | } -------------------------------------------------------------------------------- /Core/Db.class.php: -------------------------------------------------------------------------------- 1 | '=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN'); 40 | // 查询表达式 41 | protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%'; 42 | // 参数绑定 43 | protected $bind = array(); 44 | 45 | /** 46 | * 取得数据库类实例 47 | * @static 48 | * @access public 49 | * @return mixed 返回数据库驱动类 50 | */ 51 | public static function getInstance($db_config='') { 52 | static $_instance = array(); 53 | $guid = to_guid_string($db_config); 54 | if(!isset($_instance[$guid])){ 55 | $obj = new Db(); 56 | $_instance[$guid] = $obj->factory($db_config); 57 | } 58 | return $_instance[$guid]; 59 | } 60 | 61 | /** 62 | * 加载数据库 支持配置文件或者 DSN 63 | * @access public 64 | * @param mixed $db_config 数据库配置信息 65 | * @return string 66 | */ 67 | public function factory($db_config='') { 68 | // 读取数据库配置 69 | $db_config = $this->parseConfig($db_config); 70 | if(empty($db_config['dbms'])) 71 | E(L('_NO_DB_CONFIG_')); 72 | // 数据库类型 73 | if(strpos($db_config['dbms'],'\\')){ 74 | $class = $db_config['dbms']; 75 | }else{ 76 | $dbType = ucwords(strtolower($db_config['dbms'])); 77 | $class = 'Core\\Db\\Driver\\'. $dbType; 78 | } 79 | // 检查驱动类 80 | if(class_exists($class)) { 81 | $db = new $class($db_config); 82 | }else { 83 | // 类没有定义 84 | E(L('_NO_DB_DRIVER_').': ' . $class); 85 | } 86 | return $db; 87 | } 88 | 89 | /** 90 | * 根据DSN获取数据库类型 返回大写 91 | * @access protected 92 | * @param string $dsn dsn字符串 93 | * @return string 94 | */ 95 | protected function _getDsnType($dsn) { 96 | $match = explode(':',$dsn); 97 | $dbType = strtoupper(trim($match[0])); 98 | return $dbType; 99 | } 100 | 101 | /** 102 | * 分析数据库配置信息,支持数组和DSN 103 | * @access private 104 | * @param mixed $db_config 数据库配置信息 105 | * @return string 106 | */ 107 | private function parseConfig($db_config='') { 108 | if ( !empty($db_config) && is_string($db_config)) { 109 | // 如果DSN字符串则进行解析 110 | $db_config = $this->parseDSN($db_config); 111 | }elseif(is_array($db_config)) { // 数组配置 112 | $db_config = array_change_key_case($db_config); 113 | $db_config = array( 114 | 'dbms' => $db_config['db_type'], 115 | 'username' => $db_config['db_user'], 116 | 'password' => $db_config['db_pwd'], 117 | 'hostname' => $db_config['db_host'], 118 | 'hostport' => $db_config['db_port'], 119 | 'database' => $db_config['db_name'], 120 | 'dsn' => isset($db_config['db_dsn'])?$db_config['db_dsn']:'', 121 | 'params' => isset($db_config['db_params'])?$db_config['db_params']:array(), 122 | 'charset' => isset($db_config['db_charset'])?$db_config['db_charset']:'utf8', 123 | ); 124 | }elseif(empty($db_config)) { 125 | // 如果配置为空,读取配置文件设置 126 | if( C('DB_DSN') && 'pdo' != strtolower(C('DB_TYPE')) ) { // 如果设置了DB_DSN 则优先 127 | $db_config = $this->parseDSN(C('DB_DSN')); 128 | }else{ 129 | 130 | $db_config = array ( 131 | 'dbms' => C('DB_TYPE'), 132 | 'username' => C('DB_USER'), 133 | 'password' => C('DB_PWD'), 134 | 'hostname' => C('DB_HOST'), 135 | 'hostport' => C('DB_PORT'), 136 | 'database' => C('DB_NAME'), 137 | 'dsn' => C('DB_DSN'), 138 | 'params' => C('DB_PARAMS'), 139 | 'charset' => C('DB_CHARSET'), 140 | ); 141 | } 142 | } 143 | 144 | return $db_config; 145 | } 146 | 147 | /** 148 | * 初始化数据库连接 149 | * @access protected 150 | * @param boolean $master 主服务器 151 | * @return void 152 | */ 153 | protected function initConnect($master=true) { 154 | if(1 == C('DB_DEPLOY_TYPE')) 155 | // 采用分布式数据库 156 | $this->_linkID = $this->multiConnect($master); 157 | else 158 | // 默认单数据库 159 | if ( !$this->connected ) $this->_linkID = $this->connect(); 160 | } 161 | 162 | /** 163 | * 连接分布式服务器 164 | * @access protected 165 | * @param boolean $master 主服务器 166 | * @return void 167 | */ 168 | protected function multiConnect($master=false) { 169 | foreach ($this->config as $key=>$val){ 170 | $_config[$key] = explode(',',$val); 171 | } 172 | // 数据库读写是否分离 173 | if(C('DB_RW_SEPARATE')){ 174 | // 主从式采用读写分离 175 | if($master) 176 | // 主服务器写入 177 | $r = floor(mt_rand(0,C('DB_MASTER_NUM')-1)); 178 | else{ 179 | if(is_numeric(C('DB_SLAVE_NO'))) {// 指定服务器读 180 | $r = C('DB_SLAVE_NO'); 181 | }else{ 182 | // 读操作连接从服务器 183 | $r = floor(mt_rand(C('DB_MASTER_NUM'),count($_config['hostname'])-1)); // 每次随机连接的数据库 184 | } 185 | } 186 | }else{ 187 | // 读写操作不区分服务器 188 | $r = floor(mt_rand(0,count($_config['hostname'])-1)); // 每次随机连接的数据库 189 | } 190 | $db_config = array( 191 | 'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0], 192 | 'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0], 193 | 'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0], 194 | 'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0], 195 | 'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0], 196 | 'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0], 197 | 'params' => isset($_config['params'][$r])?$_config['params'][$r]:$_config['params'][0], 198 | 'charset' => isset($_config['charset'][$r])?$_config['charset'][$r]:$_config['charset'][0], 199 | ); 200 | return $this->connect($db_config,$r); 201 | } 202 | 203 | /** 204 | * DSN解析 205 | * 格式: mysql://username:passwd@localhost:3306/DbName#charset 206 | * @static 207 | * @access public 208 | * @param string $dsnStr 209 | * @return array 210 | */ 211 | public function parseDSN($dsnStr) { 212 | if( empty($dsnStr) ){return false;} 213 | $info = parse_url($dsnStr); 214 | if($info['scheme']){ 215 | $dsn = array( 216 | 'dbms' => $info['scheme'], 217 | 'username' => isset($info['user']) ? $info['user'] : '', 218 | 'password' => isset($info['pass']) ? $info['pass'] : '', 219 | 'hostname' => isset($info['host']) ? $info['host'] : '', 220 | 'hostport' => isset($info['port']) ? $info['port'] : '', 221 | 'database' => isset($info['path']) ? substr($info['path'],1) : '', 222 | 'charset' => isset($info['fragment'])?$info['fragment']:'utf8', 223 | ); 224 | }else { 225 | preg_match('/^(.*?)\:\/\/(.*?)\:(.*?)\@(.*?)\:([0-9]{1, 6})\/(.*?)$/',trim($dsnStr),$matches); 226 | $dsn = array ( 227 | 'dbms' => $matches[1], 228 | 'username' => $matches[2], 229 | 'password' => $matches[3], 230 | 'hostname' => $matches[4], 231 | 'hostport' => $matches[5], 232 | 'database' => $matches[6] 233 | ); 234 | } 235 | $dsn['dsn'] = ''; // 兼容配置信息数组 236 | return $dsn; 237 | } 238 | 239 | /** 240 | * 数据库调试 记录当前SQL 241 | * @access protected 242 | */ 243 | protected function debug() { 244 | $this->modelSql[$this->model] = $this->queryStr; 245 | $this->model = ''; 246 | // 记录操作结束时间 247 | if (C('DB_SQL_LOG')) { 248 | G('queryEndTime'); 249 | trace($this->queryStr.' [ sql 执行时间:'.G('queryStartTime','queryEndTime',6).'s ]','','SQL'); 250 | } 251 | } 252 | 253 | /** 254 | * 设置锁机制 255 | * @access protected 256 | * @return string 257 | */ 258 | protected function parseLock($lock=false) { 259 | if(!$lock) return ''; 260 | if('ORACLE' == $this->dbType) { 261 | return ' FOR UPDATE NOWAIT '; 262 | } 263 | return ' FOR UPDATE '; 264 | } 265 | 266 | /** 267 | * set分析 268 | * @access protected 269 | * @param array $data 270 | * @return string 271 | */ 272 | protected function parseSet($data) { 273 | foreach ($data as $key=>$val){ 274 | if(is_array($val) && 'exp' == $val[0]){ 275 | $set[] = $this->parseKey($key).'='.$val[1]; 276 | }elseif(is_scalar($val) || is_null($val)) { // 过滤非标量数据 277 | if(C('DB_BIND_PARAM') && 0 !== strpos($val,':')){ 278 | $name = md5($key); 279 | $set[] = $this->parseKey($key).'=:'.$name; 280 | $this->bindParam($name,$val); 281 | }else{ 282 | $set[] = $this->parseKey($key).'='.$this->parseValue($val); 283 | } 284 | } 285 | } 286 | return ' SET '.implode(',',$set); 287 | } 288 | 289 | /** 290 | * 参数绑定 291 | * @access protected 292 | * @param string $name 绑定参数名 293 | * @param mixed $value 绑定值 294 | * @return void 295 | */ 296 | protected function bindParam($name,$value){ 297 | $this->bind[':'.$name] = $value; 298 | } 299 | 300 | /** 301 | * 参数绑定分析 302 | * @access protected 303 | * @param array $bind 304 | * @return array 305 | */ 306 | protected function parseBind($bind){ 307 | $bind = array_merge($this->bind,$bind); 308 | $this->bind = array(); 309 | return $bind; 310 | } 311 | 312 | /** 313 | * 字段名分析 314 | * @access protected 315 | * @param string $key 316 | * @return string 317 | */ 318 | protected function parseKey(&$key) { 319 | return $key; 320 | } 321 | 322 | /** 323 | * value分析 324 | * @access protected 325 | * @param mixed $value 326 | * @return string 327 | */ 328 | protected function parseValue($value) { 329 | if(is_string($value)) { 330 | $value = '\''.$this->escapeString($value).'\''; 331 | }elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){ 332 | $value = $this->escapeString($value[1]); 333 | }elseif(is_array($value)) { 334 | $value = array_map(array($this, 'parseValue'),$value); 335 | }elseif(is_bool($value)){ 336 | $value = $value ? '1' : '0'; 337 | }elseif(is_null($value)){ 338 | $value = 'null'; 339 | } 340 | return $value; 341 | } 342 | 343 | /** 344 | * field分析 345 | * @access protected 346 | * @param mixed $fields 347 | * @return string 348 | */ 349 | protected function parseField($fields) { 350 | if(is_string($fields) && strpos($fields,',')) { 351 | $fields = explode(',',$fields); 352 | } 353 | if(is_array($fields)) { 354 | // 完善数组方式传字段名的支持 355 | // 支持 'field1'=>'field2' 这样的字段别名定义 356 | $array = array(); 357 | foreach ($fields as $key=>$field){ 358 | if(!is_numeric($key)) 359 | $array[] = $this->parseKey($key).' AS '.$this->parseKey($field); 360 | else 361 | $array[] = $this->parseKey($field); 362 | } 363 | $fieldsStr = implode(',', $array); 364 | }elseif(is_string($fields) && !empty($fields)) { 365 | $fieldsStr = $this->parseKey($fields); 366 | }else{ 367 | $fieldsStr = '*'; 368 | } 369 | //TODO 如果是查询全部字段,并且是join的方式,那么就把要查的表加个别名,以免字段被覆盖 370 | return $fieldsStr; 371 | } 372 | 373 | /** 374 | * table分析 375 | * @access protected 376 | * @param mixed $table 377 | * @return string 378 | */ 379 | protected function parseTable($tables) { 380 | if(is_array($tables)) {// 支持别名定义 381 | $array = array(); 382 | foreach ($tables as $table=>$alias){ 383 | if(!is_numeric($table)) 384 | $array[] = $this->parseKey($table).' '.$this->parseKey($alias); 385 | else 386 | $array[] = $this->parseKey($table); 387 | } 388 | $tables = $array; 389 | }elseif(is_string($tables)){ 390 | $tables = explode(',',$tables); 391 | array_walk($tables, array(&$this, 'parseKey')); 392 | } 393 | $tables = implode(',',$tables); 394 | return $tables; 395 | } 396 | 397 | /** 398 | * where分析 399 | * @access protected 400 | * @param mixed $where 401 | * @return string 402 | */ 403 | protected function parseWhere($where) { 404 | $whereStr = ''; 405 | if(is_string($where)) { 406 | // 直接使用字符串条件 407 | $whereStr = $where; 408 | }else{ // 使用数组表达式 409 | $operate = isset($where['_logic'])?strtoupper($where['_logic']):''; 410 | if(in_array($operate,array('AND','OR','XOR'))){ 411 | // 定义逻辑运算规则 例如 OR XOR AND NOT 412 | $operate = ' '.$operate.' '; 413 | unset($where['_logic']); 414 | }else{ 415 | // 默认进行 AND 运算 416 | $operate = ' AND '; 417 | } 418 | foreach ($where as $key=>$val){ 419 | if(is_numeric($key)){ 420 | $key = '_complex'; 421 | } 422 | if(0===strpos($key,'_')) { 423 | // 解析特殊条件表达式 424 | $whereStr .= $this->parseThinkWhere($key,$val); 425 | }else{ 426 | // 查询字段的安全过滤 427 | if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){ 428 | E(L('_EXPRESS_ERROR_').':'.$key); 429 | } 430 | // 多条件支持 431 | $multi = is_array($val) && isset($val['_multi']); 432 | $key = trim($key); 433 | if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段 434 | $array = explode('|',$key); 435 | $str = array(); 436 | foreach ($array as $m=>$k){ 437 | $v = $multi?$val[$m]:$val; 438 | $str[] = $this->parseWhereItem($this->parseKey($k),$v); 439 | } 440 | $whereStr .= '( '.implode(' OR ',$str).' )'; 441 | }elseif(strpos($key,'&')){ 442 | $array = explode('&',$key); 443 | $str = array(); 444 | foreach ($array as $m=>$k){ 445 | $v = $multi?$val[$m]:$val; 446 | $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')'; 447 | } 448 | $whereStr .= '( '.implode(' AND ',$str).' )'; 449 | }else{ 450 | $whereStr .= $this->parseWhereItem($this->parseKey($key),$val); 451 | } 452 | } 453 | $whereStr .= $operate; 454 | } 455 | $whereStr = substr($whereStr,0,-strlen($operate)); 456 | } 457 | return empty($whereStr)?'':' WHERE '.$whereStr; 458 | } 459 | 460 | // where子单元分析 461 | protected function parseWhereItem($key,$val) { 462 | $whereStr = ''; 463 | if(is_array($val)) { 464 | if(is_string($val[0])) { 465 | if(preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i',$val[0])) { // 比较运算 466 | $whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]); 467 | }elseif(preg_match('/^(NOTLIKE|LIKE)$/i',$val[0])){// 模糊查找 468 | if(is_array($val[1])) { 469 | $likeLogic = isset($val[2])?strtoupper($val[2]):'OR'; 470 | if(in_array($likeLogic,array('AND','OR','XOR'))){ 471 | $likeStr = $this->comparison[strtolower($val[0])]; 472 | $like = array(); 473 | foreach ($val[1] as $item){ 474 | $like[] = $key.' '.$likeStr.' '.$this->parseValue($item); 475 | } 476 | $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')'; 477 | } 478 | }else{ 479 | $whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]); 480 | } 481 | }elseif('exp'==strtolower($val[0])){ // 使用表达式 482 | $whereStr .= $key.' '.$val[1]; 483 | }elseif(preg_match('/IN/i',$val[0])){ // IN 运算 484 | if(isset($val[2]) && 'exp'==$val[2]) { 485 | $whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1]; 486 | }else{ 487 | if(is_string($val[1])) { 488 | $val[1] = explode(',',$val[1]); 489 | } 490 | $zone = implode(',',$this->parseValue($val[1])); 491 | $whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')'; 492 | } 493 | }elseif(preg_match('/BETWEEN/i',$val[0])){ // BETWEEN运算 494 | $data = is_string($val[1])? explode(',',$val[1]):$val[1]; 495 | $whereStr .= $key.' '.strtoupper($val[0]).' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]); 496 | }else{ 497 | E(L('_EXPRESS_ERROR_').':'.$val[0]); 498 | } 499 | }else { 500 | $count = count($val); 501 | $rule = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ; 502 | if(in_array($rule,array('AND','OR','XOR'))) { 503 | $count = $count -1; 504 | }else{ 505 | $rule = 'AND'; 506 | } 507 | for($i=0;$i<$count;$i++) { 508 | $data = is_array($val[$i])?$val[$i][1]:$val[$i]; 509 | if('exp'==strtolower($val[$i][0])) { 510 | $whereStr .= $key.' '.$data.' '.$rule.' '; 511 | }else{ 512 | $whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' '; 513 | } 514 | } 515 | $whereStr = '( '.substr($whereStr,0,-4).' )'; 516 | } 517 | }else { 518 | //对字符串类型字段采用模糊匹配 519 | if(C('DB_LIKE_FIELDS') && preg_match('/('.C('DB_LIKE_FIELDS').')/i',$key)) { 520 | $val = '%'.$val.'%'; 521 | $whereStr .= $key.' LIKE '.$this->parseValue($val); 522 | }else { 523 | $whereStr .= $key.' = '.$this->parseValue($val); 524 | } 525 | } 526 | return $whereStr; 527 | } 528 | 529 | /** 530 | * 特殊条件分析 531 | * @access protected 532 | * @param string $key 533 | * @param mixed $val 534 | * @return string 535 | */ 536 | protected function parseThinkWhere($key,$val) { 537 | $whereStr = ''; 538 | switch($key) { 539 | case '_string': 540 | // 字符串模式查询条件 541 | $whereStr = $val; 542 | break; 543 | case '_complex': 544 | // 复合查询条件 545 | $whereStr = is_string($val)? $val : substr($this->parseWhere($val),6); 546 | break; 547 | case '_query': 548 | // 字符串模式查询条件 549 | parse_str($val,$where); 550 | if(isset($where['_logic'])) { 551 | $op = ' '.strtoupper($where['_logic']).' '; 552 | unset($where['_logic']); 553 | }else{ 554 | $op = ' AND '; 555 | } 556 | $array = array(); 557 | foreach ($where as $field=>$data) 558 | $array[] = $this->parseKey($field).' = '.$this->parseValue($data); 559 | $whereStr = implode($op,$array); 560 | break; 561 | } 562 | return '( '.$whereStr.' )'; 563 | } 564 | 565 | /** 566 | * limit分析 567 | * @access protected 568 | * @param mixed $lmit 569 | * @return string 570 | */ 571 | protected function parseLimit($limit) { 572 | return !empty($limit)? ' LIMIT '.$limit.' ':''; 573 | } 574 | 575 | /** 576 | * join分析 577 | * @access protected 578 | * @param array $join 579 | * @return string 580 | */ 581 | protected function parseJoin($join) { 582 | $joinStr = ''; 583 | if(!empty($join)) { 584 | $joinStr = ' '.implode(' ',$join).' '; 585 | } 586 | return $joinStr; 587 | } 588 | 589 | /** 590 | * order分析 591 | * @access protected 592 | * @param mixed $order 593 | * @return string 594 | */ 595 | protected function parseOrder($order) { 596 | if(is_array($order)) { 597 | $array = array(); 598 | foreach ($order as $key=>$val){ 599 | if(is_numeric($key)) { 600 | $array[] = $this->parseKey($val); 601 | }else{ 602 | $array[] = $this->parseKey($key).' '.$val; 603 | } 604 | } 605 | $order = implode(',',$array); 606 | } 607 | return !empty($order)? ' ORDER BY '.$order:''; 608 | } 609 | 610 | /** 611 | * group分析 612 | * @access protected 613 | * @param mixed $group 614 | * @return string 615 | */ 616 | protected function parseGroup($group) { 617 | return !empty($group)? ' GROUP BY '.$group:''; 618 | } 619 | 620 | /** 621 | * having分析 622 | * @access protected 623 | * @param string $having 624 | * @return string 625 | */ 626 | protected function parseHaving($having) { 627 | return !empty($having)? ' HAVING '.$having:''; 628 | } 629 | 630 | /** 631 | * comment分析 632 | * @access protected 633 | * @param string $comment 634 | * @return string 635 | */ 636 | protected function parseComment($comment) { 637 | return !empty($comment)? ' /* '.$comment.' */':''; 638 | } 639 | 640 | /** 641 | * distinct分析 642 | * @access protected 643 | * @param mixed $distinct 644 | * @return string 645 | */ 646 | protected function parseDistinct($distinct) { 647 | return !empty($distinct)? ' DISTINCT ' :''; 648 | } 649 | 650 | /** 651 | * union分析 652 | * @access protected 653 | * @param mixed $union 654 | * @return string 655 | */ 656 | protected function parseUnion($union) { 657 | if(empty($union)) return ''; 658 | if(isset($union['_all'])) { 659 | $str = 'UNION ALL '; 660 | unset($union['_all']); 661 | }else{ 662 | $str = 'UNION '; 663 | } 664 | foreach ($union as $u){ 665 | $sql[] = $str.(is_array($u)?$this->buildSelectSql($u):$u); 666 | } 667 | return implode(' ',$sql); 668 | } 669 | 670 | /** 671 | * 插入记录 672 | * @access public 673 | * @param mixed $data 数据 674 | * @param array $options 参数表达式 675 | * @param boolean $replace 是否replace 676 | * @return false | integer 677 | */ 678 | public function insert($data,$options=array(),$replace=false) { 679 | $values = $fields = array(); 680 | $this->model = $options['model']; 681 | foreach ($data as $key=>$val){ 682 | if(is_array($val) && 'exp' == $val[0]){ 683 | $fields[] = $this->parseKey($key); 684 | $values[] = $val[1]; 685 | }elseif(is_scalar($val) || is_null($val)) { // 过滤非标量数据 686 | $fields[] = $this->parseKey($key); 687 | if(C('DB_BIND_PARAM') && 0 !== strpos($val,':')){ 688 | $name = md5($key); 689 | $values[] = ':'.$name; 690 | $this->bindParam($name,$val); 691 | }else{ 692 | $values[] = $this->parseValue($val); 693 | } 694 | } 695 | } 696 | $sql = ($replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')'; 697 | $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false); 698 | $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:''); 699 | return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 700 | } 701 | 702 | /** 703 | * 通过Select方式插入记录 704 | * @access public 705 | * @param string $fields 要插入的数据表字段名 706 | * @param string $table 要插入的数据表名 707 | * @param array $option 查询数据参数 708 | * @return false | integer 709 | */ 710 | public function selectInsert($fields,$table,$options=array()) { 711 | $this->model = $options['model']; 712 | if(is_string($fields)) $fields = explode(',',$fields); 713 | array_walk($fields, array($this, 'parseKey')); 714 | $sql = 'INSERT INTO '.$this->parseTable($table).' ('.implode(',', $fields).') '; 715 | $sql .= $this->buildSelectSql($options); 716 | return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 717 | } 718 | 719 | /** 720 | * 更新记录 721 | * @access public 722 | * @param mixed $data 数据 723 | * @param array $options 表达式 724 | * @return false | integer 725 | */ 726 | public function update($data,$options) { 727 | $this->model = $options['model']; 728 | $sql = 'UPDATE ' 729 | .$this->parseTable($options['table']) 730 | .$this->parseSet($data) 731 | .$this->parseWhere(!empty($options['where'])?$options['where']:'') 732 | .$this->parseOrder(!empty($options['order'])?$options['order']:'') 733 | .$this->parseLimit(!empty($options['limit'])?$options['limit']:'') 734 | .$this->parseLock(isset($options['lock'])?$options['lock']:false) 735 | .$this->parseComment(!empty($options['comment'])?$options['comment']:''); 736 | return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 737 | } 738 | 739 | /** 740 | * 删除记录 741 | * @access public 742 | * @param array $options 表达式 743 | * @return false | integer 744 | */ 745 | public function delete($options=array()) { 746 | $this->model = $options['model']; 747 | $sql = 'DELETE FROM ' 748 | .$this->parseTable($options['table']) 749 | .$this->parseWhere(!empty($options['where'])?$options['where']:'') 750 | .$this->parseOrder(!empty($options['order'])?$options['order']:'') 751 | .$this->parseLimit(!empty($options['limit'])?$options['limit']:'') 752 | .$this->parseLock(isset($options['lock'])?$options['lock']:false) 753 | .$this->parseComment(!empty($options['comment'])?$options['comment']:''); 754 | return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 755 | } 756 | 757 | /** 758 | * 查找记录 759 | * @access public 760 | * @param array $options 表达式 761 | * @return mixed 762 | */ 763 | public function select($options=array()) { 764 | $this->model = $options['model']; 765 | $sql = $this->buildSelectSql($options); 766 | $result = $this->query($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 767 | return $result; 768 | } 769 | 770 | /** 771 | * 生成查询SQL 772 | * @access public 773 | * @param array $options 表达式 774 | * @return string 775 | */ 776 | public function buildSelectSql($options=array()) { 777 | if(isset($options['page'])) { 778 | // 根据页数计算limit 779 | list($page,$listRows) = $options['page']; 780 | $page = $page>0 ? $page : 1; 781 | $listRows= $listRows>0 ? $listRows : (is_numeric($options['limit'])?$options['limit']:20); 782 | $offset = $listRows*($page-1); 783 | $options['limit'] = $offset.','.$listRows; 784 | } 785 | if(C('DB_SQL_BUILD_CACHE')) { // SQL创建缓存 786 | $key = md5(serialize($options)); 787 | $value = S($key); 788 | if(false !== $value) { 789 | return $value; 790 | } 791 | } 792 | $sql = $this->parseSql($this->selectSql,$options); 793 | $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false); 794 | if(isset($key)) { // 写入SQL创建缓存 795 | S($key,$sql,array('expire'=>0,'length'=>C('DB_SQL_BUILD_LENGTH'),'queue'=>C('DB_SQL_BUILD_QUEUE'))); 796 | } 797 | return $sql; 798 | } 799 | 800 | /** 801 | * 替换SQL语句中表达式 802 | * @access public 803 | * @param array $options 表达式 804 | * @return string 805 | */ 806 | public function parseSql($sql,$options=array()){ 807 | $sql = str_replace( 808 | array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%COMMENT%'), 809 | array( 810 | $this->parseTable($options['table']), 811 | $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false), 812 | $this->parseField(!empty($options['field'])?$options['field']:'*'), 813 | $this->parseJoin(!empty($options['join'])?$options['join']:''), 814 | $this->parseWhere(!empty($options['where'])?$options['where']:''), 815 | $this->parseGroup(!empty($options['group'])?$options['group']:''), 816 | $this->parseHaving(!empty($options['having'])?$options['having']:''), 817 | $this->parseOrder(!empty($options['order'])?$options['order']:''), 818 | $this->parseLimit(!empty($options['limit'])?$options['limit']:''), 819 | $this->parseUnion(!empty($options['union'])?$options['union']:''), 820 | $this->parseComment(!empty($options['comment'])?$options['comment']:'') 821 | ),$sql); 822 | return $sql; 823 | } 824 | 825 | /** 826 | * 获取最近一次查询的sql语句 827 | * @param string $model 模型名 828 | * @access public 829 | * @return string 830 | */ 831 | public function getLastSql($model='') { 832 | return $model?$this->modelSql[$model]:$this->queryStr; 833 | } 834 | 835 | /** 836 | * 获取最近插入的ID 837 | * @access public 838 | * @return string 839 | */ 840 | public function getLastInsID() { 841 | return $this->lastInsID; 842 | } 843 | 844 | /** 845 | * 获取最近的错误信息 846 | * @access public 847 | * @return string 848 | */ 849 | public function getError() { 850 | return $this->error; 851 | } 852 | 853 | /** 854 | * SQL指令安全过滤 855 | * @access public 856 | * @param string $str SQL字符串 857 | * @return string 858 | */ 859 | public function escapeString($str) { 860 | return addslashes($str); 861 | } 862 | 863 | /** 864 | * 设置当前操作模型 865 | * @access public 866 | * @param string $model 模型名 867 | * @return void 868 | */ 869 | public function setModel($model){ 870 | $this->model = $model; 871 | } 872 | 873 | /** 874 | * 析构方法 875 | * @access public 876 | */ 877 | public function __destruct() { 878 | // 释放查询 879 | if ($this->queryID){ 880 | $this->free(); 881 | } 882 | // 关闭连接 883 | $this->close(); 884 | } 885 | 886 | // 关闭数据库 由驱动类定义 887 | public function close(){} 888 | } --------------------------------------------------------------------------------