# frame **Repository Path**: caohd/frame ## Basic Information - **Project Name**: frame - **Description**: No description available - **Primary Language**: PHP - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2019-01-05 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 这是一个自己学习的时候的小框架 `` 这是一个单纯的架构而已, 没有产生业务逻辑`` ### 灵感 市面上那么多框架, 为什么要自己搞啊, 因为我想啊..... 学习学习.... ### 环境要求 - nginx (因为我没有写Apache的.htdc***文件, 如果你会写忽略这个, 这个后面应该会提供). - php>7.4 (参数自动注入等等功能需要用到7.4以上的特性(php7.0+支持方法参数和返回值具有数据类型,php7.4+支持类字段具有类型)) - Linux发行版 (因为我没有在Windows下测试过..., 我也没钱买mac测试) - 小小的修改一下php7.4的源代码来支撑切面和弱初始化 ### 实现了的功能 - 入口文件 - MVC的结构 - 模块分离 - 路由 - @PathVariable - ~~Filter~~ - Bean - 参数注入 - 成员变量注入 - 支持composer - phpunit支持 - Apache重写 #### 入口文件 入口文件是用来加载配置 调用控制器等等的功能的一个特殊文件, 这里的入口文件只是实现了nginx的入口文件, 在scripts下面... Apache的嘛 emmm #### MVC的结构 就是控制器层里面可以调用`$this->render($view, $data);`去加载视图, 其中, $view是视图文件的名称, 名称就是视图文件所在的位置, 在每个module的view文件夹是用来存放视图文件的, 默认值是控制器名(不带Controller)+方法名(不带action), $data是用来向view传递数据的, 是一个关联数组. 这个数组的键会变成前端中的变量名. ##### 用例 ```php class TestController extends Controlloer { public function actionTest() { $this->render('', ['hello'=>'world']); } } ``` ```php+HTML ``` #### 模块分离 就是分了模块, 在每个模块里面可以读取到当前模块和根目录下的配置, 不能读取其他模块的配置, 但是可以调用其他模块的model, Controller等等. #### 路由 1. 配置路由别名,支持正则 2. 配置pathVariables (url变量) ##### 例子 在config/Mapping里面添加 ```php private $rules = [ ['/', 'ALL', '/'], // 访问`/`后面带有一个匹配字母或数字或下划线的路由,映射到 `/`上, 并且设置一个key为c, 值为 `\w`匹配中的字符串 ['/index/postTest', 'POST'], // 配置只能通过POST访问 ['//test', 'ALL', '/ap/test1'], // 访问 `/ab/test`,`/ac/test`, `/ad/test`的时候都映射到`/ap/test1`上 ['/ap/test', 'ALL', '/ap/test1'] // 当访问/ap/test的时候实际访问的是/ap/test1 ]; ``` #### ~~Filter~~ 原有的Filter的功能将由后面的Bean+切面代替。 #### Bean 关于Bean, 这是一个从Java Web过来的概念(这个不算抄吧), 可以理解为一个由系统统一管理的单例, 在这里的话就是通过BeanManager进行管理的, 可以通过BeanManager的getBean($name)方法获得一个类的单例, 如果没有的话, 那么就由BeanManager创建一个, getBean的$name参数来获取一个bean, 这个name是一个类的全名称(带路径的那种, 可以通过 ClassName.class获取然后传进去), 一个Bean需要满足两个条件, 1. 需要实现core\utils\Bean的接口, 2. ~~需要提供一个无参数的构造器或者static的getInstance方法.~~ 3. 构造器可以依赖于别的Bean来注入。 ##### 例子 #### 参数注入(仅限于控制器的方法) ##### 什么参数可以被自动注入 - pathVariable 在上面路由规则部分有说, 会设置PathVariable, 那么PathVariables在这里也是会被注入的,并且如果设置了PathVariable,那么就无法再从`$_POST`和`$_GET`里面获取数据。 - int, string, array 类型, 如果一个参数没有定义类型, 那么也会被认为是string类型的, 这三种类型的参数会被优先从`$_POST`获取数据, 然后再从`$_GET`读取数据, 如果上述两个都没有定义和参数名相同的key, 那么就会读取默认值, 如果没有默认值, 那就会抛出一个Error. 如果定义了一个参数是int类型的, 但是传进来的参数, 如果不能转换成int类型, 也会去尝试读取默认值, 读取失败了那就会抛出一个Error. - Bean的实现类 ```php use core\controller\Controller; use core\library\Input; public class TestController extends Controller { // 这样就可以啦 public function actionIndex(int $a, string $b, Input $input) { var_dump($input); } } ``` #### 成员变量注入 ##### 什么参数可以被自动注入 - Bean的实现类 ##### 怎么实现 1. 在注释中声明 @Autowire 2. 在文件头要声明对该Bean的use语句 3. 在注释中该变量的类型 ```php use core\controller\Controller; use core\library\Input; // 2 public class TestController extends Controller { /** * @Autowire // 1 * @var Input $input // 3 */ private $input; // 这样就可以啦 public function actionIndex() { var_dump($this->input); } } ``` #### Model层的使用 ##### 1. 配置 1. 添加一个db.table的节点,里面有如下内容 ```php 'table' => [ 'prefix' => '', // 数据表的前缀 'subfix' => '', // 数据表的后缀 'name_case' => 'SmallCamelCase' // 这个是数据库表的命名规则 大写驼峰式,小写驼峰式,下划线 ] ``` 2. Model类的命名 采用首字母大写的驼峰式命名法,命名要与数据库的表名一致,(当然,如果不打算通过__call去调用数据库可以不一致)。比如说数据表的名称为`index_son`,那么Model的命名就是`IndexSonModel`。 3. Model层的其他字段 $pk;// 主键的名称 $fieldList;// 允许作为__call的条件的字段 ###### 那么问题来了,上面的东西有什么用的 方法调用: ```mysql create table index_son( id int unsigned not null auto_increment, name char(30) not null , pass char(30) not null , under_line_field int unsigned not null , primary key (`id`) ); insert into index_son(name, pass, under_line_field) values ('hello', 'world', 1), ('java', 'c++--', 3), ('php', 'world', 2); ``` ```php selectByNameInAndPassEqAndUnder_line_fieldMore(['hello', 'php'], 'world', 1, ['pass']); } ``` 如上的代码所示,`IndexSonModel`类只需要继承自`Model`, 自身不需要编写任何的代码,就可以查询出`index_son`表下,`name in ('hello', 'php') and pas='world' and under_line_field>1`的内容,查询的字段为`pass`。上面所调用的方法`selectByNameInAndPassEqAndUnder_line_fieldMore`,除却selectBy(findBy)以外,以每个大写字母进行分割,以三个大写字母一组(最后一组只有两个),每一组的第一个对应的数据表的字段名,第二个对应的是查询的(in, =, !=),第三个对应的是(or, and)。如上,可以得出,对应的三个组分别是`name in`,`pass=`,`under_line_field>`。然后这个方法对应的参数的个数为组的个数(3)或者组的个数+1(4),当为组的个数(3)的时候,那就默认查询出所有的字段,当为组的个数+1(4)的时候,多出来的那个参数就是查询到字段数组。 ##### 2. 直接查询 如上只能进行简单的单表查询,没有办法进行复杂的查询,也没有对于事务的支持。这个时候,可以通过 `$this->db`来对数据库进行操作,这是一个基于PDO封装的一个数据库操作单例。 ### 开发中的功能 - 缓存