Repository: Echosong/ES Branch: master Commit: 51216f13b75d Files: 17 Total size: 67.6 KB Directory structure: gitextract_j4d68xrk/ ├── .gitignore ├── README.md ├── index.php ├── res/ │ └── css/ │ └── style.css └── src/ ├── config.php ├── controller/ │ ├── shell/ │ │ ├── BaseController.php │ │ └── MainController.php │ └── web/ │ ├── BaseController.php │ └── MainController.php ├── core/ │ ├── controller.php │ ├── es.php │ ├── helper.php │ ├── model.php │ └── view.php ├── plugin/ │ └── App.php └── view/ └── web/ ├── default.php └── layout.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .idea /logs ================================================ FILE: README.md ================================================ # ![运营商][1] PHP-ES [![Stars](https://img.shields.io/github/stars/echosong/es.svg)](https://github.com/es/echosong/stargazers) [![Forks](https://img.shields.io/github/forks/echosong/es.svg)](https://github.com/es/echosong/network) ## 框架简介 PHP-ES 是一款 极简、灵活、 高性能、扩建性强、上手快php 框架; 以“快速开发、轻松上手、高速执行”为理念,助你成为web开发的能手 ! ### 开发缘由 与其说开发此框架,更准确说法应该是一次代码的整理,本人在接触将近10年的php开发过程中,陆续也接触了一些优秀的框架。不仅仅php 有asp.net mvc、php laravel、php yii、python web.py、python django、golang beego 等等 框架各自有各自的优势,但是使用场景 和性能方面各有所长,在2015年给公司同事分享mvc核心思想的时候, 我在想既然用了这些框架那是不是自己整理出一些核心的、或者说是开发过程中最需要的部分, 来写自己的这么一个极简型框架,如此便有了 ES。 ## 框架结构 整个框架核心五个文件,所有文件加起来放在一起总行数不超过400 行,总大小 18k。 ``` |--src //受保护代码文件夹  |--core |--es.php //启动文件 |- helper.php //实现流程的核心方法类 |--controller.php //控制器文件 |--model.php //模型文件 |--view.php //视图引擎  |--controller //控制器业务文件  |--view //视图文件  |--model //模型一般小型业务可以省略,数据操作直接放到controller  |--config.php //全局配置文件,业务相关的配置也可以放这里,或者自己建立一个独立的配置文件index.php 文件引用 |--res //静态资源 |--logs // 日志记录路径 可以省如果有请保证有写入权限 |--index.php //入口文件 ``` ## 安装 ### 下载 git clone https://github.com/Echosong/es(web建立到当前文件夹) ### 各种web 服务器配置重定向 .hitaccess(Apache): RewriteEngine On RewriteBase / # Allow any files or directories that exist to be displayed directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php/$1 [QSA,L] .htaccess(Nginx): rewrite ^/(.*)/$ /$1 redirect; if (!-e $request_filename){ rewrite ^(.*)$ /index.php break; } ### 配置域名访问比如: http://es.dev/ (hosts 修改) (ES 和 Laravel 性能比较 http://esassets.oss-cn-shanghai.aliyuncs.com/x.png ) ## 具体实现功能 ### 配置文件 #### 配置路由规则 ES 没有像些重型框架单独有 Route 配置, ES的想法很简单,主要分为 模块[m],控制器[c],动作[a] 来路由 ```php 'rewrite' => array( //设置模块 碰到 http://{host}/admin/ 认为进入了后台模块 数组 0 标识默认 m 'm'=>['web','admin','app','api'], 'c'=>'main', //controller 默认值 'a'=>'index', //action 默认值, 'isRewrite'=> TRUE //是否开启伪静态 .htaccess 文件配置 ), ``` 其中 m 为模块,一般我们开发小型web系统时候,后台(admin)、前端(web)、接口(api) 来划分结构; 大型一点的web系统,常根据业务进行模块划分,比如 shop、order、user 等等模块划分。 实际划分就对应着 controller view 里面的文件夹的安排,一般的 一个模块对应其下面的一个文件夹,这样清晰的管理模块 方便协作开发和解耦 另外 配置中 m=>[api..] 数组就是划分的模块,对应地址栏会去选择 www.baidu.com/admin/con/index 域名部分后面的第一个 /admin/ 如果在配置中就表示为识别到的模块, 否者将模块默认为 m[0] 实现代码可以参考 es: ```php $rewrite = $GLOBALS['rewrite']; if ($rewrite['isRewrite']) { $route = explode("/", $_SERVER['PHP_SELF']); if (!empty($rule[1])) { if (in_array($rule[1], $rewrite['m'])) { $_GET['m'] = $route[1]; list($_GET['c'], $_GET['a']) = array_slice($route, 2, 3); } else { $_GET['m'] = $rewrite['m'][0]; list($_GET['c'], $_GET['a']) = array_slice($route, 1, 2); } } } ``` #### 数据库配置 数据库目前支持mysql ```php $dbb = array( 'mysql' => [ //主库 'master'=>[ 'MYSQL_HOST' => '127.0.0.1', 'MYSQL_PORT' => '3306', 'MYSQL_USER' => 'root', 'MYSQL_DB' => 'db_demo', 'MYSQL_PASS' => '123456', 'MYSQL_CHARSET' => 'utf8', ], //从库可以加入多个实例 'slave'=>[ 'MYSQL_HOST' => '127.0.0.1', 'MYSQL_PORT' => '3306', 'MYSQL_USER' => 'root', 'MYSQL_DB' => 'db_demo', 'MYSQL_PASS' => '123456', 'MYSQL_CHARSET' => 'utf8', ] ], 'prefix' => 'mo_', ); ``` #### 业务自定义配置 自定义的业务方面的配置,可以自己定义个配置文件,在config.php 进行引入,也可以直接在config.php 进行修改配置添加节点,后面使用全部用 $GLOBALS = require(APP_PATH . '../config.php'); $GLOBALS 全局 数组配置进行获取相应的配置项。 ### 核心实现文件(Helper.php) ```php /** * 获取规则的url,统一获取url方便路由规则的对应 * @param $c * @param $a * @param array $param * @return string */ public static function url($c, $a, $param = array()) /** * 启动程序,实现了mvc的核心逻辑(根据 c , a 参数怎样去对应执行相应的controller) */ public static function start() /** * 所有的输出格式统一 * @param $message 输出对象 * @param $code 输出错误码 */ public static function responseJson($message, $code = 0) /** 在操作完成一个事务的时候,引导调整到下一个事务,支出ajax的请求返回 * @param $msg * @param string $url * @param int $code 非0 错误提示 */ public static function redirect($msg, $url = '',$code= 0) /** 打印文件日志方便显示调试 * @param $errmsg * @param $level debug, info, error 基本类似log4 */ public static function log($errMsg, $level = 'info') /** * request获取信息设置默认值(特别的 $_GET['**'] 不存在的参数会notice,此函数很好解决了这个问题) * @param $name * @param $defult * @return mixed */ public static function request($name, $defult) ``` ### 控制器 #### Controller 控制器 (Get, Post , Head , Put )+$_Get['a'] 的函数,直接暴露给了请求,请求可以通过 $_GET['a'] 直接调用,如果么没有指定相应http 动作时间,直接调用 public action+$_GET['a'] 的函数 比如 客户端get请求 /api/main/index 会路由到 controller/api/mainController.php 文件 下面 的 public function getIndex(){} 函数,如果此函数不存在,会找个 public function actionIndex(){} 函数 另外在写http 接口时候我们可以直接 Restful api 格式用http 请求自动对应 比如 /api/good 会路由到 controller/api/goodController.php 文件 下面 的 public function get(){} 函数 public function post(){} 函数 ....已至restful Api接口写法 ```php //获取视图数据源的值 public function __get ($name) //此函数设置的值可以在视图模板里直接使用 public function __set ($name, $value) //处理 action对应的模板 $tpl_name 模板地址 会自定到view 和controller 同名的文件夹 默认 $c/$a.php public function display ($tpl_name = "", $return = false) ``` #### baseController baseController 在每个模块里面有父类,继承系统核心Controller 主要用来处理 - 处理模块常用的业务(比如权限验证) - 处理比如分页的数据展现样式 - 公共的业务处理在默认情况下没有model 那么把处理一些公共的数据操作业务也可以放到这里 - 做数据输出的过滤,比如同一个输入格式 {"code":0,"message":""} 诸如此类函数的处理 ### 数据模型 es 数据库操作使用PDO pdo 本身对数据库操作,参数化,防止sql注入, 请在使用元素sql 语句查询的时候,不要直接拼凑字符串。 ### 模型类 模型类继承 Model 一般情况下可以不需要模型类之间在Action里面进行数据库操作,当我们某些业务数据库操作部分其他公用起来,那么模型类是不错的选择。 ```php class User extends Model { //模型对应的表名 public $table_name = 'user_view'; //定义使用的常用 const USER_ON = 0; const USER_OFF = 1; //定义数据的一些枚举 public static $member =[ self::USER_ON => '正常', self::USER_OFF => '禁用' ]; //公用的操作数据方法 public function login($username, $password) { $this->find() //todo } } //直接实例等同 $userDb = new Model('user') 只不过模型里面只能用到原始的Model表; $userDb = new User(); ``` #### 读写分离(多数库操作) ```php public function setDB($db_config_key = 'default', $is_readonly = false) $userDb = new Model('user'); //设置以下操作使用 sale0 实例 $userDb->setDb('sale0', true); $user->... ``` #### 查询 ```php //查询user表数据 $userDb = new Model("user"); /** 查询数据返回数据数组集合 * @param array 查询条件可以是数组,也可以直接是字符串 比如 ['id'=>1] 等效 "and id = 1" * @param null 排序 如 " id desc" * @param string 查詢表字段 * @param null $limit 这个参数比较关键,如果这个参数不为空将 可以分页 * @return mixed */ public function findAll ($conditions = array(), $sort = null, $fields = '*', $limit = null) //分页查询 $userData = $userDb->findAll('id> 1', 'id desc', '*', 10) //执行为上面的语句后 $userDb->page 返回一个分页数组,模板里面可以根据这个数组去做一定处理 一般情况BaseController 自定义个函数来拼凑html 显示 或者直接 是前后端分离 json 直接打印给前端处理 //查询单条数据 public function find ($conditions = array(), $sort = null, $fields = '*') //sql 直接查询(注意参数化形式不要直接拼凑sql) public function query($sql, $params = array()) $user = $userDb->query("select * from mo_user where id=:id ", [':id'=>1]); //查询统计量等同于 sum($field) 返回直接返回整型 public function findSum($conditions, $field) //查询统计量等同于 count(1) 返回直接返回整型 public function findCount($conditions) ``` #### 各种函数的查询条件 ```sql 方式一: 拼凑sql $user->find(" username='{$username}' and sex > $sex") 方式二: 简单数组实现and 连接 $user->find(['username'=>$username, 'sex>'=>$sex]) 方式三: 参数化写法(类似 上面的query 函数方式) $user->find([ "username = :username and sex > :sex and id in (:ids)" , [':username'=>$username, ':sex'=>$sex, ':ids'=>[1,3,4,..] ] ]) 方式一 可能为存在sql注入问题 特别注意, 方式二简单明了,但是连接 or 条件 为能实现 方式三稍微复杂 建议使用 ``` #### 新增 ```php /** 表插入记录 * @param $row * @return mixed */ public function create($row); 演示 $userDb = new Model('user'); //插入单条记录 $userDb->create( ['username'=>'es', 'password'=>'123456', 'sex'=>1, '#created'=>'CURRENT_TIMESTAMP()' //带#的key 后面的value 可以直接是mysql 内置函数 ] ); 批量插入 $userDb->create( [ ['username'=>'es',password'=>'123456','#created'=>'CURRENT_TIMESTAMP()'], ['username'=>'es',password'=>'1234564', '#created'=>'CURRENT_TIMESTAMP()' ]... ] ); ``` #### 更新 ```php /** * @param 查询条件 * @param 更加的数据 * @return mixed */ public function update($conditions, $row) $userDb->update( ['id'=>1], ['username'=>'es', 'password'=>'123456', '+sex'=>1, // 操作符号支持 + - * / 转换成sql sex = sex+1 '#created'=>'CURRENT_TIMESTAMP()' //支持mysql 函数 ] ); ``` #### 删除 ```php //按条件删除数据 public function delete($conditions) $userDb = new Model('user'); $userDb->delete(['id'=>1]); ``` #### 事务 ```php $Db = Model::startTrans(); $userDb = new User(); ...... $Db->commit() ``` ### 视图 es 采用php原始 脚本作为模板标记语言, 主要好处有 - 不需要额外学习一门新的标识语言 - 速度上也是最快的 - 实现起来简单 #### 模板引擎 php 原始脚本,只不过模板里面能够使用的变量 只能来源controller __set 所设置的变量 保证模板使用的数据安全,按需所给。 2. 母版 引入母版机制(具体参考案例) ```html compile($__template_file); ?> ``` ## 扩展引用 es 本身最求灵活,极简单,所有没有引入其他重型模板的功能点,比如 cache 、http 等常用模块,那么使用者,如果需要 相关功能怎么办能 步骤如下: - 配置扩展目录 ```php 'plugins'=>['include','plugin'] //扩展目录 ``` - 扩展目录里面放入扩展类[类文件直拷贝到目录里面] ```php class P { public function test(){ return '扩展'; } } ``` - 在controller action 里面 ```php $p = new P(); $p->test() ``` 同理静态方法调用跟为方便 ## composer 机制扩展 现实开发中大部分功能模块通过composer 安装进去 扩展我们的功能,这是非常棒的选择。具体建议做法 1 . index.php 里面引入 ```php require_once (__DIR__.'/vendor/autoload.php'); ``` 2 . plugin 里面做个静态类 比如 App.php ```php use RedisClient\RedisClient; class App { private static $_redis; /** * 缓存实现 */ public static function redis() { if (empty(self::$_redis)) { self::$_redis = new RedisClient($GLOBALS['redis']); } return self::$_redis; } ``` 3 . 配置文件加入config 构造需要的参数 ```php 'redis'=>[ 'server' => '192.168.1.61:6379', 'timeout' => 2, 'password' => 'songfeiok', 'database' => 3, ], ``` 3 . 使用 ```php $redisClient = App::redis() $redisClient->set("A", 1) $redisClient->get("A") ..... ``` ## 支持常驻脚本 ```php //能处理shell 请求 if (!empty($argc)) { $_REQUEST['m'] = $argv[1]; $_REQUEST['c'] = $argv[2]; $_REQUEST['a'] = $argv[3]; $_REQUEST['p'] = empty($argv[4])? '': $argv[4]; } $ php index.php m c a "自定义参数" ``` 注意 在脚本的 action 跟 web 有区别,shell的controller 能执行的函数是 action前缀的方法,比如 MainController -> actionIndex ## 联系方式 * bug和建议请发送至:`313690636@qq.com`; * 技术支持、技术合作或咨询请联系作者QQ:`313690636`、群:`571627871`。 * 博客地址 :http://www.cnblogs.com/echosong/ [1]:http://dbbsale.oss-cn-shanghai.aliyuncs.com/keqiang/32.ico ================================================ FILE: index.php ================================================ array( //设置模块 碰到 http://{host}/admin/ 认为进入了后台模块 数组 0 标识默认 m 'm' => ['web', 'admin', 'app', 'api'], 'c' => 'main', //controller 默认值 'a' => 'index', //action 默认值, 'isRewrite' => true //是否开启伪静态 .htaccess 文件配置 ), 'debug' => true, 'plugins' => ['include', 'plugin'], //扩展目录 'static' => "res", 'logPath' => 'logs', //日志路径,请保证路径权限可写 'startSession' => false, //session 默认不开启 'limitMax' => 1000 //保护数据库以免一失误导致大查询 ); $dbb = array( 'mysql' => [ //主库 'master' => [ 'MYSQL_HOST' => '127.0.0.1', 'MYSQL_PORT' => '3306', 'MYSQL_USER' => 'root', 'MYSQL_DB' => 'db_yxbd', 'MYSQL_PASS' => '', 'MYSQL_CHARSET' => 'utf8', ], //从库可以加入多个实例 'slave' => [ 'MYSQL_HOST' => '127.0.0.1', 'MYSQL_PORT' => '3306', 'MYSQL_USER' => 'root', 'MYSQL_DB' => 'db_yxbd', 'MYSQL_PASS' => '123456', 'MYSQL_CHARSET' => 'utf8', ] ], 'prefix' => 'tb_', ); return $dbb + $config; ================================================ FILE: src/controller/shell/BaseController.php ================================================ layout = "layout.php"; } /** * 统一输出下分页 */ function pager($pageArr, $param = "") { if ($param != '') { $param = '&' . $param; } if (!$pageArr['all_pages']) { return ""; } $pageStr = ''; $pageStr .= ' 条记录' . ' ' . $pageArr['total_count'] . $pageArr['total_page'] . ' 页   '; $current = $pageArr['current_page']; if ($current > 1) { $pageStr .= '首页 上一页'; } else { $pageStr .= '首页上一页'; } foreach ($pageArr['all_pages'] as $p) { if ($p == $current) { $pageStr .= '  ' . strval($p) . ' '; } else { $pageStr .= ' ' . strval($p) . ' '; } } if ($current < $pageArr['total_page']) { $pageStr .= '下一页 末页'; } else { $pageStr .= '下一页末页'; } return $pageStr; } } ================================================ FILE: src/controller/web/MainController.php ================================================ findAll('', 'id desc', 'id,name', [$page, 20]); Helper::responseJson([$users, $userDb->page ]); } public function getIndex() { $this->display('default.php'); } public function postIndex() { //sleep(6); var_dump($_REQUEST); } public function getTest() { $userDb = new Model('user'); $id = $userDb->create([ [ 'username' => 'admin6', 'password' => 'password1', 'login_count' => 1, '#last_time' => 'CURRENT_TIMESTAMP()' ], [ 'username' => 'admin8', 'password' => 'password1', 'login_count' => 1, '#last_time' => 'CURRENT_TIMESTAMP()' ] ]); echo $userDb->update(['id' => 1], ['+login_count' => 10, "#email" => 'uuid()']); } } ================================================ FILE: src/core/controller.php ================================================ routes = ['m' => $__module, 'c' => $__controller, 'a' => $__action]; $this->template_dir = APP_DIR . DS . "src" . DS . 'view' . DS . $this->routes['m']; $this->init(); } public function __get($name) { return $this->_data[$name]; } public function __set($name, $value) { $this->_data[$name] = $value; } public function display($tpl_name="", $return = false) { if(empty($tpl_name)){ $tpl_name = $this->routes['c'].DS.$this->routes['a'].".php"; } if (!$this->_v) { $this->_v = new View(); } //controller 成员对模板外公开 $this->_v->assign(get_object_vars($this)); $this->_v->assign($this->_data); if ($this->layout) { $this->_v->assign('__render_body', $this->template_dir . DS . $tpl_name); $tpl_name = $this->layout; } if ($return) { //此方式保留方便action里面直接生成静态文件 return $this->_v->render( $this->template_dir . DS . $tpl_name); } else { echo $this->_v->render( $this->template_dir . DS . $tpl_name); } } } ================================================ FILE: src/core/es.php ================================================ $action_name(); } /** * 所有的输出格式统一 支持shell 输出 * @param $message 输出对象 * @param $code 输出错误码 */ public static function responseJson($message, $code = 0) { if (PHP_SAPI === 'cli') { printf("[%s] %s", date('Y/m/d H:i:s'), $message . PHP_EOL); } else { header('x-powered-by:ES.1.0'); header('Content-type: application/json'); exit(json_encode(['code' => $code, 'message' => $message])); } } /** * @param $msg * @param string $url * @param int $code 非0 错误提示 */ public static function redirect($msg, $url = '', $code = 0) { if (isset($_SERVER["HTTP_X_REQUESTED_WITH"]) && strtolower($_SERVER["HTTP_X_REQUESTED_WITH"]) == "xmlhttprequest") { if (is_array($msg)) { exit(json_encode($msg)); } else { self::responseJson(['alertStr' => $msg, 'redirect' => $url], $code); } } else { $strAlert = ""; if (!empty($msg)) { $strAlert = "alert(\"{$msg}\");"; } if ($url == "") { exit(""); } else { } exit(""); } } /** * 获取客户端ip * @return array|false|string */ public static function userIp() { if (getenv("HTTP_CLIENT_IP")) { $ip = getenv("HTTP_CLIENT_IP"); } else { if (getenv("HTTP_X_FORWARDED_FOR")) { $ip = getenv("HTTP_X_FORWARDED_FOR"); } else { if (getenv("REMOTE_ADDR")) { $ip = getenv("REMOTE_ADDR"); } else { $ip = "Unknow"; } } } return $ip; } /** 日志记录 * @param $errMsg * @param $level (debug, info, error) */ public static function log($errMsg, $level = 'info') { if(!is_string($errMsg)){ $errMsg = json_encode($errMsg); } //shell 的操作权限跟web不一样,所以需要区分 global $__module; $logPath = APP_DIR . DS . $GLOBALS['logPath'] . DS . $level . "_".$__module. date('Ymd') . ".log"; error_log(date('Ymd H:i:s') . " " . $errMsg . PHP_EOL, 3, $logPath); if (strtolower(trim($level)) === 'fatal_error') { if ($GLOBALS['debug']) { Helper::responseJson($errMsg, 500); } else { Helper::responseJson('异常查看系统日志', 500); } } } /** 自定义错误 * @param $errNo (错误码) * @param $errStr (错误说明) * @param $errFile 错误文件 * @param $errLine 错误行号 */ public static function customError($errNo, $errStr, $errFile, $errLine) { $errMsg = "[{$errNo}] {$errStr} {$errFile} {$errLine} "; if ($errNo == E_ERROR) { $errNo = 'fatal_error'; } self::log($errMsg, $errNo); if ($GLOBALS["debug"]) { echo $errMsg; } } /**request获取信息设置默认值 * @param $name * @param $default * @param bool $isSafe * @return mixed */ public static function request($name, $default, $isSafe = true) { if (!isset($_REQUEST[$name])) { return $default; } else { return $isSafe ? str_replace("''", "", $_REQUEST[$name]) : $_REQUEST[$name]; } } /** 字段过滤 * @param array $input * @param $fields */ public static function filterFields(array &$input, $fields) { $operator = ['*', '+', '-', '/', '#']; if (empty($fields)) { return; } foreach ($input as $k => $v) { $key = $k; if (in_array(substr($k, 0, 1), $operator)) { $key = substr($k, 1); } if (!in_array($key, $fields)) { unset($input[$k]); } } } } ================================================ FILE: src/core/model.php ================================================ table_name = $GLOBALS['prefix'] . $table_name; } if (empty($this->table_name)) { $this->table_name = $this->view_name; } else { if (substr($this->table_name, 0, strlen($GLOBALS['prefix'])) != $GLOBALS['prefix']) { $this->table_name = $GLOBALS['prefix'] . $this->table_name; } } } /** 开启事务并处理业务 */ public static function transaction( $fn){ $db = self::startTrans(); try { $res = $fn(); $db->commit(); return $res; }catch (Throwable $e){ $db->rollback(); $code = $e->getCode(); if($code == 0){ $code = 500; } Helper::responseJson($e->getMessage(),$code); } } public function __get($name) { if (empty($this->_model[$name])) { return null; } else { return $this->_model[$name]; } } public static function startTrans(){ $db = $GLOBALS['mysql_instances']['default']; if($db == null){ $db = (new Model())->setDB(); } $db->beginTransaction(); return $db; } public function findAll($conditions = array(), $sort = null, $fields = '*', $limit = null) { $sort = !empty($sort) ? ' ORDER BY ' . $sort : ''; $conditions = $this->_where($conditions); $sql = ' FROM ' . $this->table_name . $conditions["_where"]; if (is_array($limit)) { $total = $this->query('SELECT COUNT(*) as M_COUNTER ' . $sql, $conditions["_bindParams"]); $limit = $limit + array(1, 20, 10); $limit = $this->pager($limit[0], $limit[1], $limit[2], $total[0]['M_COUNTER']); $limit = empty($limit) ? '' : ' LIMIT ' . $limit['offset'] . ',' . $limit['limit']; } else { $limit_max = empty($GLOBALS["limitMax"]) ?1000: $GLOBALS['limitMax'] ; if (empty($limit)) { $limit = " LIMIT {$limit_max}"; } else { $limit = intval($limit_max) < $limit ? " LIMIT {$limit_max} " : " LIMIT {$limit} "; } } $_data = $this->query('SELECT ' . $fields . $sql . $sort . $limit, $conditions["_bindParams"]); if ($_data == null) { return []; } else { return $_data; } } public function find($conditions = array(), $sort = null, $fields = '*') { $res = $this->findAll($conditions, $sort, $fields, 1); if (!empty($res)) { $this->_model = array_pop($res); return $this->_model; } else { return false; } } public function update($conditions, $row) { $values = []; Helper::filterFields($row, $this->fields); foreach ($row as $k => $v) { $op = substr($k, 0, 1); if ($op == "+" || $op == "-" || $op == "*" || $op == "/") { $k = substr($k, 1); $set_value[] = '`' . $k . "`= {$k}{$op}{$v}"; continue; } if (strpos($k, '#') === 0) { $set_value[] = '`' . substr($k, 1) . "`=" . $v; continue; } $values[":M_UPDATE_" . $k] = str_ireplace('','', $v); $set_value[] = '`' . $k . "`=" . ":M_UPDATE_" . $k; } // var_dump($values); $conditions = $this->_where($conditions); return $this->execute("UPDATE " . $this->table_name . " SET " . implode(', ', $set_value) . $conditions["_where"], $conditions["_bindParams"] + $values); } public function delete($conditions) { $conditions = $this->_where($conditions); return $this->execute("DELETE FROM " . $this->table_name . $conditions["_where"], $conditions["_bindParams"]); } public function create($rows) { $keys = []; $stack = []; $map = []; if (empty($rows[0])) { $rows = [$rows]; } foreach ($rows as $key => $row) { Helper::filterFields($row, $this->fields); $rows[$key] = $row; } foreach ($rows[0] as $k => $v) { if (strpos($k, '#') === 0) { $k = substr($k, 1); } $keys[] = "`{$k}`"; } foreach ($rows as $key => $row) { $values = []; foreach ($row as $k => $v) { if (strpos($k, '#') === 0) { $values[] = $v; continue; } $map_key = ":{$k}_{$key}"; $values[] = $map_key; $map[$map_key] = str_ireplace('','', $v); } $stack[] = '(' . implode($values, ', ') . ')'; } $sql = "INSERT INTO " . $this->table_name . " (" . implode(', ', $keys) . ") VALUES " . implode(', ', $stack); $this->execute($sql, $map); return $this->_master_db->lastInsertId(); } public function findCount($conditions) { $conditions = $this->_where($conditions); $count = $this->query("SELECT COUNT(*) AS M_COUNTER FROM " . $this->table_name . $conditions["_where"], $conditions["_bindParams"]); return isset($count[0]['M_COUNTER']) && $count[0]['M_COUNTER'] ? $count[0]['M_COUNTER'] : 0; } public function findSum($conditions, $field) { $conditions = $this->_where($conditions); $sum = $this->query("SELECT sum({$field}) AS M_COUNTER FROM " . $this->table_name . $conditions["_where"], $conditions["_bindParams"]); return isset($sum[0]['M_COUNTER']) && $sum[0]['M_COUNTER'] ? $sum[0]['M_COUNTER'] : 0; } public function dumpSql() { return $this->sql; } public function pager($page, $pageSize = 10, $scope = 10, $total) { $this->page = null; if ($total > $pageSize) { $total_page = ceil($total / $pageSize); $page = min(intval(max($page, 1)), $total); $this->page = array( 'total_count' => $total, 'page_size' => $pageSize, 'total_page' => $total_page, 'first_page' => 1, 'prev_page' => ((1 == $page) ? 1 : ($page - 1)), 'next_page' => (($page == $total_page) ? $total_page : ($page + 1)), 'last_page' => $total_page, 'current_page' => $page, 'all_pages' => array(), 'offset' => ($page - 1) * $pageSize, 'limit' => $pageSize, ); $scope = (int)$scope; if ($total_page <= $scope) { $this->page['all_pages'] = range(1, $total_page); } elseif ($page <= $scope / 2) { $this->page['all_pages'] = range(1, $scope); } else { $this->page['all_pages'] = range($page - $scope / 2, min($page + $scope / 2 - 1, $total_page)); } }else{ $this->page = array( 'total_count' => $total, 'page_size' => $pageSize, 'total_page' => 1, 'first_page' => 1, 'prev_page' => 1, 'next_page' => 1, 'last_page' => 1, 'current_page' => $page, 'all_pages' => array(), 'offset' => ($page - 1) * $pageSize, 'limit' => $pageSize, ); } return $this->page; } public function query($sql, $params = array()) { return $this->execute($sql, $params, true); } public function execute($sql, $params = array(), $is_query = false) { $this->sql[] = $sql; if ($is_query && is_object($this->_slave_db)) { $sth = $this->_slave_db->prepare($sql); } else { if (!($this->_master_db)) { $this->setDB('default'); } if (empty($this->_master_db)) { return ; } $sth = $this->_master_db->prepare($sql); } if (is_array($params) && !empty($params)) { foreach ($params as $k => &$v) { $sth->bindParam($k, $v); } } $bool = $sth->execute(); if ($bool) { return $is_query ? $sth->fetchAll(PDO::FETCH_ASSOC) : $sth->rowCount(); } $err = $sth->errorInfo(); if ($err[1] == 2006 || $err[1] == 2013) { $GLOBALS['mysql_instances']['default'] = null; } Helper::log('Database SQL: "' . $sql . '", ErrorInfo: ' . $err[2], Helper::FATAL_ERROR); } public function setDB($db_config_key = 'default', $is_readonly = false) { if ('default' == $db_config_key) { $db_config = $GLOBALS['mysql']['master']; } else { if (!empty($GLOBALS['mysql'][$db_config_key])) { $db_config = $GLOBALS['mysql'][$db_config_key]; } else { Helper::log("Database Err: Db config '$db_config_key' is not exists!", Helper::FATAL_ERROR); } } if ($is_readonly) { return $this->_slave_db = $this->_db_instance($db_config, $db_config_key); } else { return $this->_master_db = $this->_db_instance($db_config, $db_config_key); } } private function pdo_ping($dbconn){ try{ $dbconn->getAttribute(PDO::ATTR_SERVER_INFO); } catch (PDOException $e) { if(strpos($e->getMessage(), 'MySQL server has gone away')!==false){ return false; } } return true; } private function _db_instance($db_config, $db_config_key) { if(!empty($GLOBALS['mysql_instances'][$db_config_key])) { if(!$this->pdo_ping($GLOBALS['mysql_instances'][$db_config_key])){ $GLOBALS['mysql_instances'][$db_config_key] = null; } } if (empty($GLOBALS['mysql_instances'][$db_config_key])) { try { $GLOBALS['mysql_instances'][$db_config_key] = new PDO('mysql:dbname=' . $db_config['MYSQL_DB'] . ';host=' . $db_config['MYSQL_HOST'] . ';port=' . $db_config['MYSQL_PORT'], $db_config['MYSQL_USER'], $db_config['MYSQL_PASS'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'' . $db_config['MYSQL_CHARSET'] . '\'')); } catch (PDOException $e) { Helper::log('Database Err: ' . $e->getMessage(), Helper::FATAL_ERROR); } } return $GLOBALS['mysql_instances'][$db_config_key]; } private function _wherein($sql, $inArray=array()) { foreach ($inArray as $key => $value) { if (!is_array($value)) { continue; } $itemSql = []; unset($inArray[$key]); foreach ($value as $k => $item) { $itemKey = "{$key}_{$k}"; array_push($itemSql, $itemKey); $inArray[$itemKey] = $item; } $sql = str_replace($key, implode(',', $itemSql), $sql); } return [$sql, $inArray]; } private function _where($conditions) { $result = array("_where" => " ", "_bindParams" => array()); if (empty($conditions)) { return $result; } if (is_array($conditions) && !empty($conditions)) { $sql = null; $join = array(); if (array_values($conditions) === $conditions) { list($sql, $conditions) = $this->_wherein($conditions[0], $conditions[1]); // $innerJoin = $conditions[2] ?? []; } else { foreach ($conditions as $key => $condition) { $optStr = substr($key, strlen($key) - 1, 1); if ($optStr == '>' ) { $optStr2 = substr($key, strlen($key) - 1, 2); if($optStr2 == '<>'){ $optStr = $optStr2; } unset($conditions[$key]); $key = str_replace($optStr, '', $key); }else if ($optStr == '=' ) { $optStr2 = substr($key, strlen($key) - 1, 2); if($optStr2 == '>=' || $optStr2 == '<='){ $optStr = $optStr2; } unset($conditions[$key]); $key = str_replace($optStr, '', $key); } else if ($optStr == '<') { unset($conditions[$key]); $key = str_replace($optStr, '', $key); } else { $optStr = '='; } if (substr($key, 0, 1) != ":") { unset($conditions[$key]); $conditions[":" . $key] = $condition; } $join[] = "`{$key}`{$optStr} :{$key}"; } if (!$sql) { $sql = join(" AND ", $join); } } $result["_where"] = " WHERE " . $sql; // if(isset($innerJoin) && $innerJoin){ // $result["_where"] = " AS a INNER JOIN {$GLOBALS['prefix']}{$innerJoin[0]} AS b ON ". // "a.{$innerJoin[1]} = b.{$innerJoin[2]}".$result["_where"]; // } $result["_bindParams"] = $conditions; } else { $result["_where"] = " WHERE " . $conditions; $result["_bindParams"] = array(); } return $result; } } ================================================ FILE: src/core/view.php ================================================ template_vals, EXTR_SKIP); include $tempalte_name; return ob_get_clean(); } public function assign($mixed, $val = '') { if (is_array($mixed)) { foreach ($mixed as $k => $v) { if ($k != '') { $this->template_vals[$k] = $v; } } } else { if ($mixed != '') { $this->template_vals[$mixed] = $val; } } } } ================================================ FILE: src/plugin/App.php ================================================

Welcome to ES

ES 是一款 极简、灵活、 高性能、扩建性强、上手快php 框架; 以“快速开发、轻松上手、高速执行”为理念,助你成为web开发的能手 !
================================================ FILE: src/view/web/layout.php ================================================ ES framework