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
[](https://github.com/es/echosong/stargazers)
[](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
<?php include $_view_obj->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
================================================
<?php
define('APP_DIR', realpath('./'));
//加载composer 信息
if(file_exists(__DIR__.'/vendor/autoload.php')){
require_once __DIR__.'/vendor/autoload.php';
}
//能处理shell 请求
if (!empty($argc)) {
$_REQUEST['m'] = $argv[1];
$_REQUEST['c'] = $argv[2];
$_REQUEST['a'] = $argv[3];
$_REQUEST['p'] = empty($argv[4])? '': $argv[4];
}
require(APP_DIR . '/src/core/es.php');
================================================
FILE: res/css/style.css
================================================
*,body {
margin: 0px;
padding: 0px;
}
body {
margin: 0px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
background-color: #fff;
}
header,
footer {
width: 960px;
margin-left: auto;
margin-right: auto;
}
.logo {
background-image: url('data:image/png;base64,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');
background-repeat: no-repeat;
-webkit-background-size: 100px 100px;
background-size: 100px 100px;
background-position: center center;
text-align: center;
font-size: 42px;
padding: 250px 0 70px;
font-weight: normal;
text-shadow: 0px 1px 2px #ddd;
}
header {
padding: 100px 0;
}
footer {
line-height: 1.8;
text-align: center;
padding: 50px 0;
color: #999;
}
.description {
text-align: center;
font-size: 16px;
}
a {
color: #444;
text-decoration: none;
}
.backdrop {
position: absolute;
width: 100%;
height: 100%;
box-shadow: inset 0px 0px 100px #ddd;
z-index: -1;
top: 0px;
left: 0px;
}
================================================
FILE: src/config.php
================================================
<?php
$config = array(
'rewrite' => 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
================================================
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2017/10/13
* Time: 20:29
*/
class BaseController extends Controller
{
public function init()
{
}
}
================================================
FILE: src/controller/shell/MainController.php
================================================
<?php
/**
* Created by PhpStorm.
* User: echosong
* Date: 2017/9/10
* Time: 12:21
*/
class MainController extends BaseController
{
/**
* php 脚本运行 php index.php shell main index "自定义参数"
*/
public function actionIndex()
{
while (true){
//todo
echo "fdsfdsfds";
sleep(1);
}
}
}
================================================
FILE: src/controller/web/BaseController.php
================================================
<?php
Class BaseController extends Controller
{
/**
* 初始化 action 执行之前执行
*/
public function init()
{
$this->layout = "layout.php";
}
/**
* 统一输出下分页
*/
function pager($pageArr, $param = "")
{
if ($param != '') {
$param = '&' . $param;
}
if (!$pageArr['all_pages']) {
return "";
}
$pageStr = '';
$pageStr .= '</span> 条记录' . ' <span>共 <span style="color: #92d2ff;">' . $pageArr['total_count'] . $pageArr['total_page'] . ' 页 </span> ';
$current = $pageArr['current_page'];
if ($current > 1) {
$pageStr .= '<a href="?page=1' . $param . '">首页</a> <a href="?page=' . strval($current - 1) . $param . '">上一页</a>';
} else {
$pageStr .= '<span>首页</span><span>上一页</span>';
}
foreach ($pageArr['all_pages'] as $p) {
if ($p == $current) {
$pageStr .= '<span> ' . strval($p) . ' </span>';
} else {
$pageStr .= '<a href="?page=' . strval($p) . $param . '"> ' . strval($p) . ' </a>';
}
}
if ($current < $pageArr['total_page']) {
$pageStr .= '<a href="?page=' . strval($current + 1) . $param . '">下一页</a> <a href="?page=1' . $param . '">末页</a>';
} else {
$pageStr .= '<span>下一页</span><span>末页</span>';
}
return $pageStr;
}
}
================================================
FILE: src/controller/web/MainController.php
================================================
<?php
class MainController extends BaseController
{
public function getLogin()
{
}
private function uplod()
{
}
/**
* 测试分页
*/
public function getPage(){
$userDb = new Model('category');
$page = Helper::request('page', 0);
$users = $userDb->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
================================================
<?php
class Controller
{
public $layout;
private $_v;
private $_data = array();
public $routes;
public $template_dir ;
public function init() { }
public function __construct()
{
global $__module, $__controller, $__action;
$this->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
================================================
<?php
defined('DS') or define('DS', DIRECTORY_SEPARATOR);
define('APP_ROOT', '/');
define('APP_PATH', dirname(__FILE__) . DS);
Date_default_timezone_set("PRC");
$GLOBALS = require(APP_PATH . '../config.php');
if(!empty($GLOBALS['startSession'])){
session_start();
}
require_once(APP_PATH . "helper.php");
require_once(APP_PATH . "controller.php");
require_once(APP_PATH . "model.php");
require_once(APP_PATH . "view.php");
set_error_handler( ["Helper", "customError"]);
register_shutdown_function(function (){
$fatalMsg = error_get_last();
if($fatalMsg){
Helper::log(json_encode($fatalMsg), Helper::FATAL_ERROR);
}
});
//设置路由规范
Helper::setRoute();
//定义全局变量
$__module = $_REQUEST['m'];
$__controller = $_REQUEST['c'];
$__action = $_REQUEST['a'];
spl_autoload_register(function ($class) use ($__module) {
foreach (array_merge($GLOBALS['plugins'], ['model', 'controller' . DS . $__module, './']) as $dir) {
$file = APP_PATH . '../' . $dir . DS . $class . '.php';
if (file_exists($file)) {
include $file;
}
}
});
//开始运行
Helper::start();
================================================
FILE: src/core/helper.php
================================================
<?php
Class Helper
{
//致命错误级别
const FATAL_ERROR = 'fatal_error';
/**
* 检查参数合法性
* @param $name
* @return int
*/
public static function is_available_classname($name)
{
return preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $name);
}
/**
* 获取规则的url
* @param $c
* @param $a
* @param array $param
* @return string
*/
public static function url($c, $a, $param = array())
{
GLOBAL $__module;
GLOBAL $GLOBALS;
$rewrite = $GLOBALS['rewrite'];
$c = empty($c) ? $rewrite['c'] : $c;
$a = empty($a) ? $rewrite['a'] : $a;
$params = empty($param) ? '' : http_build_query($param);
if($_SERVER['SERVER_PORT'] ==443){
$protocol = 'https';
}else{
$protocol = 'http';
}
if ($rewrite['isRewrite']) {
if (!empty($params)) {
$params = "?$params";
}
if ($__module == $rewrite['m'][0]) {
$url = "$protocol://" . $_SERVER["HTTP_HOST"] . '/' . $c . '/' . $a . $params;
} else {
$url = "$protocol://" . $_SERVER["HTTP_HOST"] . '/' . $__module . '/' . $c . '/' . $a . $params;
}
} else {
if ($__module != $rewrite['m'][0]) {
$url = "$protocol://" . $_SERVER["SCRIPT_NAME"] . "?m=$__module&c=$c&a=$a$params";
} else {
$url = "$protocol://" . $_SERVER["SCRIPT_NAME"] . "?c=$c&a=$a$params";
}
}
return $url;
}
/**
* 设置路由
*/
public static function setRoute()
{
$rewrite = $GLOBALS['rewrite'];
$list_route = [$rewrite['m'][0], $rewrite['c'], $rewrite['a']];
if ($rewrite['isRewrite'] && isset($_SERVER['REQUEST_URI'])) {
$requestURI = $_SERVER['REQUEST_URI'];
$requestURI = str_replace('?' . $_SERVER["QUERY_STRING"], '', $requestURI);
$route = explode("/", $requestURI);
if (in_array($route[1], $rewrite['m'])) {
$list_route[0] = $route[1];
$route = array_slice($route, 1, count($route));
}
$list_route[1] = empty($route[1]) ? $list_route[1] : $route[1];
$list_route[2] = empty($route[2]) ? $list_route[2] : $route[2];
}
$_REQUEST['m'] = strtolower(self::request("m", $list_route[0]));
$_REQUEST['c'] = strtolower(self::request("c", $list_route[1]));
$_REQUEST['a'] = strtolower(self::request("a", $list_route[2]));
}
/**
* 启动程序
*/
public static function start()
{
GLOBAL $__module, $__action, $__controller;
//模块对应目录
if (!self::is_available_classname($__module)) {
die("Err: Module name '$__module' is not correct!");
}
if (!is_dir(APP_PATH . '../controller' . DS . $__module)) {
self::responseJson("Err: Module '$__module' is not exists!", 404);
}
if (!self::is_available_classname($__controller)) {
self::responseJson("Err: Controller name '$__controller' is not correct!", 404);
}
$controller_name = $__controller . 'Controller';
//处理restful
$httpMethod = strtolower(empty($_SERVER['REQUEST_METHOD']) ? 'get' : $_SERVER['REQUEST_METHOD']);
$action_name = $httpMethod . ucfirst($__action);
if (!class_exists(ucfirst($controller_name), true)) {
self::responseJson("Err: Controller '$controller_name' is not exists!", 404);
}
$controller_obj = new $controller_name();
if (!method_exists($controller_obj, $action_name)) {
$action_name = 'action' . $__action;
if (!method_exists($controller_obj, $action_name)) {
self::responseJson("Err: Method '$action_name' of '$controller_name' is not exists!", 404);
}
};
$controller_obj->$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("<script>alert('$msg');window.history.go(-1);</script>");
} else {
}
exit("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><script>function sptips(){ {$strAlert} location.href=\"{$url}\";}</script></head><body onload=\"sptips()\"></body></html>");
}
}
/**
* 获取客户端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
================================================
<?php
class Model
{
public $page;
public $table_name;
public $view_name;
public $fields;
private $_model;
private $_master_db;
private $_slave_db;
private $sql = array();
public function __construct($table_name = null)
{
global $GLOBALS;
if ($table_name) {
$this->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('</script>','', $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('</script>','', $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
================================================
<?php
class View
{
private $template_vals = array();
public function render($tempalte_name)
{
@ob_start();
//核心作用语句
extract($this->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
================================================
<?php
//直接应用 composer 安装的名字空间
class App
{
public static function redis()
{
//实例化第三方的类入口
}
}
================================================
FILE: src/view/web/default.php
================================================
<header>
<h1 class="logo">Welcome to ES</h1>
<div class="description">
ES 是一款 极简、灵活、 高性能、扩建性强、上手快php 框架; 以“快速开发、轻松上手、高速执行”为理念,助你成为web开发的能手 !
</div>
</header>
<footer>
<div class="author">
Official website:
<a href="http://www.cnblogs.com/echosong/">my blog</a> /
Contact me:
<a class="email" href="mailto:313690636@qq.com">313690636@qq.com</a>
<a target="_blank" href="http://wpa.qq.com/msgrd?v=3&uin=313690636&site=qq&menu=yes"><img border="0" src="http://wpa.qq.com/pa?p=2:313690636:51" alt="es" title="es"/></a>
</div>
</footer>
<div class="backdrop"></div>
================================================
FILE: src/view/web/layout.php
================================================
<!DOCTYPE html>
<html>
<head>
<title>ES framework </title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHS01jB0tNejdLTX33O01+10tNftdLTX7XS01+10tNftdLTX7XS01+10tNftcK/R7WOeve1gmrrtZZ687b7f6O3o/v7t6P7+7ej+/u3o/v7t6P7+7ej+/u3o/v7t6P7+7ej+/e3n/v3j5v39p+L7+zIAAAAAAAAAAAAAAAB0tNdgdLTX+3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/crLV/2ejw/9hm7r/Y5y7/7ze5//p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6P79++T8/GQAAAAAdLTXNHS01vt0tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/c7PW/2qnyP9hm7r/ZZ68/9Dt8f/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6P79++P7/Dh0tNevdLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/2uoyf9hm7r/f7HJ/+j+/f/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/5v39s3S01+90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/c7PW/2ikxP9hm7r/xOTr/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/n/v3zdLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/crLV/2KdvP+RvtL/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+j+/f90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/aqfI/2yjv//o/v3/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/9xsNL/YZu6/9fx9f/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3Oz1v9inbz/xeTs/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/2WgwP/C4ur/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/ZaDA/8/s8f/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3Oz1v9posD/5fz8/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/cK/R/5PA0//p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/9sp8f/1PDz/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/b67Q/6/V4f/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3S01/90tNf/dLTX/3Gw0v+o0N//6P79/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/014mv9NeJr/TXia/014mv9NeJr/TXia/014mv9NeJr/TXia/014mv9NeJr/TXia/014mv9NeJr/TXia/014mv9XgJ7/tM/Z/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9TfJ3/k7DD/93z9P/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9UfZ3/or7O/+L29//p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Ldpj/Zoyn/9Tr7v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/2KIo//d9PX/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9LdZX/zOPn/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/36csf/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9KdZf/rsTP/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0hzlP/D197/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/SHOT/7zP2P/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9KdJb/nLLA/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+j+/f9Md5n1THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0t2mP9ig5z/7/7+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/5vz89Ux4mrlMd5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/THeZ/0Vujf/E1Nz/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/l/Pu5TXmbQEx3mf1Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/SnWX/1p9lv/o9Pb/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6P79/d/3+EIAAAAATXiadEx3mf1Md5n/THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/SXOU/2iIn//i7/L/6v/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n//v/p//7/6f/+/+n+/f3l+/t2AAAAAAAAAAAAAAAATXmbQEx4mrlMd5n1THeZ/0x3mf9Md5n/THeZ/0x3mf9Md5n/SnSW/091kf+ftML/3Ovu//D+/v/w//7/8P/+//D//v/w//7/8P/+//D//v/w//7/8P/+//D//v/w//7/8P7+/+79/fft/f277fr7QgAAAAAAAAAA4AAAB8AAAAOAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABwAAAA+AAAAc=" type="image/x-icon" />
<link rel="stylesheet" href="<?=APP_ROOT.$GLOBALS['static']?>/css/style.css" />
</head>
<body>
<?php include $__render_body?>
</body>
</html>
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
SYMBOL INDEX (58 symbols across 9 files)
FILE: src/controller/shell/BaseController.php
class BaseController (line 9) | class BaseController extends Controller
method init (line 11) | public function init()
FILE: src/controller/shell/MainController.php
class MainController (line 9) | class MainController extends BaseController
method actionIndex (line 14) | public function actionIndex()
FILE: src/controller/web/BaseController.php
class BaseController (line 3) | Class BaseController extends Controller
method init (line 10) | public function init()
method pager (line 18) | function pager($pageArr, $param = "")
FILE: src/controller/web/MainController.php
class MainController (line 4) | class MainController extends BaseController
method getLogin (line 6) | public function getLogin()
method uplod (line 11) | private function uplod()
method getPage (line 19) | public function getPage(){
method getIndex (line 26) | public function getIndex()
method postIndex (line 32) | public function postIndex()
method getTest (line 38) | public function getTest()
FILE: src/core/controller.php
class Controller (line 4) | class Controller
method init (line 12) | public function init() { }
method __construct (line 14) | public function __construct()
method __get (line 22) | public function __get($name)
method __set (line 27) | public function __set($name, $value)
method display (line 32) | public function display($tpl_name="", $return = false)
FILE: src/core/helper.php
class Helper (line 3) | Class Helper
method is_available_classname (line 13) | public static function is_available_classname($name)
method url (line 25) | public static function url($c, $a, $param = array())
method setRoute (line 60) | public static function setRoute()
method start (line 83) | public static function start()
method responseJson (line 122) | public static function responseJson($message, $code = 0)
method redirect (line 138) | public static function redirect($msg, $url = '', $code = 0)
method userIp (line 163) | public static function userIp()
method log (line 185) | public static function log($errMsg, $level = 'info')
method customError (line 210) | public static function customError($errNo, $errStr, $errFile, $errLine)
method request (line 228) | public static function request($name, $default, $isSafe = true)
method filterFields (line 241) | public static function filterFields(array &$input, $fields)
FILE: src/core/model.php
class Model (line 3) | class Model
method __construct (line 15) | public function __construct($table_name = null)
method transaction (line 30) | public static function transaction( $fn){
method __get (line 47) | public function __get($name)
method startTrans (line 56) | public static function startTrans(){
method findAll (line 64) | public function findAll($conditions = array(), $sort = null, $fields =...
method find (line 90) | public function find($conditions = array(), $sort = null, $fields = '*')
method update (line 102) | public function update($conditions, $row)
method delete (line 129) | public function delete($conditions)
method create (line 135) | public function create($rows)
method findCount (line 172) | public function findCount($conditions)
method findSum (line 180) | public function findSum($conditions, $field)
method dumpSql (line 188) | public function dumpSql()
method pager (line 193) | public function pager($page, $pageSize = 10, $scope = 10, $total)
method query (line 238) | public function query($sql, $params = array())
method execute (line 244) | public function execute($sql, $params = array(), $is_query = false)
method setDB (line 277) | public function setDB($db_config_key = 'default', $is_readonly = false)
method pdo_ping (line 295) | private function pdo_ping($dbconn){
method _db_instance (line 306) | private function _db_instance($db_config, $db_config_key)
method _wherein (line 325) | private function _wherein($sql, $inArray=array())
method _where (line 343) | private function _where($conditions)
FILE: src/core/view.php
class View (line 3) | class View
method render (line 7) | public function render($tempalte_name)
method assign (line 16) | public function assign($mixed, $val = '')
FILE: src/plugin/App.php
class App (line 5) | class App
method redis (line 7) | public static function redis()
Condensed preview — 17 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (78K chars).
[
{
"path": ".gitignore",
"chars": 11,
"preview": ".idea\n/logs"
},
{
"path": "README.md",
"chars": 10737,
"preview": "# ![运营商][1] PHP-ES\n[](https://github.com/es/echosong/starga"
},
{
"path": "index.php",
"chars": 380,
"preview": "<?php\n\ndefine('APP_DIR', realpath('./'));\n//加载composer 信息\nif(file_exists(__DIR__.'/vendor/autoload.php')){\n require_o"
},
{
"path": "res/css/style.css",
"chars": 20426,
"preview": "*,body {\n margin: 0px;\n padding: 0px;\n}\n\nbody {\n margin: 0px;\n font-family: \"Helvetica Neue\", Helvetica, Ari"
},
{
"path": "src/config.php",
"chars": 1151,
"preview": "<?php\n\n$config = array(\n 'rewrite' => array(\n //设置模块 碰到 http://{host}/admin/ 认为进入了后台模块 数组 0 标识默认 m\n 'm'"
},
{
"path": "src/controller/shell/BaseController.php",
"chars": 181,
"preview": "<?php\n/**\n * Created by PhpStorm.\n * User: Administrator\n * Date: 2017/10/13\n * Time: 20:29\n */\n\nclass BaseController ex"
},
{
"path": "src/controller/shell/MainController.php",
"chars": 358,
"preview": "<?php\n/**\n * Created by PhpStorm.\n * User: echosong\n * Date: 2017/9/10\n * Time: 12:21\n */\n\nclass MainController extends "
},
{
"path": "src/controller/web/BaseController.php",
"chars": 1477,
"preview": "<?php\n\nClass BaseController extends Controller\n{\n\n\n /**\n * 初始化 action 执行之前执行\n */\n public function init()\n "
},
{
"path": "src/controller/web/MainController.php",
"chars": 1232,
"preview": "<?php\n\n\nclass MainController extends BaseController\n{\n public function getLogin()\n {\n\n }\n\n private function "
},
{
"path": "src/core/controller.php",
"chars": 1488,
"preview": "<?php\r\n\r\n\r\nclass Controller\r\n{\r\n public $layout;\r\n private $_v;\r\n private $_data = array();\r\n public $routes"
},
{
"path": "src/core/es.php",
"chars": 1152,
"preview": "<?php\r\ndefined('DS') or define('DS', DIRECTORY_SEPARATOR);\r\ndefine('APP_ROOT', '/');\r\ndefine('APP_PATH', dirname(__FILE_"
},
{
"path": "src/core/helper.php",
"chars": 8183,
"preview": "<?php\n\nClass Helper\n{\n //致命错误级别\n const FATAL_ERROR = 'fatal_error';\n\n /**\n * 检查参数合法性\n * @param $name\n "
},
{
"path": "src/core/model.php",
"chars": 14926,
"preview": "<?php\r\n\r\nclass Model\r\n{\r\n public $page;\r\n public $table_name;\r\n public $view_name;\r\n public $fields;\r\n pr"
},
{
"path": "src/core/view.php",
"chars": 670,
"preview": "<?php\r\n\r\nclass View\r\n{\r\n private $template_vals = array();\r\n\r\n public function render($tempalte_name)\r\n {\r\n "
},
{
"path": "src/plugin/App.php",
"chars": 113,
"preview": "<?php\n\n//直接应用 composer 安装的名字空间\n\nclass App\n{\n public static function redis()\n {\n //实例化第三方的类入口\n }\n}"
},
{
"path": "src/view/web/default.php",
"chars": 631,
"preview": "<header>\n <h1 class=\"logo\">Welcome to ES</h1>\n <div class=\"description\">\n ES 是一款 极简、灵活、 高性能、扩建性强、上手快php 框架;"
},
{
"path": "src/view/web/layout.php",
"chars": 6086,
"preview": "\n<!DOCTYPE html>\n\n<html>\n<head>\n <title>ES framework </title>\n <meta http-equiv=\"Content-Type\" content=\"text/html;"
}
]
About this extraction
This page contains the full source code of the Echosong/ES GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 17 files (67.6 KB), approximately 30.5k tokens, and a symbol index with 58 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.