Skip to content

基于mvc设计模式的php框架,单入口的设计,底层组件化的操作,IoC控制实现低耦合。代码简约冗余度小,可读性强,功能稳定可依赖。:rocket:

Notifications You must be signed in to change notification settings

fantiq/phpframework

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

<title></title>

概览

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<'12-31'
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(表名称)
我们创建这样一张表
字段名称类型注释
idunsigned int记录id
namevarchar(255)名称
emailvarchar(255)邮箱
addrtext地址
add_timeunsigned int添加记录的时间
ipunsigned 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的一个示例:
  • 上一页
  • 1
  • 2
  • 3
  • 4
  • 5
  • 下一页

工具-文件

About

基于mvc设计模式的php框架,单入口的设计,底层组件化的操作,IoC控制实现低耦合。代码简约冗余度小,可读性强,功能稳定可依赖。:rocket:

Resources

Stars

Watchers

Forks

Packages

No packages published