主流的设计模式,分离界面与逻辑,高效的协作开发。
采用模块形式组织控制器。解决大型项目文件多、难管理的难题,避免后期迭代文件增多导致代码管理混乱的场面。小项目同样可以采用简单的但模块化的架构组织。
框架所有功能都是以组件形式工作,您可以根据自己的需求对各个组件进行改进升华,以适应自己的开发规则,同时可以添加自己的功能组件到框架中,过程简单易用。
在控制器以及模型中可以自动的加载所有组件,省去配置、加载等繁琐的操作,一切都是如此简单$this->组件名
框架根据php语言的特性,在特定部分采用单例的设计模式以节省内存的使用。采用控制反转(IoC)的设计模式实例化类,以降低模块之间的耦合度。
数据库提供了三种操作形式。1 直接执行sql,2 通过连贯操作组合sql语句,3 ORM操作数据库,简单快捷。
1、用户发送的数据全部进行初步检测,并且销毁全局数组,防止一句话脚本的攻击
2、提供数据过滤,清除非打印字符,文件名不合法,XSS字符串。让网站免受跨站攻击
3、防止sql注入,数据库在执行前都会对sql字符串进行合法性检测
4、提供数据格式验证组件,对用户提交的数据类型进行检测,防止数据表字段溢出 5、提供验证码以及表单CSRF防御机制,以应对互联网的'洪水'攻击
2、提供数据过滤,清除非打印字符,文件名不合法,XSS字符串。让网站免受跨站攻击
3、防止sql注入,数据库在执行前都会对sql字符串进行合法性检测
4、提供数据格式验证组件,对用户提交的数据类型进行检测,防止数据表字段溢出 5、提供验证码以及表单CSRF防御机制,以应对互联网的'洪水'攻击
我们创建自己的项目文件夹叫做 App,然后在文件夹里面创建我们项目需要的各个文件
- App
- controllers放置控制器类
- models 放置模型类
- config 放置配置文件
- view 放置模版文件
- index.php 入口文件
<?php define('APP_PATH', dirname(__DIR__)); // 指定项目文件夹 include dirname(__DIR__).'/src/App.php'; // 加载框架入口文件 App::run(); // 执行框架设置你的配置文件 参照这里 配置文件
下面是配置文件夹的内容:
钩子程序中的类继承了Base.class 你在钩子程序中是可以调用所有绑定过的组件的,但是你不能修改钩子程序类中的方法名。
- config
- configs.php核心配置文件
- rules.php表单自动验证定义的规则
- hooks.php定义的钩子程序
- datas.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 | 整个交互过程完成之后执行 |
<?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风格
模块名是为了指明你的控制器所在的文件夹目录,未指定则指定是 项目/controllers/ 这个目录,指定的话需要你事先在配置文件中 ['router']['modules'] => [模块1,模块2,模块3] 声明。指定模块名的话,框架所调用的控制器文件路径就是 项目/controllers/模块名/ 这个目录了。
比如我们需要访问一个控制器,这个控制器文件所在目录路径 App/controllers/Pay/Taobao/CashController.php 如下图:
- ?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
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 这样的形式来命名的,一般这个字符串要反映出所属模块以及文件路径的 模块\路径\路径..
在controllers目录创建一个文件名称为 IndexController.php 文件内容如下:
控制器文件名首字母要大写且要连接上字符串Controller,后缀为php。比如我们的控制器名称为Index,则文件名为 IndexController.php。若我们要定义的控制器名称是两个单词(customer 、analysis)组成的 则文件名应该是 CustomerAnalysisController.php 。值得注意的是 文件名要和类名一致,包括大小写。 我们采用命名空间对用户的类进行管理,这样您就不必担心类命名冲突的问题啦!如何使用命名空间?
1、在文件的最开始定义命名空间 namespace 自定义的名字
2、需要使用一个类的时候 use 自定义的命名空间\类名 更多关于命名空间的用法可以到这里查看 php5增加的命名空间以及异常
3、命名空间的名称是以 foo\bar\class 这样的形式来命名的,一般这个字符串要反映出所属模块以及文件路径的 模块\路径\路径..
在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 。
tablename 要更新的表名称,data 更新的数据,更新的条件(这里可以不填,在$this->db->where() 处指明条件)。 数据部分的形式多为这样
我们创建这样一张表
- $this->db->select(查询的字段)->where(查询条件)->limit(条数限制)->all()
- $this->db->insert(表名,存储的数据)
- $this->db->where(条件)->update(表名,修改的数据)
- $this->db->update(表名,修改的数据,条件)
- $this->db->where(条件)->delete(表名)
- $this->db->delete(表名,条件)
$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'添加数据就是 $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.....], // 第四条数据 ........ // 更多 ]更新数据 $this->update(tablename,data[,condition])
tablename 要更新的表名称,data 更新的数据,更新的条件(这里可以不填,在$this->db->where() 处指明条件)。 数据部分的形式多为这样
data = [field1=>newValue1,field2=>newValue2,field3=>newValue3,.......]$this->delete(表名称,条件) or $this->where(条件)->delete(表名称) 值得注意的是目前的ORM仅限于在model层使用,ORM的操作会让你的开发效率更上一层楼。当我们想要映射一张表的时候只需要在模型(model)的方法里这样写 $表的实例化对象 = $this->orm(表名称) 。
我们创建这样一张表
字段名称 | 类型 | 注释 |
id | unsigned int | 记录id |
name | varchar(255) | 名称 |
varchar(255) | 邮箱 | |
addr | text | 地址 |
add_time | unsigned int | 添加记录的时间 |
ip | unsigned int | 用户的ip |
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 }$orm->save(条件) 这个条件跟非orm模式的条件格式一样。不同的是,为了安全起见,如果在更新的时候不想设置条件,需要在条件的部分填写false,否则方法返回false
$tests = $this->orm('tests'); $tests->name = 'fantiq'; $tests->email = '[email protected]'; var_dump($tests->save(['id',1]));$orm->delete(条件) 条件跟save方法的用法一样。 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的使用非常简单,你只需要 $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() 返回总在线用户数
- $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的某项数据
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()截取图片; 你可以参考源代码里面的代码示例 验证码示例
参数列表:
- $h 验证码的图片高度
- $n 验证码字符数量
- $bg 验证码图片背景色(rgb)
- $color 验证码字符颜色(rgb)
参数说明:
- $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图片保存地址
- count 要分页的数据的总条数
- listNum 每页显示的数据的条数
- page 要显示哪页的数据
['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
- 下一页