├── 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 | 用户= $tip ?>
9 |
10 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/App/Demo/Template/userinfo.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 | = $username ?> 欢迎你
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 |