├── App └── Demo │ ├── Conf │ └── User.php │ ├── Controller │ └── User.php │ ├── Model │ └── User.php │ └── Template │ ├── signup.php │ └── userinfo.php ├── README.md ├── Zan ├── Common.php ├── Conf │ └── database.php ├── Library │ ├── Bootstrap.php │ ├── Controller.php │ ├── Factory.php │ ├── Hooks │ │ └── Loader.php │ ├── Log.php │ ├── Model.php │ ├── Model │ │ ├── Mongo.php │ │ └── Mysqli.php │ ├── Router.php │ ├── Util │ │ ├── FileInfo.php │ │ ├── FileOp.php │ │ ├── Http.php │ │ ├── Image.php │ │ ├── Request.php │ │ └── Session.php │ ├── View.php │ └── ZException.php └── Zan.php └── index.php /App/Demo/Conf/User.php: -------------------------------------------------------------------------------- 1 | view = new Library\View(); 16 | $this->model = new Demo_Model_User(); 17 | } 18 | 19 | public function signupAction() { 20 | if (Util\Request::isPost()) { 21 | $exists = $this->model->userExists(Util\Request::post('user_name')); 22 | if ($exists) { 23 | $this->view->set('error_msg', '用户名已经存在,请更换'); 24 | } else { 25 | $userName = Util\Request::post('user_name'); 26 | $userPass = Util\Request::post('user_pass'); 27 | $ret = $this->model->signUp($userName, $userPass); 28 | if ($ret) { 29 | $this->model->login($userName, $userPass); 30 | Util\Http::redirect('/demo/user/info'); 31 | } else { 32 | $this->view->set('error_msg', '注册失败'); 33 | } 34 | } 35 | } 36 | 37 | $this->view->render(array( 38 | 'action' => '/demo/user/signup', 39 | 'tip' => '注册', 40 | ), 'signup.php'); 41 | } 42 | 43 | public function loginAction() { 44 | if (Util\Request::isPost()) { 45 | $userName = Util\Request::post('user_name'); 46 | $userPass = Util\Request::post('user_pass'); 47 | $ret = $this->model->login($userName, $userPass); 48 | if ($ret) { 49 | Util\Http::redirect('/demo/user/info'); 50 | } else { 51 | $this->view->set('error_msg', '登录失败,请检查用户名和密码'); 52 | } 53 | } 54 | 55 | $this->view->render(array( 56 | 'action' => '/demo/user/login', 57 | 'tip' => '登录' 58 | ), 'signup.php'); 59 | } 60 | 61 | public function infoAction() { 62 | $uid = $this->model->getUid(); 63 | if (!$uid) { 64 | Util\Http::redirect('/demo/user/login'); 65 | } 66 | $userInfo = $this->model->getUserInfo($uid); 67 | if (false === $userInfo) { 68 | $this->view->set('error_msg', '获取用户失败'); 69 | 70 | } 71 | Library\Log::error('xxx'); 72 | Library\Log::warning('xxx'); 73 | Library\Log::notice('notice'); 74 | $this->view->render(array( 75 | 'username' => $userInfo['name'], 76 | ), 'userinfo.php'); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /App/Demo/Model/User.php: -------------------------------------------------------------------------------- 1 | userExists($userName); 22 | if ($exists) { 23 | return false; 24 | } 25 | $userPass = $this->getMd5Pass($userPass); 26 | $sql = "insert into user(name, pass) values ('{$userName}', '{$userPass}')"; 27 | $table = "user"; 28 | $data = array('name' => $userName, 'pass' => $userPass); 29 | return $this->db->insert($table, $data); 30 | } 31 | 32 | public function login($userName, $userPass) { 33 | if (!$userName || !$userPass) { 34 | return false; 35 | } 36 | $userPass = $this->getMd5Pass($userPass); 37 | $sql = "select id from user " 38 | . "where name = '{$userName}' and pass = '{$userPass}' " 39 | . "limit 1"; 40 | 41 | $ret = $this->db->fetchOne($sql); 42 | if ($ret) { 43 | $this->setUid($ret['id']); 44 | return true; 45 | } else { 46 | return false; 47 | } 48 | } 49 | 50 | public function getUserInfo($uid) { 51 | $sql = "select * from user where id = {$uid}"; 52 | return $this->db->fetchOne($sql); 53 | } 54 | 55 | public function setUid($uid) { 56 | if ($uid) { 57 | Util\Session::set('login_uid', (int)$uid); 58 | return true; 59 | } 60 | return false; 61 | } 62 | 63 | public function getUid() { 64 | return Util\Session::get('login_uid'); 65 | } 66 | 67 | public function userExists($userName) { 68 | return $this->db->getCount('user', array('name' => $userName)); 69 | } 70 | 71 | public function getMd5Pass($pass) { 72 | return md5(Demo_Conf_User::PASS_PREFIX . $pass); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /App/Demo/Template/signup.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 用户<?= $tip ?> 6 | 7 | 8 |

用户

9 |
10 |
11 | 12 |
13 | 14 |

15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /App/Demo/Template/userinfo.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 |

欢迎你

9 |

10 | 重新登录 11 | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## zan 简介 2 | [zan 赞] 是一个免费开源的,快速,简单的面向对象的,轻量级PHP微框架。 3 | 之所以开发这个框架,不是因为它的性能比Yaf好,也不是因为比ci,tp功能强大,仅仅是因为我想练习设计,它只是一个产出,如果你也喜欢它的设计,或者有优雅的方式,那就一起构建吧。 4 | 5 | 我对zan的期望是:它是一个只包含了路由分发,尽可能少的基础库。始终相信在一个轻快的平台上,总能为有想法的技术人提供更多的可能和精彩。 6 | 7 | ## 能做什么? 8 | * controller,model,view 9 | * 基础工具log,session,http,requst 10 | * composer 11 | 12 | ## 基本要求 13 | * PHP5.3+ 14 | * composer(如需要引用其它packagist时) 15 | 16 | # zan 实例 17 | 基于zan的个人站点 [花满树](http://www.huamanshu.com/) http://www.huamanshu.com/ 18 | 19 | # zan 简明教程 20 | 21 | 使用zan是一件很轻松的事情,它体贴地根据uri(/app/controller/action?)转发到相应的app的controller::action中。你的开发只需要从控制器开始,剩下的交给你了,精彩看你如何自由发挥。 22 | 23 | 此例会展示在清晰的结构中,使用简单的基础库简洁优雅地实现app,相信你只需要花上5分钟了解这些东西,就能完全得掌握zan。 24 | 25 | ## 路由分发 26 | 27 | 当发起请求如:`http://great-compayn/demo/user/login?username=zan&pass=superzan`,先配置一项`nginx`的rewrite规则: 28 | 29 | location / { 30 | try_files $uri $uri/ /index.php$is_args$args; 31 | } 32 | 33 | 此时zan会找到`App/Demo/Controller/User.php`类,自动加载所需类和配置,并调用`loginAction`的方法。 34 | 35 | App 36 | └── Demo 37 | ├── Conf 38 | │ └── User.php 39 | ├── Controller 40 | │ └── User.php 41 | ├── Model 42 | │ └── User.php 43 | └── Template 44 | ├── signup.php 45 | └── userinfo.php 46 | 47 | zan把这部分工作交由`Zan\Library\Bootstrap`去处理,在找寻`User.php`和确认是否存在`loginAction`的方法过程,如有其一不存在,则返回404;如在执行过程有自定义错误或异常,将返回50x系统错误。 48 | 49 | ## App 开发 50 | 由开发过程中存在多app,如openapi(开放接口),mis(运营后台),help(反馈平台)。明显的好处在于uri更符合RESTful设计,组织结构也更清晰明了。暂且我们定义一个Demo吧 51 | 52 | ## Controller 53 | 实际上,编写应用的过程就是不断的添加Controller和Action并把它实现。 54 | 55 | 下边是一个Controller User的样子: 56 | 57 | ```php 58 | use Zan\Library\Util; 59 | use Zan\Library\Controller; 60 | use Zan\Library\View; 61 | 62 | class Demo_Controller_User extends Controller { 63 | public function __construct() { 64 | $this->view = new View(); 65 | $this->model = new Demo_Model_User(); 66 | } 67 | 68 | public function loginAction() { 69 | if (Util\Request::isPost()) { 70 | $userName = Util\Request::post('user_name'); 71 | $userPass = Util\Request::post('user_pass'); 72 | $ret = $this->model->login($userName, $userPass); 73 | if ($ret) { 74 | Util\Http::redirect('/demo/user/info'); 75 | } else { 76 | $this->view->set('error_msg', '登录失败,请检查用户名和密码'); 77 | } 78 | } 79 | 80 | $this->view->render(array( 81 | 'action' => '/demo/user/login', 82 | 'tip' => '登录' 83 | ), 'signup.php'); 84 | } 85 | } 86 | ``` 87 | 88 | 在User中,使用了两种自动加载方式,一种是命名空间,另一种是下划线,前者是全局的,后者是在App工作空间内实现快速自动加载(另外个人审美原因)。 89 | 90 | ## Model 91 | 92 | `Demo_Model_User`会自动加载App/Demo/Model/User.php, 93 | 94 | ```php 95 | use Zan\Library\Util; 96 | use Zan\Library\Model; 97 | 98 | class Demo_Model_User extends Model { 99 | 100 | public function __construct() { 101 | parent::__construct('mysqli'); 102 | } 103 | 104 | public function login($userName, $userPass) { 105 | if (!$userName || !$userPass) { 106 | return false; 107 | } 108 | $userPass = $this->getMd5Pass($userPass); 109 | $sql = "select * from user where name = '{$userName}' and pass = '{$userPass}'"; 110 | return $this->db->fetchOne($sql); 111 | } 112 | } 113 | ``` 114 | 115 | ## view 116 | 117 | 模板渲染目前实现非常简单,100%原生,没有做任何正则替换。模板赋值有两种方式,`$this->view->set('error_msg', '登录失败,请检查用户名和密码');`;另外一种就是在最后渲染模板`$this->view->render($array, $tpl)`统一赋值。$tpl模板在当前App目前下Template下,即Demo/Template/signup.php 118 | 119 | 渲染方式: 120 | ```php 121 | Zan\Library\View; 122 | ... 123 | public function render($array = [], $tpl = null) { 124 | if (is_array($array) && $tpl) { 125 | $this->_tpl = APPPATH . APP . '/Template/' . $tpl; 126 | } 127 | $this->mset($array); 128 | 129 | if (!file_exists($this->_tpl)) { 130 | throw new \ZException("can not find tpl[{$this->_tpl}]", E_ERROR); 131 | } 132 | ob_start(); 133 | extract($this->_extract); 134 | include($this->_tpl); 135 | ob_flush(); 136 | } 137 | ... 138 | ``` 139 | 140 | 可以看到,render接受一个$array数组,然后将数组中的数据extract出来,这样一个原本是$array['tip']的数据在模板里边就能通过$tip访问了。而载入模板部分更简单,只是直接require。这是因为zan直接使用PHP来做模板的解释引擎。Template模板signup.php输出如下 141 | 142 | ```php 143 | 144 | 145 | 146 | ``` 147 | 148 | ## 基础库 149 | 一直在想哪些是web开发最常用的基础库,如何构建优雅的组件,以下是个开始,力争简单够用,让喜欢它的开发者能快速上手,同时让自己能这些组件优雅地开发复杂的应用。 150 | 151 | - 日志 152 | - 分模块、分级别 153 | - 配置简单(目前0配置) 154 | - 日志格式清晰易读 155 | - 应用简单 156 | - http 157 | - request 158 | - session 159 | - image 160 | -------------------------------------------------------------------------------- /Zan/Common.php: -------------------------------------------------------------------------------- 1 | init(); 16 | $controller = $router->controller(); 17 | $action = $router->action(); 18 | 19 | try { 20 | if (!Zan\Zan::class2file($controller)) { 21 | Zan\Zan::display40x(); 22 | } 23 | $instance = new $controller(); 24 | if (!method_exists($instance, $action)) { 25 | Zan\Zan::display40x(); 26 | } 27 | $instance->$action(); 28 | } catch (\PDOException $e) { 29 | Log::error($e); 30 | Zan\Zan::display50x('fetch data error'); 31 | } catch (ZException $e) { 32 | var_dump($e); 33 | Log::error($e); 34 | Zan\Zan::display50x($e->getMessage()); 35 | } 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Zan/Library/Controller.php: -------------------------------------------------------------------------------- 1 | 'log', 15 | E_WARNING => 'wf', 16 | E_ERROR => 'wf', 17 | ); 18 | 19 | public static function notice($msg, $errno = 0, $method = NULL, $param = NULL, $depth = 0) { 20 | $message = self::format('NOTICE', $msg, $errno, $method, $param, $depth); 21 | self::_log(E_NOTICE, $message); 22 | } 23 | 24 | public static function warning($msg, $errno = 0, $method = NULL, $param = NULL, $depth = 0) { 25 | $message = self::format('WARNING', $msg, $errno, $method, $param, $depth); 26 | self::_log(E_WARNING, $message); 27 | } 28 | 29 | public static function error($msg, $errno = 0, $method = NULL, $param = NULL, $depth = 0) { 30 | $message = self::format('ERROR', $msg, $errno, $method, $param, $depth); 31 | self::_log(E_ERROR, $message); 32 | } 33 | 34 | private static function format($errType, $msg, $errno = 0, $method = NULL, $param = NULL, $depth = 0) { 35 | // for exception 36 | if (is_a($msg, 'Exception')) { 37 | $trace = $msg->getTraceAsString(); 38 | $file = $msg->getFile(); 39 | $line = $msg->getLine(); 40 | $msg = $msg->getMessage(); 41 | } else { 42 | // normal write log 43 | $bt = debug_backtrace(); 44 | $file = $bt[$depth+1]['file']; 45 | $line = $bt[$depth+1]['line']; 46 | $trace = ''; 47 | } 48 | $uri = $_SERVER['REQUEST_URI']; 49 | $refer = $_SERVER['HTTP_REFERER']; 50 | return sprintf(self::DEFAULT_FORMAT, 51 | $errType, date("Y-m-d H:i:s", time()), $msg, $file, $line, $error, $uri, $refer, $trace); 52 | } 53 | 54 | private static function _log($errLevel, $message) { 55 | $level = self::$ERR_LEVEL[$errLevel]; 56 | $logDir = sprintf("%s/log/%s", ROOT, APP); 57 | if (!is_dir($logDir)) { 58 | if (!mkdir($logDir, 0777, true)) return; 59 | } 60 | $logFile = sprintf("%s%s.%s", $logDir, date("YmdH", time()), $level); 61 | if (!file_exists($logFile) || is_writable($logFile)) { 62 | file_put_contents($logFile, $message, FILE_APPEND); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Zan/Library/Model.php: -------------------------------------------------------------------------------- 1 | db, $func)) { 33 | return call_user_func_array(array($this->db, $func), $params); 34 | } 35 | // trigger_error('') 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Zan/Library/Model/Mongo.php: -------------------------------------------------------------------------------- 1 | db = new \MongoClient($host); 26 | /* 27 | // to do 副本集 28 | $this->db = $db; 29 | $mongoConf = Bd_Conf::getConf('/lbsugc/mongo'); 30 | $mongoConf = $mongoConf['servers']; 31 | foreach ($mongoConf as $v) { 32 | $hosts .= "{$v['server']}:{$v['port']},"; 33 | } 34 | $uri = sprintf("mongodb://%s/?readPreference=nearest", rtrim($hosts, ',')); 35 | $this->mongod = new MongoClient($uri); 36 | */ 37 | } 38 | 39 | public function getCollection($collection, $master = false) { 40 | if (is_object($this->connections[$collection])) { 41 | return $this->connections[$collection]; 42 | } 43 | 44 | $this->connections[$collection] = $this->mongod->selectCollection($this->db, $collection); 45 | return $this->connections[$collection]; 46 | } 47 | 48 | /** 49 | * 返回查询结果的游标,是find的基础方法 50 | * 51 | * Pass the query and options as array objects (this is more convenient than the standard 52 | * Mongo API especially when caching) 53 | * 54 | * $options may contain: 55 | * fields - the fields to retrieve 56 | * sort - the criteria to sort by 57 | * limit - the number of objects to return 58 | * skip - the number of objects to skip 59 | * 60 | * @param string $collection 61 | * @param array $query 62 | * @param array $options 63 | * @return MongoCursor 64 | **/ 65 | public function query($collection, $query = array(), $options = array()) { 66 | $col = $this->getCollection($collection, true); 67 | $fields = isset($options['fields']) ? $options['fields'] : array(); 68 | $result = $col->find($query, $fields); 69 | if (isset($options['sort']) && $options['sort'] !== null) { 70 | $result->sort($options['sort']); 71 | } 72 | if (isset($options['limit']) && $options['limit'] !== null) { 73 | $result->limit($options['limit']); 74 | } 75 | if (isset($options['skip']) && $options['skip'] !== null) { 76 | $result->skip($options['skip']); 77 | } 78 | return $result; 79 | } 80 | 81 | /** 82 | * 数组形式返回 83 | * 84 | * @param string $collection 85 | * @param array $query 86 | * @param array $options 87 | * @return array 88 | **/ 89 | public function find($collection, $query = array(), $options = array()) { 90 | $result = $this->query($collection, $query, $options); 91 | $array = array(); 92 | foreach ($result as $val) { 93 | $array[] = $val; 94 | } 95 | return $array; 96 | } 97 | 98 | /** 99 | * 返回只含一列的数组 100 | * 101 | * @param string $collection 102 | * @param string $field 103 | * @param array $query 104 | * @param array $options 105 | * @return array 106 | **/ 107 | public function findField($collection, $field, $query = array(), $options = array()) { 108 | $options['fields'] = array($field => 1); 109 | $result = $this->query($collection, $query, $options); 110 | $array = array(); 111 | foreach ($result as $val) { 112 | $array[] = $val[$field]; 113 | } 114 | return $array; 115 | } 116 | 117 | /** 118 | * 返回关联数组 119 | * 120 | * @param string $collection 121 | * @param string $key_field 122 | * @param string $value_field 123 | * @param array $query 124 | * @param array $options 125 | * @return array 126 | **/ 127 | public function findAssoc($collection, $key_field, $value_field, $query = array(), $options = array()) { 128 | $options['fields'] = array($key_field => 1, $value_field => 1); 129 | $result = $this->query($collection, $query, $options); 130 | $array = array(); 131 | foreach ($result as $val) { 132 | $array[$val[$key_field]] = $val[$value_field]; 133 | } 134 | return $array; 135 | } 136 | 137 | /** 138 | * 返回一个一行记录的对象 139 | * 140 | * @param string $collection 141 | * @param mixed $id 142 | * @return array 143 | **/ 144 | public function findOne($collection, $id) { 145 | $col = $this->getCollection($collection, true); 146 | if (!is_array($id)) { 147 | $id = array('_id' => self::id($id)); 148 | } 149 | return $col->findOne($id); 150 | } 151 | 152 | /** 153 | * 查询记录数 154 | * 155 | * @param string $collection 156 | * @param array $query 157 | * @return integer 158 | **/ 159 | public function count($collection, $query = array()) { 160 | $col = $this->getCollection($collection, true); 161 | if ($query) { 162 | $res = $col->find($query); 163 | return $res->count(); 164 | } else { 165 | return $col->count(); 166 | } 167 | } 168 | 169 | /** 170 | * Save a Mongo object -- just a simple shortcut for MongoCollection's save() 171 | * 172 | * @param string $collection 173 | * @param array $data 174 | * @return boolean 175 | **/ 176 | public function save($collection, $data) { 177 | $col = $this->getCollection($collection); 178 | return $col->save($data); 179 | } 180 | 181 | /** 182 | * 插入 183 | * 184 | * @param string $collection 185 | * @param array $array 186 | * @return boolean 187 | **/ 188 | public function insert($collection, $data) { 189 | $col = $this->getCollection($collection); 190 | return $col->insert($data); 191 | } 192 | 193 | /** 194 | * 批量插入 195 | * 196 | * @param string $collection 197 | * @param array $array 198 | * @return boolean 199 | **/ 200 | public function batchInsert($collection, $array) { 201 | $col = $this->getCollection($collection); 202 | return $col->batchInsert($array); 203 | } 204 | 205 | /** 206 | * 最近错误 207 | * 208 | **/ 209 | public function lastError() { 210 | $db = $this->mongod->selectDB($this->db); 211 | return $db->lastError(); 212 | } 213 | 214 | /** 215 | * 更新数据 216 | * 217 | * @param string $collection 218 | * @param array $criteria 219 | * @param array $newobj 220 | * @param boolean $upsert 221 | * @return boolean 222 | **/ 223 | public function update($collection, $criteria, $newobj, $options = array()) { 224 | $col = $this->getCollection($collection); 225 | if ($options === true) { 226 | $options = array('upsert' => true); 227 | } 228 | if (!isset($options['multiple'])) { 229 | $options['multiple'] = false; 230 | } 231 | return $col->update($criteria, $newobj, $options); 232 | } 233 | 234 | public function updateConcurrent($collection, $criteria, $newobj, $options = array()) { 235 | $col = $this->getCollection($collection); 236 | if (!isset($options['multiple'])) { 237 | $options['multiple'] = false; 238 | } 239 | $i = 0; 240 | foreach ($col->find($criteria, array('fields' => array('_id' => 1))) as $obj) { 241 | $col->update(array('_id' => $obj['_id']), $newobj); 242 | if (empty($options['multiple'])) { 243 | return; 244 | } 245 | if (!empty($options['count_mod']) && $i % $options['count_mod'] == 0) { 246 | if (!empty($options['count_callback'])) { 247 | call_user_func($options['count_callback'], $i); 248 | } else { 249 | echo '.'; 250 | } 251 | } 252 | $i++; 253 | } 254 | } 255 | 256 | /** 257 | * update + upsert = true的组合版本 258 | * 259 | * @param string $collection 260 | * @param array $criteria 261 | * @param array $newobj 262 | * @return boolean 263 | **/ 264 | public function upsert($collection, $criteria, $newobj) { 265 | return $this->update($collection, $criteria, $newobj, true); 266 | } 267 | 268 | /** 269 | * Shortcut for MongoCollection's remove() method, with the option of passing an id string 270 | * 271 | * @param string $collection 272 | * @param array $criteria 273 | * @param boolean $just_one 274 | * @return boolean 275 | **/ 276 | public function remove($collection, $criteria, $options = ['justOne' => true]) { 277 | $col = $this->getCollection($collection); 278 | if (!is_array($criteria)) { 279 | $criteria = array('_id' => self::id($criteria)); 280 | } 281 | return $col->remove($criteria, $options); 282 | } 283 | 284 | /** 285 | * 如果线上业务要直接group才可以的话,说明你是个笨蛋 286 | * 聚合计算请转移到后台脚本操作 287 | * 288 | **/ 289 | public function group($collection, array $keys, array $initial, $reduce, array $condition = array()) { 290 | $col = $this->getCollection($collection, true); 291 | return $col->group($keys, $initial, $reduce, $condition); 292 | } 293 | 294 | /** 295 | * 返回一个神奇的东西,当然得看你传啥进来了,可字符串,可数字,可对象 296 | * a MongoId from a string, MongoId, array, or Dbo object 297 | * 298 | * @param mixed $obj 299 | * @return MongoId 300 | **/ 301 | public static function id($obj) { 302 | if ($obj instanceof MongoId) { 303 | return $obj; 304 | } 305 | if (is_string($obj)) { 306 | return new MongoId($obj); 307 | } 308 | if (is_array($obj)) { 309 | return $obj['_id']; 310 | } 311 | return new MongoId($obj->_id); 312 | } 313 | 314 | public function __call($method, $params) { 315 | if (method_exists($this->db, $method)) { 316 | call_user_func_array(array($this->db, $method), $params); 317 | } 318 | } 319 | } -------------------------------------------------------------------------------- /Zan/Library/Model/Mysqli.php: -------------------------------------------------------------------------------- 1 | pdo = new \PDO($dns, $dbConf['user'], $dbConf['passwd']); 30 | $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); 31 | } 32 | 33 | /** 34 | * 单例模式 35 | */ 36 | public static function getInstance() { 37 | static $instance = null; 38 | if (null == $instance) { 39 | $instance = new self(); 40 | } 41 | return $instance; 42 | } 43 | 44 | public function query($sql, array $params = []) { 45 | $stmt = $this->pdo->prepare($sql); 46 | $stmt->execute($params); 47 | return $stmt; 48 | } 49 | 50 | /** 51 | * 查询一条记录 52 | * @param $sql string 53 | * @param $params array bind_params 54 | * @return array 55 | */ 56 | public function fetchOne($sql, array $params = []) { 57 | if (! $stmt = $this->query($sql, $params)) return; 58 | return $stmt->fetch(\PDO::FETCH_ASSOC); 59 | } 60 | 61 | /** 62 | * 查询所有记录 63 | * @param $sql string 64 | * @param $params array bind_params 65 | * @return array 66 | */ 67 | public function fetch($sql, array $params = []) { 68 | if (! $stmt = $this->query($sql, $params)) return; 69 | return $stmt->fetchAll(\PDO::FETCH_ASSOC); 70 | } 71 | 72 | /** 73 | * 查询记录数 74 | * @param $table string 75 | * @param $where array 76 | * @return array 77 | */ 78 | public function getCount($table, array $where = []) { 79 | list($whereStr, $whereParams) = $this->where($where); 80 | $sql = sprintf("select count(*) as num from `%s` where %s", $table, $whereStr); 81 | if (! $stmt = $this->query($sql, $whereParams)) return 0; 82 | $ret = $stmt->fetch(\PDO::FETCH_ASSOC); 83 | return $ret['num']; 84 | } 85 | 86 | /** 87 | * insert data 88 | * @param $table string 89 | * @param $data array 90 | * @return int 影响行数 91 | */ 92 | public function insert($table, array $data) { 93 | $insert = $this->getInsert($table, $data); 94 | return $this->query($insert, array_values($data)) ? $this->pdo->lastInsertId() : false; 95 | } 96 | 97 | /** 98 | * update data 99 | * @param $table string 100 | * @param $data array 101 | * @param $where array 102 | * @return int 影响行数 103 | */ 104 | public function update($table, array $data, array $where = []) { 105 | list($whereStr, $whereParams) = $this->where($where); 106 | $update = $this->getUpdate($table, array_keys($data), $whereStr); 107 | if (! $stmt = $this->query($update, array_merge(array_values($data), $whereParams))) return; 108 | return $stmt->rowCount(); 109 | } 110 | 111 | /** 112 | * delete data 113 | * @param $table string 114 | * @param $where array 115 | * @return int 影响行数 116 | */ 117 | public function delete($table, array $where) { 118 | list($whereStr, $whereParams) = $this->where($where); 119 | $delete = $this->getDelete($table, $whereStr); 120 | if (! $stmt = $this->query($delete, $whereParams)) return; 121 | return $stmt->rowCount(); 122 | } 123 | 124 | public function getDelete($table, $where) { 125 | return sprintf('DELETE FROM %s WHERE %s', $table, $where); 126 | } 127 | 128 | public function getInsert($table, array $data) { 129 | return sprintf('INSERT INTO `%s` (`%s`) VALUES (%s)', 130 | $table, join('`, `', array_keys($data)), rtrim(str_repeat("?, ", count($data)), ', ')); 131 | } 132 | 133 | public function getUpdate($table, $field, $where) { 134 | $set = rtrim(join('=?, ', $field), ', '); 135 | $set .= '=?'; 136 | return sprintf('UPDATE %s SET %s WHERE %s', $table, $set, $where); 137 | } 138 | 139 | public function where(array $where) { 140 | return array(join(' AND ', array_keys($where)), array_values($where)); 141 | } 142 | 143 | public function __call($func, $params) { 144 | if (method_exists($this->pdo, $func)) { 145 | return call_user_func_array(array($this->pdo, $func), $params); 146 | } 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /Zan/Library/Router.php: -------------------------------------------------------------------------------- 1 | _app = ucwords($uriInfo[0]); 33 | $this->_controller = $this->_app . '_Controller_' . ucwords($uriInfo[1]); 34 | $this->_action = ucwords($uriInfo[2]) . 'Action'; 35 | define('APP', $this->_app . '/'); 36 | } 37 | 38 | /** 39 | * 获取当前app 40 | * 41 | * @return string 42 | */ 43 | public function app() { 44 | return $this->_app; 45 | } 46 | 47 | /** 48 | * 设置或获取当前controller 49 | * 50 | * @param $controller string 51 | * @return string 52 | */ 53 | public function controller($controller = null) { 54 | if ($controller) { 55 | $this->_controller = $controller; 56 | } 57 | return $this->_controller; 58 | } 59 | 60 | /** 61 | * 设置或获取当前action 62 | * 63 | * @param 64 | * @return 65 | */ 66 | public function action($action = null) { 67 | if ($action) { 68 | $this->_action = $action; 69 | } 70 | return $this->_action; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Zan/Library/Util/FileInfo.php: -------------------------------------------------------------------------------- 1 | $method(); 51 | } 52 | trigger_error("class FileInfo $method not exists", E_USER_ERROR); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Zan/Library/Util/FileOp.php: -------------------------------------------------------------------------------- 1 | $x/$width) { 91 | $sy = $y/4-($height/4); 92 | } else { 93 | $sx = $x/2-($width/2); 94 | } 95 | } 96 | 97 | $new = imagecreatetruecolor($width, $height); 98 | self::alpha($new); 99 | 100 | // Crop and resize image 101 | imagecopyresampled($new, $image, 0, 0, $sx, $sy, $width, $height, $x-($x-($small*$width)), $y-($y-($small*$height))); 102 | 103 | return $new; 104 | } 105 | 106 | /** 107 | * Preserve the alpha channel transparency in PNG images 108 | * 109 | * @param resource $image the image resource handle 110 | */ 111 | public static function alpha($image) { 112 | imagecolortransparent($image, imagecolorallocate($image, 0, 0, 0)); 113 | imagealphablending($image, false); 114 | imagesavealpha($image, true); 115 | } 116 | -------------------------------------------------------------------------------- /Zan/Library/Util/Request.php: -------------------------------------------------------------------------------- 1 | $val) { 21 | $_SESSION[$key] = $val; 22 | } 23 | } 24 | 25 | public static function delete($name) { 26 | if (!isset($_SESSION[$name])) { 27 | return true; 28 | } 29 | unset($_SESSION[$name]); 30 | return true; 31 | } 32 | public static function set($name, $value) { 33 | if (empty($name)) return; 34 | $_SESSION[$name] = $value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Zan/Library/View.php: -------------------------------------------------------------------------------- 1 | _tpl = APPPATH . APP . '/Template/' . $tpl; 17 | } 18 | } 19 | 20 | public function setTpl($tpl) { 21 | $this->_tpl = APPPATH . APP . '/Template/' . $tpl; 22 | } 23 | 24 | public function set($key, $val) { 25 | $this->_extract[$key] = $val; 26 | } 27 | 28 | public function mset($array = []) { 29 | if (!is_array($array) || empty($array)) { 30 | return; 31 | } 32 | foreach ($array as $key => $val) { 33 | $this->set($key, $val); 34 | } 35 | } 36 | 37 | public function render($array = [], $tpl = null) { 38 | if (is_array($array) && $tpl) { 39 | $this->_tpl = APPPATH . APP . '/Template/' . $tpl; 40 | } 41 | $this->mset($array); 42 | 43 | if (!file_exists($this->_tpl)) { 44 | throw new \ZException("can not find tpl[{$this->_tpl}]", E_ERROR); 45 | } 46 | ob_start(); 47 | extract($this->_extract); 48 | include($this->_tpl); 49 | ob_flush(); 50 | } 51 | 52 | public static function renderJSON($array) { 53 | if (!is_array($array)) { 54 | trigger_error('View::renderJSON param is not array', E_USER_ERROR); 55 | } 56 | header('content-type:application/json'); 57 | echo json_encode($array, true); 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /Zan/Library/ZException.php: -------------------------------------------------------------------------------- 1 | getMessage(), $code); 18 | } else { 19 | $msg = sprintf("%s in %s:%s", $message, self::getMessage(), self::getLine()); 20 | parent::__construct($msg, $code); 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Zan/Zan.php: -------------------------------------------------------------------------------- 1 |