概览
1、MVC
主流的设计模式,分离界面与逻辑,高效的协作开发。
2、模块化
采用模块形式组织控制器。解决大型项目文件多、难管理的难题,避免后期迭代文件增多导致代码管理混乱的场面。小项目同样可以采用简单的但模块化的架构组织。
3、组件式
框架所有功能都是以组件形式工作,您可以根据自己的需求对各个组件进行改进升华,以适应自己的开发规则,同时可以添加自己的功能组件到框架中,过程简单易用。
4、易使用
在控制器以及模型中可以自动的加载所有组件,省去配置、加载等繁琐的操作,一切都是如此简单$this->组件名
5、高效率
框架根据php语言的特性,在特定部分采用单例的设计模式以节省内存的使用。采用控制反转(IoC)的设计模式实例化类,以降低模块之间的耦合度。
6、数据库
数据库提供了三种操作形式。1 直接执行sql,2 通过连贯操作组合sql语句,3 ORM操作数据库,简单快捷。
7、安全性
1、用户发送的数据全部进行初步检测,并且销毁全局数组,防止一句话脚本的攻击
2、提供数据过滤,清除非打印字符,文件名不合法,XSS字符串。让网站免受跨站攻击
3、防止sql注入,数据库在执行前都会对sql字符串进行合法性检测
4、提供数据格式验证组件,对用户提交的数据类型进行检测,防止数据表字段溢出
5、提供验证码以及表单CSRF防御机制,以应对互联网的'洪水'攻击
项目
创建项目文件夹
我们创建自己的项目文件夹叫做
App,然后在文件夹里面创建我们项目需要的各个文件
- App
- controllers放置控制器类
- models 放置模型类
- config 放置配置文件
- view 放置模版文件
- index.php 入口文件
其他文件夹用户可自行创建,建议您将项目文件(控制器、模型类、配置文件)与可访问的文件(入口文件、图片、css、js)分开存放,并且设置不同的读写权限。
入口文件
入口文件内容仅仅需要下面的三行代码
<?php
define('APP_PATH', dirname(__DIR__)); // 指定项目文件夹
include dirname(__DIR__).'/src/App.php'; // 加载框架入口文件
App::run(); // 执行框架
配置
设置你的配置文件 参照这里
配置文件
配置
配置文件
下面是配置文件夹的内容:
- config
- configs.php核心配置文件
- rules.php表单自动验证定义的规则
- hooks.php定义的钩子程序
- datas.php自定义的配置参数
具体请参阅源代码中configs.php文件的注释
主配置文件
<?php
return [
// 全局的应用程序配置项
'application'=>[
'id'=>'app', // 应用程序的id,项目的命名空间会用到
'timezone'=>'RPC', // 设置时区
'hooks'=>['file'=>'config/hooks.php','class'=>'Hooks'], // 指定钩子程序的位置
],
// 路由配置
// 'router'=>[
// 0 自动识别url
// 1 ?m=Admin&c=Access&a=login&arg1=1....
// 2 Admin/Access/login/arg1/arg2...
// 3 ?r=Admin/Access/login/arg1/arg2...
// 4
'urlmode'=>0,
'defaultController'=>'Index', // 默认控制器
'defaultAction'=>'index', // 默认方法
'modules'=>['Api/WeiXin','Admin'], // 存在的模块名
'regex'=>[ // 正则匹配url规则
'pattern'=>'Index',
],
],
// 数据库配置
'database'=>[
// 'dsn'=>'pdo-mysql://[email protected]/test',
'dsn'=>'mysql://[email protected]/test', // dsn形式
'scheme'=>'pdo-mysql', // 数据库类型(pdo类型的要以 pdo-模型 的形式指定)
'host'=>'127.0.0.1', // 地址
'port'=>3306, // 端口
'dbname'=>'test', // 数据库名称
'user'=>'root', // 帐号
'passwd'=>'321321', // 密码
'charset'=>'utf8', // 数据表编码
'prefix'=>'' // 数据表前缀
],
// 内存缓存
'cache'=>[
'dsn'=>'memcache://127.0.0.1:11211', // dsn字符串形式定义
'scheme'=>'memcache', // 缓存类型
'host'=>'127.0.0.1', // 地址
'port'=>11211, // 端口
],
// session
'session'=>[
'auto_start'=>false, // 自动加载
'passwd'=>'321321', // 连接store的密码
// 'dsn'=>'pdo-mysql://root:@127.0.0.1:3306/session/sess_tab',
'dsn'=>'memcache://127.0.0.1:11211', // 字符串形式
'scheme'=>'pdo-mysql', // 存储session数据库的模型
'host'=>'127.0.0.1', // 地址
'port'=>3306, // 端口号
'user'=>'root', // 用户名
'dbname'=>'session', // 存储session的数据库名称
'tbname'=>'sess_tab', // 存储session的数据表名称
'charset'=>'utf8', // 编码方式
'prefix'=>'', // 表前缀
'sess_name'=>'__SESSIONID__',
'sess_expire'=>3600*24, // 默认session过期时间
'alive_time'=>3600, // 用户活跃时间间隔 这个时间内没有任何操作视为下线
'cookie_path'=>'/', // cookie 路径
'cookie_domain'=>'', // cookie 域名
],
// 模版配置
'view'=>[
'drive'=>'template', // 模板引擎
'skin'=>'default', // 默认皮肤
'tpl_ext'=>'php', // 模版文件后缀
'form_hash_name'=>'__hash__', // 表单hash字段名
'form_hash_keys'=>'fantasy', // 表单hash的key
],
/////////////
// 加载用户数据 //
/////////////
'datas'=>include 'datas.php',
'alias'=>[],
];
钩子程序
默认的钩子程序是在 项目文件夹/config/hooks.php,你也可以在配置文件中指定其他的文件作为钩子程序。
| # |
方法名 |
作用 |
| 1 |
preSystem |
框架初始化之后调用执行 |
| 2 |
preRoute |
在路由解析url之前调用 |
| 3 |
preController |
在url解析之后,调用执行指定的控制器之前调用 |
| 4 |
preResponse |
在发送内容到用户客户端之前调用 |
| 5 |
endSystem |
整个交互过程完成之后执行 |
钩子程序中的类继承了Base.class 你在钩子程序中是可以调用所有绑定过的组件的,但是你不能修改钩子程序类中的方法名。
<?php
use framework\base\Base;
class Hooks extends Base{
public function __construct(){
$this->preSystem();
}
// 框架开始执行之前 初始化之前
public function preSystem(){}
// 路由开始解析之前
public function preRoute(){
// echo 'hello';
}
// 路由解析之后 调用用户控制器之前
public function preController(){
// echo "你请求的控制器是:".$this->dispatch->getControllerName()."
";
// echo "你请求的控制器方法是:".$this->dispatch->getActionName()."
";
}
//发送内容到用户浏览器之前
public function preResponse(){}
框架结束
public function endSystem(){}
}
路由解析规则
框架支持多种的url风格
- ?m=模块名&c=控制器名&a=方法&args....
- /模块名/控制器名/方法/args....
- ?r=模块名/控制器名/方法/args....
模块名是为了指明你的控制器所在的文件夹目录,未指定则指定是
项目/controllers/ 这个目录,指定的话需要你事先在配置文件中
['router']['modules'] => [模块1,模块2,模块3] 声明。指定模块名的话,框架所调用的控制器文件路径就是
项目/controllers/模块名/ 这个目录了。
比如我们需要访问一个控制器,这个控制器文件所在目录路径 App/controllers/Pay/Taobao/CashController.php 如下图:
- App
- controllers
- Pay 嵌套的模块
- Taobao 嵌套的模块
- CashController.php 控制器类文件
- models
- config
我们可以知道,要访问的控制器 CashController.php 在文件夹 controllers/Pay/Taobao 下,而 Pay/Taobao 则是这个控制器的模块名。URL中的参数部分应该是这样的 /Pay_Taobao/Cash/index
Pay_Taobao 会被框架自动转义成 Pay/Taobao ,URL中的模块名 控制器名都会被自动转义,规则如下
| URL部分 |
原来的 |
转义后 |
转义规则 |
| 模块名 |
foo |
Foo |
在没有 '-' '_' 字符串时,将模块名字符串首字母大写 |
| 模块名 |
foo_bar |
Foo/Bar |
对于中间有下划线的,会依据下划线分割字符串,再将每个字符串首字母大写然后用 '/' 将字符串拼接 |
| 模块名 |
foo-bar |
FooBar |
对于中间有横线的,会依据横线分割字符串,再将每个字符串首字母大写然后直接将字符串拼接 |
| 控制器 |
bar |
Bar |
在没有 '-' 字符串时,将控制器的首字母大写。(所以在类以及类文件的命名规则都要首字母大写) |
| 控制器 |
foo-bar |
FooBar |
对于中间有横线的,会依据横线分割字符串,再将每个字符串首字母大写然后直接将字符串拼接 |
控制器
命名规则
我们采用流行的驼峰命名法对文件名、类名进行命名规范
控制器文件名首字母要大写且要连接上字符串Controller,后缀为php。比如我们的控制器名称为Index,则文件名为 IndexController.php。若我们要定义的控制器名称是两个单词(customer 、analysis)组成的 则文件名应该是 CustomerAnalysisController.php 。值得注意的是
文件名要和类名一致,包括大小写。
命名空间
我们采用命名空间对用户的类进行管理,这样您就不必担心类命名冲突的问题啦!如何使用命名空间?
1、在文件的最开始定义命名空间
namespace 自定义的名字
2、需要使用一个类的时候
use 自定义的命名空间\类名
更多关于命名空间的用法可以到这里查看
php5增加的命名空间以及异常
3、命名空间的名称是以 foo\bar\class 这样的形式来命名的,一般这个字符串要反映出所属模块以及文件路径的 模块\路径\路径..
创建我们的第一个控制器 IndexController.php
在controllers目录创建一个文件名称为
IndexController.php
文件内容如下:
<?php
namespace app\controllers; // 命名空间
use framework\base\Controller; // 继承框架的控制器
class IndexController extends Controller{ // 定义类名
public function show(){ // 创建一个方法
echo "Hello World!";
}
}
对于命名空间
app\controllers app指明模块是我们的项目 ,controllers是我们文件的存放路径。注意,这里的 app 是你在配置文件中定义的 ['application']['id'] 项。在你的项目中的所有类定义命名空间都要以这个id为开头,来表示文件所属的模块。
框架的命名空间是以 framework 为开始的。并且控制器要继承框架的控制器,这样你就能方便的调用各个组件啦!
// 常用的页面显示函数
$this->assign('key','val') 为模版赋值
$this->display('tpl_name') 显示模版
数据库
连贯操作
无论是在控制器还是在模型中进行编码,我们都可以随时使用数据库,只需要你简单的直接使用
$this->db 。
- $this->db->select(查询的字段)->where(查询条件)->limit(条数限制)->all()
- $this->db->insert(表名,存储的数据)
- $this->db->where(条件)->update(表名,修改的数据)
- $this->db->update(表名,修改的数据,条件)
- $this->db->where(条件)->delete(表名)
- $this->db->delete(表名,条件)
1、查询
一个查询
$this->db->select()->from('tests')->where('level',2)->order('name','DESC')->limit(10,15)->all();最终执行的sql语句是这样的
select * from `tests` where level=2 order by name desc limit 10,15
对于where条件语句值得注意,你可以用三种形式构造你个查询条件(或简单或复杂)
- 1、where(field,value) 简单的就是两个值,field=value
- 2、where(field=>value,比较符,连接符) 多个条件的情况
- 3、where('field1=? and field2>?',['name',3]) 以statement的形式传递参数
举例如下:
$this->db->where([
['name'=>'fantasy','=','and'],
['date'=>'12-01','>','or'],
['date'=>'12-31','对应的SQL语句: where name='fantasy' and date>'12-01' or date
2、添加
添加数据就是 $this->db->insert(tablename,data)
tablename 就是表名成,对于data是添加的数据,形如 [field1=>val1,field2=>val2,field3=>val3.....]。若要增加多条记录data需要是多维数组的形式
[
[field1=>val1-1,field2=>val1-2,field3=>val1-3.....], // 第一条数据
[field1=>val2-1,field2=>val2-2,field3=>val2-3.....], // 第二条数据
[field1=>val3-1,field2=>val3-2,field3=>val3-3.....], // 第三条数据
[field1=>val4-1,field2=>val4-2,field3=>val4-3.....], // 第四条数据
........ // 更多
]
3、修改
更新数据 $this->update(tablename,data[,condition])
tablename 要更新的表名称,data 更新的数据,更新的条件(这里可以不填,在$this->db->where() 处指明条件)。
数据部分的形式多为这样 data = [field1=>newValue1,field2=>newValue2,field3=>newValue3,.......]
4、删除
$this->delete(表名称,条件) or $this->where(条件)->delete(表名称)
ORM
值得注意的是目前的ORM仅限于在model层使用,ORM的操作会让你的开发效率更上一层楼。当我们想要映射一张表的时候只需要在模型(model)的方法里这样写 $表的实例化对象 = $this->orm(表名称) 。
我们创建这样一张表
| 字段名称 | 类型 | 注释 |
| id | unsigned int | 记录id |
| name | varchar(255) | 名称 |
| email | varchar(255) | 邮箱 |
| addr | text | 地址 |
| add_time | unsigned int | 添加记录的时间 |
| ip | unsigned int | 用户的ip |
1、添加数据
public function testOrm(){
$tests = $this->orm('tests'); // 声明一个表的映射
$tests->name='orm'; // 赋值
$tests->email='[email protected]';
$tests->addr='jinhua';
$tests->add_time=time();
$tests->ip = sprintf('%u',ip2long('192.168.0.12'));
var_dump($tests->add()); /// 调用add方法添加数据 成功返回true 失败返回false
}
2、修改数据
$orm->save(条件) 这个条件跟非orm模式的条件格式一样。不同的是,为了安全起见,如果在更新的时候不想设置条件,需要在条件的部分填写false,否则方法返回false
$tests = $this->orm('tests');
$tests->name = 'fantiq';
$tests->email = '[email protected]';
var_dump($tests->save(['id',1]));
3、删除
$orm->delete(条件) 条件跟save方法的用法一样。
4、查询数据
ORM模块提供了方法的查询方法,满足您各种的查询需求。查询结果都是以对象的形式返回,默认是只去结果集的前20条的,你可以通过
setLimit(...)来修改这个参数;查询条件中如果只传递一个只的话,框架会将这个值使用在表的主键字段上,默认的主键字段是 id ,你可以通过
setPrimaryKey 来修改这个参数。下面是ORM查询方面的方法:
- get([fields...]) 获取指定字段的所有数据
- getOne([fields...]) 获取指定字段的一条数据
- getWhere(条件) 根据条件查询数据
- getBy(排序规则[order/group],排序字段) 对查询结果进行排序
- getWhereBy(条件,排序规则,排序字段) 根据条件查询数据并根据排序规则将结果排序
- setLimit() 修改limit的参数
- setPrimaryKey() 设置主键
模版
控制器调用模版
在控制器里面我们常用的是assign(key,val) 方法,对模版进行赋值,display([path]) 方法进行显示页面信息,若display指定参数则会调用指定路径的页面,若未指定这调用默认页面 项目/views/皮肤/控制器名称/方法名.模版后缀。
cache(时间min) 方法可以设置页面静态缓存,参数单位为分钟,表示缓存过期时间。
public function index(){
$this->assign('name','fantasy');
$this->assign('lists',['fantasy','addr','time']);
// $this->cache(1); // 一分钟的缓存
$this->display();
// $this->display('ad/main');
}
模版输出
你可以通过
setLeftTag() 、
setRightTag() 这些方法定义模版标签的界定符,默认是
"和 "}>" 。模版支持皮肤功能功能,你可以通过 setSkin() 方法切换皮肤,默认的皮肤是 'default' 。
输出
<!-- 这样会直接输出变量 -->
<{foreach $data->$val}> <!-- 遍历数字索引的数组 -->
<{val}>
<{/foreach}>
<{foreach $data=$key->$val}> <!-- 遍历字符串索引的数组 -->
<{key}>---><{val}>
<{/foreach}>
<{if 判断条件}> <!-- 分支判断 -->
....
<{else}>
....
<{/if}>
模版功能
页面可被分为多个模块,可以通过模版提供的命令加载模版
@@
layout 模版路径(不要写文件后缀)
css css资源
js js资源
@@
layout 可以让你指定模版文件,然后当前模版的内容会填充到指定模版的位置( 指定模版的标签 <{tag-content}>)处。css 、js 会将指定的资源引用一并注入到模版中(模版中对应的标签是 <{tag-assets}>)。
组件
Session
框架中session的使用非常简单,你只需要
$this->session->start() session数据就加载成功并且能够提供给你使用了,并且框架会在最后更新数据并持久化存储,常用的方法有如下几种,使用非常简单:
- $this->session->set(key,val) 设置或修改session数据
- $this->session->get(key) 获取session数据,若设置key值,则返回所有的session数据
- $this->session->del(key) 删除一项session数据
- $this->session->isOnline(user_id) 查询某个uid是否在线
- $this->session->countOnline() 返回总在线用户数
值得注意的是你需要在配置文件中指明你需要存储session的数据库(mysql memcache redis),建议您使用memcache redis的内存型缓存数据库效率会高,mysql或者php文件型的存储在大访问量下过多的IO使项目效率下降。如若你使用php原生的session机制,可以直接使用
$_SESSION 数据进行操作。
数据验证
框架提供了web应用中最常用的几个数据格式验证 ,你可以通过
$this->verify->验证函数(参数) 直接使用,下面列出这些方法以及用法
1、字段不能为空
$this->verify->required(string $str);
方法会先过滤用户提交的不可打印的字符,其次过滤掉空格,最后检测是否是空字符串。
2、邮箱格式验证
$this->verify->email(string $email)
内部采用的是php的过滤函数进行的验证,字符串太长(超过1000字符)也会返回false
3、url链接格式验证
$this->verify->url(string $url)
由于php自带的验证不太灵活(不带协议会返回false),采取新的验证格式。
4、ip地址验证
$this->verify->ip(string $ip)
采用php内部过滤函数的验证方法。
5、身份证号码验证
$this->verify->id(string/int $id)
采用身份证的数据验证算法对身份证数字格式进行验证。
6、手机号(中国)格式验证
$this->verify->phone(string/int $phone)
手机号码验证,目前只支持中国的手机号。
7、数组验证
$this->verify->number(int/string number,int min,int max)
检测给定的数字是否在 min max的范围内,如果仅需要检测数字是否小于某个数只需要,
$num=10;$this->verify->number($num,null,100) 这个是检测数字$num是否小于 100。
8、检测字符串格式
$this->verify->string(string $str,string $rule)
其中$rule 可以是 alpha、num、zh 以及其他的。alpha表示仅允许字符串,num仅允许数字,zh仅允许汉字。若我们需要字符串允许数字级字母就要这样写 $rule = "alpha,num"
$this->verify->string($str,"alpha,num")。若我们需要允许其他的字符只需要在$rule字符串后面跟上需要允许的字符并用 ',' 隔开即可。比如
$this->verify->string('[email protected]','alpha,num,@,.') 这个将返回
true
9、检测字符串长度
$this->verify->len(string $str,int min,int max)
10、检测是否存在一个列表中
$this->verify->in(mixed $var,string $lists)
变量 $lists 是一列数据的字符串,这些数据用 ',' 隔开。如同这样的数据 $lists = "android,iOS,linux,centos,windows"; 方法就是检测给定的数值是否存在于这个列中。
11、匹配
$this->verify->match(mixed $var,string $field)
这个功能最常用在在注册的时候检测两次密码是否一致。$var 是值,$field是需要检测值是否一致的字段名称。
配置
配置的用法最简单 当我我们想要获取某项值的时候
$this->config->get(key); 同样在需要设置修改某项值的时候
$this->config->set(key,val);
HTTP数据
在web交互方面这个用的很频繁,但是又十分容易遭到攻击(XSS),框架最这些数据进行了初步的过滤并删除全局变量,防止在不正当的使用中导致攻击。
- $this->request->get(key) 获取GET形式的数据
- $this->request->post(key) 获取POST形式的数据
- $this->request->cookie(key) 获取COOKIE数据
- $this->request->files(key) 获取上传文件 $_FILES 数据
- $this->setGet(key,val) 设置修改GET的某项数据
- $this->setPost(key,val) 设置修改POST的某项数据
- $this->setCookie(key,val) 设置修改COOKIE的某项数据
工具
工具类组件中提供了丰富的类供您使用,您只需要
$upload = $this->utils->getUpload(); 就可以使用上传类了。其他的有图片,验证码,文件上传,分页,文件系统 等等...
工具-上传文件
下面一段代码是文件上传,非常简单:
public function upload(){
$upload = $this->utils->getUpload(); // 实例化上传组件
$config = [ // 设置配置项
'type'=>['txt','jpg','png','gif'],
'size'=>2048,
'path'=>'./uploads',
'rename'=>true
];
if($upload->run(field)){ // run(field,config) 开始上传
// 上传成功 返回成功相关信息(上传后的文件路径)
print_r($upload->getInfo());
}else{
// 上传失败 获取失败信息
print_r($upload->getError());
}
}配置部分: type =>[...] 指定允许的文件后缀;size=>2048 指定文件最大尺寸 单位是KB;path=>'./....' 指定上传文件的存储位置,注意这个文件夹要可写(chmod 777);rename=>true/false 是否将文件名重命名
工具-图片 / 验证码
同样通过
$img = $this->utils->getImage(); 实例化图片组件,这个组件主要有三个功能:
captcha()生成验证码 ;
thumb(缩放比例,保存路径,缩放参照)生成图片缩略图;
crop()截取图片;
验证码: $image->captcha(string $h,number $n,array $bg,array $color)
你可以参考源代码里面的代码示例
验证码示例
参数列表:
- $h 验证码的图片高度
- $n 验证码字符数量
- $bg 验证码图片背景色(rgb)
- $color 验证码字符颜色(rgb)
图片缩放:$image->thumb(int $refer,string $savePath,int $order)
参数说明:
- $refer缩放比例
- $savePath处理后的图片保存路径
- $order参照标准,0 参照宽度缩放,1 参照高度缩放
图片截取:$image->crop(int $start_x,int $start_y,int $x_len,int $y_len,int $dst_w,int $dst_h,string $savePath)
- $start_xx轴开始截取的位置
- $start_yy轴开始截取的位置
- $x_lenx轴截取长度
- $y_leny轴截取长度
- $dst_w粘贴到的图片宽度
- $dst_h粘贴到的图片高度
- $savePath图片保存地址
工具-分页
使用分页工具的时候我们需要这样
$page = $this->utils->getPagination(int count,int listNum,int page)
解释下方法 getPagination的三个参数:
- count 要分页的数据的总条数
- listNum 每页显示的数据的条数
- page 要显示哪页的数据
每页显示的条数我们可以通过
$page->setListNum(int n) 来动态设置,也可以在配置文件中设置
['application'=>[
.....
'page_lists'=> 每页显示的条数,
....
]
]
分页中的数据在技术上是反映在sql语句的limit部分的参数的写法,这个分页类通过计算为你提供的这些参数,
$page->getStart() 返回limit要开始的位置,
$page->getLimitNum() 返回取出显示的条数,在使用中就是下面这样:
public function list($p=1){
$page = $this->utils->getPagination($count,$listNum,$p);
sql..... limit .$page->getStart.','.$page->getListNum();
// 或者 用getLimit() 方法,这个方法直接返回的是limit参数的组合
sql..... limit $page->getLimit();
}
下面是前端框架的分页css的一个示例:
工具-文件