————通过观看李炎恢B站视频、以《thinkphp5.1完全开发手册》为依据整理
PHP如何与外界沟通?PHP以请求/响应为周期运行服务端程序。客户端与服务端程序的每一次交互(无论是来自浏览器、命令行还是REST API)都是以请求进入服务端程序的,当接收到请求以后,程序启动=》处理请求=》产生响应=》返回给客户端=》程序关闭。每一个请求都在重复此交互过程。与原生PHP开发相比,Thinkphp提供了实现此交互过程的简便方式。主要是以MVC为基础,建立类库,并有机组合这些类。
一、基础知识
(一)访问
1、入口文件。public目录下的index.php是应用的入口文件。
2、动态URL、静态URL、伪静态URL。
静态就是真实的html文档,URL是常以html、htm、shtml、xml为后缀,是存放于服务器上的一个独立文件,有真实的物理路径,访问时没有操作数据库,只是直接提取一个文件。
动态URL常以JSP、PHP等为后缀,最直观的特点是尾部常带参数,如?id=7(新闻列表)。不是独立的一个网页文件,只有逻辑路径,内容存在于数据库中,根据用户请求,提供个性化网页内容。经过了编译。
伪静态URL长得和静态一样,但事实上是动态的,也是只有逻辑路径,没有物理路径,当然也要经过编译,这种伪装操作主要想让搜索引擎把自己当静态页面处理。
3、URL访问有4种模式:
普通模式:http://localhost/tp5/index.php?s=index&s=index&s=index&参数=值
PATH_INFO模式:http://localhost/tp5/index.php/index/index/index/参数/值
rewrite模式:http://localhost/tp5/index/index 就是重写模式,隐藏了入口文件
兼容模式:http://localhost/tp5/?s=index/index/index/参数/值
4、在没有定义路由的情况下典型的URL访问规则是PATH_INFO。可以访问到每个方法,其中分隔符/是可以设置的。
不再支持普通模式的访问(用普通模式访问index&index&index似乎可以,但这是因为默认设置,如果用普通模式访问其它方法就会报错)。
但支持普通模式的参数传递。
如果服务器不支持PATH_INFO模式,可以用兼容模式。
5、控制器的驼峰写法UserAdd在URL中要写成user_add,方法的驼峰写法goodsList在URL中要写成goodslist。这个应该是'url_convert'配置的说明有问题。
6、入口文件的隐藏(就是“在URL中省略”)通过修改.htaccess内容实现,手册中有,但有一行是错误的。这叫URL重写。
7、app.php配置文件中设置了默认模块、控制器、方法均为index(Index),分开理解,即不只是index模块,admin模块也可以只写到模块名admin,后面默认访问admin下的index/index。
8、URL中的参数可以在方法中设置默认值。
(二)请求和响应
1、tp提供了5种方法获取Request对象,这里注意:手册中只提供了4种,没有说$this->request这种。
$this->request,这就得到了Request对象。因为Controller基类中有这个属性,即Request对象。然后->param(),就可以拿到输入变量。
应该使用facade模式。要引入use think\facade\Request; 然后Request::param()即可。
另外可以用助手函数,不用引入,不用继承,不用注入。但个人不喜欢。
2、获取输入变量:param是tp推荐的获取输入变量的方法,统一了get、post等各种请求方法(就是一种facade呗)。
3、获取固定信息:除了param用来获取输入变量,还有一些方法用于获取请求固定信息,个人理解是指输入变量以外的一些系统信息,如host、domain、url、root、path等。
3、获取请求类型:在很多情况下面,我们需要判断当前操作的请求类型是 GET 、 POST 、 PUT 、 DELETE 或者 HEAD,一方面可以针对请求类型作出不同的逻辑处理,另外一方面有些情况下面需要验证安全性,过滤不安全的请求。
4、获取HTTP头信息:感觉这属于获取固定信息的内容啊,而且获取了一堆固定信息,不知道为什么单拎出来?
5、不论获取什么东西,都可以在()中加入true,获取更全面的信息。
6、伪静态。URL伪静态通常是为了满足更好的SEO效果,ThinkPHP支持伪静态URL设置,可以通过设置url_html_suffix 参数随意在URL的最后增加你想要的静态后缀(这就算设置了伪静态,如果设个false,就是关闭了伪静态),而不会影响当前操作的正常执行。例如,设置'url_html_suffix' => 'shtml'的话(默认是html),我们可以把下面的URLhttp://serverName/Home/Blog/read/id/1变成http://serverName/Home/Blog/read/id/1.shtml,后者更具有静态页面的URL特征,但是具有和前面的URL相同的执行效果,并且不会影响原来参数的使用。url方法可以获取当前访问路径的伪静态URL。
7、参数绑定。
8、请求缓存。有两种方式:全局请求缓存和局部请求缓存。请求缓存仅对GET有效,且可设置有效期。
全局缓存在配置中'request_cache' => true,'request_cache_expire' => 3600。
局部缓存在路由中设置,感觉就是路由缓存的内容。
可在F12中查看缓存情况。
9、响应(含10-13共4个子内容)。响应( Response )对象用于动态响应客户端请求,控制发送给用户的信息。通常用于输出数据给客户端或者浏览器。ThinkPHP5.1 的 Response 响应对象由 think\Response 类或者子类完成,ThinkPHP的响应输出是自动的(命令行模式除外),最终会调用 Response 对象的 send 方法完成输出。大多数情况,我们不需要关注 Response 对象本身,只需要在控制器的操作方法中返回数据即可。
10、响应输出。之前一直在用,包括return,json,view,这些的背后是Response对象,但普通输出不需要管这个对象。(?也不用引入这个对象吗?是不是基类里有?)
11、响应参数。当想给出一些状态码或者其它信息时,就需要return response($data,201)。
12、重定向。return redirect('Index/index') 必须要return。
13、文件下载。本质上是有个类来完成文件下载的,但从tp6开始已经全部用助手函数了,所以这里也选择用助手函数。
支持图片下载和文本下载。
return download($data,'test.txt',true),参数说明:准备下载的文件,准备保存的名字,直接下载。
(三)模块设计
1、应用。应用在tp中是一个管理系统架构及生命周期的对象,由系统的think\App类完成,应用通常在入口文件中被调用和执行。应用具有自己独立的配置文件、公共(函数)文件和路由定义文件等。
2、模块。一个典型的应用是由多个模块组成的,这些模块通常都是应用目录下面的一个子目录,每个模块都有自己的配置文件、公共文件和类库文件。
3、控制器。每个模块拥有独立的类库及配置文件,一个模块下面有多个控制器负责响应请求,而每个控制器其实就是一个独立的控制器类。控制器主要负责请求的接收,并调用相关的模型处理,并最终通过视图输出。严格来说,控制器不应该过多的介入业务逻辑处理。事实上,控制器是可以被跳过的,通过路由我们可以直接把请求调度到某个模型或者其他的类进行处理。
4、操作。一个控制器包含多个操作(方法),操作是一个URL访问的最小单元。
5、模型。模型类通常完成实际的业务逻辑和数据封装,并返回和格式无关的数据。模型类并不一定要访问数据库,而且在5.1的架构设计中,只有进行实际 的数据库查询操作的时候,才会进行数据库的连接,是真正的惰性连接。tp的模型支持多层设计,可以对模型层进行更细化的设计和分工,例如把模型层分为逻辑层/服务层/事件层等。
6、视图。控制器调用模型类后,返回的数据通过视图组装成不同格式的输出,视图根据不同的需求,来决定调用模板引擎进行内容解析后输出还是直接输出。
7、模板。视图通常会有一系列的模板文件对应不同的控制器和方法,并且支持动态设置模板目录。模板文件中可以使用一些特殊的模板标签,这些标签的解析通常由模板引擎负责实现。tp中内置了一个基于XML的性能卓越的模板引擎,这是一个专门为tp服务的内置模板引擎,可以很方便地实现模板输出。
8、命名空间。有效解决多个方法命名冲突的问题。可采用use...的方式导入类。命名空间只对函数、类、常量有效。
二、核心知识
tp中MVC的具体实现是需要掌握的核心知识。
(一)控制器(C)
1、tp5.1默认是多模块。
2、可以设置'empty_module'=>'index',当访问不存在的模块时自动转到index模块。
3、tp5.1提供了一个类库Env来获取环境变量:return Env::get('app_path)。
4、应用目录application,在控制器中写命名空间时写为app,如app\controller\...,这是在think目录下的app.php中设置的。
5、控制器主要负责请求的接收,并调用相关的模型处理,并最终通过视图输出(不精确地说,就是PHP接收请求并给出响应的过程)。严格来说,控制器不应该过多的介入业务逻辑处理。事实上,控制器是可以被跳过的,通过路由我们可以直接把请求调度到某个模型或者其他的类进行处理。
6、在模块/controller目录下创建控制器文件,如Index.php,首字母大写且驼峰法(整个站点中,只有控制器和模型文件名是首字母大写的;其它自建的所有目录和文件名都是首字母小写的),控制器中先写命名空间namespace app\index\controller,一般都要继承基类控制器use think\Controller(继承后就可以使用最常用的两个方法assign和fecth)。不继承的话可以用助手函数,但个人不喜欢助手函数。不继承不能用$this吗?不是吧,当前对象还是有的,可能是指不能用fetch等方法。打印$this发现,不继承的话是空对象,没有任何方法,继承的话有一堆方法。
7、注意return和echo,var_dump、dump、halt、return json()的区别。
return主要是用于返回一个结果,echo用于返回一个字符串。
对于非字符串,以后只用halt和var_dump。halt=dump+die;var_dump可以看到真实的数据类型,dump和halt总是以数组形式返回。
对于字符串,return和echo要注意。return有返回的意思,后面代码不执行;echo没有这意思。有的地方只能return,有的地方只能echo。
8、tpl设置的默认输出文件是html,不是json。json主要是ajax用的。json比XML好用,xml中好多标签,json没有标签,且是js的子集,与js完美配合。
9、如果继承了基类控制器,可以定义控制器初始化方法initialize(),执行其它方法时,都会先执行该方法。
10、前置操作,这比初始化方法更灵活。可以用于权限控制。前置方法中只能echo。
11、跳转和重定向。跳转的2个模板在thinkphp/tpl/dispatch_jump.tpl,可以在模板中设置相关信息。
12、当访问了不存在的方法和控制器时,系统会报错,可以使用空方法(感觉就是魔术方法)来拦截。
13、客户端的访问先到达控制器,再访问数据库,渲染模板文件,传给客户端。
(二)模型(M)
1、模型类通常完成实际的业务逻辑和数据封装,并返回和格式无关的数据。模型类并不一定要访问数据库,而且在5.1的架构设计中,只有进行实际的数据库查询操作的时候,才会进行数据库的连接,是真正的惰性连接。
2、ThinkPHP的模型层支持多层设计,你可以对模型层进行更细化的设计和分工,例如把模型层分为逻辑层/服务层/事件层等等。
3、在模块下,建model目录,建模型文件,如User.php,名字与数据表去掉前缀后一致(数据表名都小写,自动对应)。模型文件中,命名空间为namespace app\模块\model,一般继承基类模型use think\Model。
4、在database.php设置数据库连接信息,端口3306可写可不写,一般都设表前缀。
5、以上的操作完成后,通过PDO或模型已经可以直接连接数据库了。
6、不论通过PDO和模型类操作数据库,都有多种语法,选取一种自己喜欢的、容易记的就可以!个人喜欢Db::name('user')->where()->select()这种造型的(设置了表前缀才可以用name,不然要用table且表要写全名think_user)。对模型方式而言,个人喜欢以下方式(手册和视频中没有这样讲,自己试的):先初始化$user=new User;再$user->where()->select()。因为和PDO方式可以共用语法,简单好记。
8、一般用PDO,但有时必须用模型,因为模型中可以定义一些操作。如自动完成数据,验证等,因为在model中定义了,就必须通过模型操作数据库。
9、链式方法。链式查询每一链返回的都是对象,所以可以一直链下去。最后一步的select和find返回数据,所以selecet和find明显不是链式方法的一部分。链式方法很多,用熟就好了,常用的如where,field,limit,page,order,having。
10、查询不到数据时抛出异常,而不是返回null、空数组等(真的是这样吗?)。
$data=Db::name('user')->where('id',142)->findOrFail()会在浏览器中显示以下异常:DataNotFoundException
try--catch就是用来捕获处理异常的,任何可能抛出异常的语句都应该放进去。try--catch不影响后面语句的执行。
11、当多个语句执行时,多次实例化,浪费内存。可以实例化一次,但会出bug,用removeOption处理bug。写法比较麻烦,对自己而言,内存应该够,先不考虑这。
12、子查询用闭包最舒服。
13、JSON字段:数据表中“字段”可以指定json格式,直接可以写入json数据(以数组形式写入)。如果字段是文本类型且想写入json数据,可以在链式中加上->json('name')->。
14、模型的知识还有获取器和修改器、数据自动完成、软删除等(详见《各模块细节》)。
15、数据表可以在项目之初设计,也可以在后端各模块开必中设计,个人目前喜欢后者。
(三)视图(V)
1、可以认为视图和模板是一个意思,更准确点,为了对模板文件更加有效地管理,tp对模板文件进行目录划分,目录就是视图)。
2、控制器调用模型类后,返回的数据通过视图组装成不同格式的输出。视图根据不同的需求,来决定调用模板引擎进行内容解析后输出还是直接输出。这就牵扯出了3,被这个问题困扰了一下午。
3、普遍有种说法:“一个视图对应一个控制器;一个模板对应一个方法”。明明可以看到,在方法与模板一一对应之后,还有一些“多余”的方法,个人是这样理解的:
仍然认同网上的说法“一个模板对应一个方法”,这是指这些渲染模板的方法,明显的标志是方法最后有return $this->fetch(),可称之为“渲染方法”。
“渲染方法”不足以搞定所有功能,还需要其它方法辅助,可称之为“配套方法”。
两者的区别:前者一般是从数据库获取数据,fetch模板;后者一般是紧接前者,对数据进行校验、增删改查,跳转到指定方法。
4、模板技术。
模板技术是为前后端分离而生:模板文件+数据==》模板引擎==》html文档,模板技术干的是字符串拼接的体力活(网站开发干的也就是个装修活)。模板技术出现前,前后端不分离时,只能通过<?php XX ?>的方式。模板引擎是一套独立的语法【NND,模板标签完全是一种新语言!】,除了用{$data}代替<?php XX ?>赋值外,还有一些自己独特的功能,如{include}等。
5、模板引擎。
模板文件中可以使用一些特殊的模板标签,这些标签的解析通常由模板引擎负责实现。模板引擎分两种,一种内置,一种外置作为插件引入,我们用内置的就够。内置引擎的配置文件template.php。配置得很好,不需要修改任何参数。'view_path'虽然是空的,但默认就是view目录。内置的标签库就是Cx标签库。标签库源文件在thinkphp/library/think/template/taglib/Cx.php。这里有所有的标签,包括url。自建扩展标签先不弄,暂时用不到。
6、模板渲染。return $this->fetch()。没继承的时候才用助手函数view,一个效果。
7、模板赋值。assign('name','achen'),assign('data',$data),第一个参数才是在模板中用的名字。
当访问模板时,会在runtime/temp下生成一个编译文件,打开可以看到模板中的{$name}成了<?php echo htmlentities($name); ?>。【报错时,页面上原来是编译文件】。
htmlentities这个函数是干什么的呢?是个过滤函数,防止XSS跨站脚本攻击。如果想换个过滤函数,如htmlspecialchars(),可以在配置文件template.php中增加一条'default_filter'=>'htmlspecialchars'。
如果某个字符,不需要HTML实体转义,可以单独使用raw处理{$name|raw}。
【如果模板这块有任何问题,查看编译文件是个好方法】。
8、模板过滤。$this->filter()->fetch()。
9、模板变量输出。
重要!(刚开始理解错误了)变量输出指的是当模板赋值后,在模板中的{$name}这类造型的数据。整个标签库的标签都是变量输出下的内容!!!
10、模板标签。【一套新语法主要体现在这,即在模板中插入的,这种造型的{xxx}玩意】
(1)模板中函数和运算符。
{$name|md5},也可以用以前的写法{:md5($name)},但tp的函数不能用后者方式??。(那么{:url()}也是这么来的喽)。函数:default,md5,raw,date,format,substr...(这里面有tp提供的函数,也有php内置的函数)。运算符:用了运算符就不能再用函数。这里使用的函数可以支持内置的PHP函数或者用户自定义函数,甚至是静态方法,所以助手函数url肯定也可以这样用喽。可以在模板中设置默认值以便没有赋值时使用{$name|default='achen'}。
(2)模板的循环标签。
{foreach $list as $key=>$value}将每一条数据拎出来,这条数据可能是字符串、数组或对象等,这里$key,$value都是自己起的名字,表示键和值。
{volist name='list' id='obj'},volist比foreach可实现的功能多,比如取奇偶组,但一般就用foreach,简单。'obj'是值,自己起的名字,$key没显示出来,但可以直接用,从0开始,如果用key,从1开始。
两种方式中,$要不要,比较乱,真想知道能不能加,看编译文件。自己按上面这样写就行了。
还有{for}标签。
注意:是这样编译的<?php echo htmlentities($obj['id']); ?>,这里是echo,所以如果从数据库中拿出了一行数据,不能直接写{$val},要写{$val.name}=>{$val.email}。
"模板赋值时,数组会降维",这句话应该是指用循环后会降一维,这是当然的,从二维数组拿出一条,当然成一维了,再用.号,再降一维,成字符串了。
(3)比较标签。{eq}内容{/eq} 相等才输出内容。还可以加入{else/}。还有{compare},而且像eq这些都可以统一用进去,type='eq'。
(4)定义变量。有种场景,在控制器中无法定义变量,需要在模板中定义,用assign。知道就行,用得时候再查,因为比较偏门。有时实在不知道标签了,就用原生<?php ?>。
(5)条件判断标签。switch,if,in,between,present,empty。
(6)包含文件。{include}。这里可以明确知道,这玩意是模板引擎的东西。这是一种老式的布局方式,PHP中也有这个。
(7)输出替换。
css和js放在public下面的原因可能是路径好弄,毕竟起始路径在这,所有的路径都是从index.php开始,所以和它同级的static目录可以直接开始,static/...。
这里的static前面到底加不加根目录/?如果加上,刷新,似乎可以,其实不是,“因为是配置文件,要先删除temp中的缓存,配置才起作用”【这句很重要】,再试,报错了。什么原因?为什么手册中有/呢,如果是网站顶级域名,可以加;是二级域名,加了报错,NND,原来原因在这。
./是本级,../是上一级。【再次强调:改配置,清编译】,也有人提出,html文件内容发生变化,就可以不清temp,随便写个123。
为了简化,可以把static/css整理打包。在template.php中,新增一个配置 'tpl_replace_string'=>['__JS__'=>'static/js,'__CSS__'=>'statc/css']。是关于模板的,就在template.php中配置。
(8)文件加载。传统css和js调用采用link和script,更加智能的是用{load}标签。{load href='__CSS__/basic.css'}。
(9)模板布局和继承。不爱用layout,设置太多,而且不灵活,强制先用layout.html,但有时个别模板不需要继承,必如弹窗“增加用户”,喜欢继承和包含文件混合用。
注意:自己把继承和include混用,有点四不像了,主流只用继承就行了!注意{__block__}的用法。
(10)模板标签怎么注释?{//...} {/*...*/}。
(11)原样输出。用{literal}包住=不解析,编译文件中直接删除。如果只用html的注释,只是表面的,编译文件中还能看到,还是会解析。
三、辅助知识
学完基础知识和核心知识已经可以做出站点,但以下辅助知识可以让站点更完善。
(一)路由
1、路由是用于规划(一般同时也会进行简化)请求的访问地址,在访问地址和实际操作方法之间建立一个路由规则=> 路由地址的映射关系。
2、ThinkPHP并非强制使用路由,默认开启路由(不能关闭)(默认设置中已经没有了url_route_on),如果一个URL没有定义路由,则采用默认的 PATH_INFO 模式访问URL。如果定义了一个路由,则只能通过路由规则访问该路由对应的路由地址,不能再采用PATH_INFO访问了(这里有个注意点,这句话有前提:路由定义时包含了完整的模块/控制器/方法,而不是只控制器/方法,这种情况下,定义了路由,就不能通过PATH_INFO模式访问了。如果定义路由时没写模块名,刚指向默认模块index,此时,通过路由和PATH_INFO模式都可以访问。这样的话,个人感觉定义路由应该把模块也写上,手册中有的没写,应该是单模块,app下直接是控制器目录),但其它路径(模块/控制器/方法)仍然可以通过PATH_INFO模式访问。一旦开启强制路由参数,则必须为每个请求定义路由(包括首页)。所以一般不开户强制路由。tp默认设置就是开启了路由,没开启强制路由。
3、使用路由有一定的性能损失,但随之也更加安全,因为每个路由都有自己的生效条件,如果不满足条件的请求是被过滤的。远比你在控制器的操作中进行各种判断要实用的多。可以理解为:如果只是为了美观,就别用了。
4、路由是应用开发中比较关键的一个环节,其主要作用包括但不限于让URL更规范以及优雅,还包括隐式传入额外请求参数、统一拦截并进行权限检查等操作、绑定请求数据、使用请求缓存、路由中间件支持( V5.1.6+ )的输出。
5、掌握路由主要是要掌握路由定义,其它环节(检测、解析、调度)是由系统自动完成的。路由定义:完成路由规则的定义和参数设置(5.1的路由定义采用了对象化的思维,相对5.0而言更直观)。
6、路由的主体规划和定义应该尽可能在应用开发前完成,在后期可以进行路由的参数调整和规则增补。
7、路由解析先从操作开始、然后控制器、然后模块。如果模块设置了默认或者绑定,可以在定义中不写模块。但这会发生2中的情况。
8、定义路由时的一些简要规则:
首页:Route::get('/','index');
静态地址:Route::get('ad','index/address/index');
静态动态结合的地址:Route::get('details/:id','address/details')
多参数静态动态结合的地址:Route::get('details/:id/:uid','address/details')
全动态地址:Route::get(':search/:id/:udi','address/search),可选参数的,规则完全匹配的,
9、路由的变量规则。系统默认的路由变量规则为\w+,即字母、数字和下划线。可通过pattern()方法设置。
Route::get('details/:id','index/address/details')->pattern('id','\d+')。更深入些,可以多变量设置,可以在Route.php中设置成通用的,闭包(route.php中的回调函数那个就是示例)。
10、路由的知识很很多,还有 路由缓存,路由参数,快捷路由,分组和注解,MISS和跨域请求,绑定和别名,资源路由,域名路由,路由的URL生成等。大致都了解了,暂时先不深入研究。
11、路由的URL生成,(效果就是把PATH_INFO生成一个更常见的URL形式),之前我们说的路由URL都是手动在地址栏中输入的,而路由也提供了一套生成方法,方便我们在超链接中使用URL。两种生成方式:静态方法和助手函数url。url('Blog/create',['id=>5,uid=>10],'shtml','127.0.0.1')
(二)静态代理
1、依赖注入。
依赖是一种关系,A类中实例化了B类,就称A类依赖于B类,或B类是A类的依赖。依赖注入是一种设计模式,不再在A类中实例化B类,而是将B类的实例化对象通过参数的形式传入A类中(可以作为构造函数的参数,也可以是方法的参数),作用是降低代码的耦合度(这样不需要在A类中写实例化B类的代码)。
2、容器。
(1)可能A类依赖的不只B类一个,而是依赖于很多类,就可以用一个容器把这些类的对象都装进去,容器就是一个装有各种类的对象的仓库,然后通过容器来实现对象的调用,进一步解耦。容器也是一个类,容器类,think\Container。大多数情况下都是使用其助手函数app()或者think\App类操作容器。
(2)依赖注入的类统一由容器进行管理,大多数情况下是自动绑定并且实例化的。不过你可以随时进行手动绑定类到容器中,支持多种绑定方式。系统已经内置绑定了核心常用类库,无需重复绑定。
3、Facade。
(1)虽然容器中的对象可以直接用了,但还是没有静态方法用的舒服。Facade为容器中的(动态)类提供了一个静态调用接口,相比于传统的静态方法调用,带来了更好的可测试性和扩展性,你可以为任何的非静态类库定义一个facade类。这可以叫静态代理。
(2)将以上的内容说的直白一点,Facade可以让类无需实例化而静态调用其方法(虽然这些方法本来不是静态的),只要是Facade中定义过的类就行。
(3)【“think\Db 类的实现本来就类似于Facade机制,所以不需要再进行静态代理就可以使用静态方法调用(确切的说 Db 类是没有方法的,都是调用的 Query 类的方法)”。怪不得在Facade目录中找不到Db,而且Db中也没有定义静态方法,却可以使用Db::name()。被这个问题困扰了好久!还好在手册中看到了这段红色说明。】
(4)tp5.1给常用的类都定义了Facade类库,即把think下的类大多数都在Facade中做了静态代理,用就是了。使用的时候注意引入路径,详见手册P53(Db还是用use think\Db。如果不想引入还可以使用其别名,详见P55。
(5)如果要给自定义的类使用静态代理。
方法一:在app下建common目录,自定义类Test;在app下建facade目录,建Test类,
class Test extends Facade{
protected static function getFacadeClass(){
return 'app\common\Test';
}
即完成了自定义类Test的静态代理,在控制器中可以采用Test::方法()的形式调用。注意控制器中要use app\facade\Test。
方法二:facade目录下的Test类这样定义:
class Test extends Facade{}
然后在app下的common.php中写上Facade::bind('app\facade\Test','app\common\Test'),即可完成静态代理的绑定。注意common.php中要use think\Facade。
(二)拦截
【钩子和行为】
1、“行为”是为了让你无需改动框架和应用,而在外围通过扩展或者配置来改变或者增加一些功能。而不同的行为之间也具有位置共同性,比如,有些行为的作用位置都是在应用执行前,有些行为都是在模板输出之后,我们把这些行为发生作用的位置称之为“钩子”,当应用程序运行到这个钩子的时候,就会被拦截下来,统一执行相关的行为,类似于 AOP 编程中的“切面”的概念,给某一个钩子绑定相关行为就成了一种类AOP编程的思想。直白点就是:在程序执行流程的某个点上,放一个钩子,触发某些行为,就如设个路障,查一下酒驾。
2、一个钩子可以注册多个行为,执行到某个钩子位置后,会按照注册的顺序依次执行相关的行为。但在某些特殊的情况下,你可以设置某个钩子只能执行一次行为,又或者你可以在一个钩子的某个行为中返回 false 来强制终止后续的行为执行;一个行为可以同时注册到多个不同的钩子上,完全看应用的需求来设计。
3、钩子的位置必须是事先设计好的,无论是框架还是应用的,要设置一个钩子,只需要在相关的位置添加一行代码(事先需要引入 think\facade\Hook 类)。
4、钩子和行为主要是tp框架自己在用,我们也可以自定义。
5、在app下建bahavior目录,建Test.php,然后在tags.php中绑定行为。
tags中已经有7个钩子,手册中有11个;
可以自定义钩子;
定义行为时,可以用run方法定义通用行为,或者用appInit等方法定义专用行为。
注意:行为中用return不行。
6、钩子和行为在6.0被废弃了,用事件取代。
7、common.php是公共函数定义文件,可以用于绑定facade
provider.php可以用于绑定容器
tags.php可以用于绑定行为
command.php是命令行定义文件
这些大致都是配置文件。
【中间件】
1、中间件和钩子行为有点类似,它主要用于拦截和过滤http请求,并进行相应处理。意思就是URL访问时,先通过中间件,允许了才能进入下一步。
2、主要用于重定向、权限验证等。像检测是支付宝还是微信。
3、使用中间件。
定义中间件:可以用命令行 php think make:middleware Check。app下会有http\Check.php。
注册中间件:在app下建middleware.php,里面写app\http\middleware\Check::class。(这个文件和provider.php、tags.php一样,是配置文件)。
注意:中间件必须返回Response对象。用return $next($request)。在返回对象前可以进行一些操作。
所以,就好理解了,中间件就像在执行URL前设置了一个钩子,并执行了一些行为。(钩子和行为当然可以实现同样的功能)
中间件的类是think\middleware。
4、前/后置中间件。
上面在操作之后再返回对象的叫前置中间件,就是在请求阶段拦截验证,如登录判断、跳转、权限等。
后置中间件就是请求完毕之后再执行中间件代码,比如写入日志等。
前置用得多。
5、路由中间件。用得多。
6、控制器中间件。必须继承基类。?这和控制器的前置操作有什么区别?
【事件】
(四)异常处理&日志处理
1、在think下有Exception类和Log类。
2、和PHP默认的异常处理不同,tp抛出的不是单纯的错误信息,而是一个人性化的错误页面。调试模式下,会展示异常页面,包括错误码和错误类型,如[0]HttpException。如果在部署模式下,只会显示“页面错误,请稍后再试”。
3、Exception类虽然没有Facade形式,但和Db一样也在think目录下有个单独文件夹,估计和Db一样基于Facade实现原理。
4、日志记录和写入由think\Log类完成,通常使用Facade形式。由于日志记录了所有的运行错误,要养成经常看日志文件的习惯,可以避免和及早发现很多的错误隐患。
(五)验证器
1、tp5.1推荐使用验证器进行数据验证(也支持think\Validate类进行独立验证),当然是用验证器了。
2、为具体的验证场景或者数据表定义好验证器类,直接调用其check方法即可完成验证。
3、验证器类中一是验证规则,二是错误提示信息。如果没有定义错误提示信息,会使用系统默认的提示信息。
4、系统提供了一些常用的规则,如果不能满足需求,可以自己定义。一般都够用了。
5、验证规则支持对表单的令牌验证。
6、一般新增数据的时候需要前后端同时验证,后端是第二道防线。
7、当有多个数据都不合格的时候,默认验证第一个就返回,不再验证后面的,并返回第一个的错误提示。如果需要全提示,加上protected $batchValidate=true。但测试发现,这种只在$this->validate起作用,在new Validate不起作用。
8、可以将错误提示以异常形式显示。
9、Facade模式非常适合单个数据验证。
(六)session&cookie
(七)多语言
(八)分页
(九)上传
(十)缓存
(十一)验证码
(十二)图像处理
(十三)调试
(十四)安全和性能
(十五)命令行
(十六)关联模型
【零散的知识点】
1、现在基本都是B2C网站,或者只有后台模块,B2C2B的商家网站基本被垄断了,没啥需求,暂时不练习这种模式(其实也就多了一个商家模块)。
2、关于tp版本的选择,学习用最新版本(哪怕只是核心版),上线用最新的完整版。自己现在用5.0完整版就行了。
3、“tp5最有用的是多层MVC”,这句话是指在网站中以插件形式实现团购、秒杀等功能,如唯品会,客户按需安装。站点中在app目录下增加一个app_extend模块
4、统计模块,如统计浏览量、上稿数等,用reids,不然与数据库交互太多
5、关于优化(详见B站优化视频),目前只知道以下这些:
(1)reids可以做缓存优化,像三级分类这种就需要缓存优化。
(2)除了reids,还可以用php序列化serialize起到同样作用。
(3)缓存后,就不用每次都调用数据库了。
(4)runtime文件夹下是不是存放着各种方式做出的缓存文件?
(5)除了缓存优化,还有mysql索引优化,利用二叉树方法或哈希方法提高查询速度。
(6)主键不需要建索引,其它字段都可以建,name是唯一的,可以建唯一索引,pwd建普通索引,content可以建全文本索引,一般用BTREE,不用HASH。
(7)应该是:数据表很大时,才建索引。
6、ASP没必要学,微软的,除了简单没有任何优点;PHP当然得学,各种理由适合现在的自己;JSP除了难其它全是优点,但现在还不合适自己。
8、助手函数。系统为一些常用的操作提供了助手函数支持,但核心框架本身并不依赖任何助手函数。使用助手函数和性能并无直接影响,只是某些时候无法享受IDE自动提醒的便利,但是否使用助手函数看项目自身规范,在应用的公共函数文件中也可以对系统提供的助手函数进行重写。
9、tp5.1安装。用composer方式,可靠。注意windows下先安装composer,再按手册运行命令。
10、开启trace可以查看原生SQL。如果return json($data),显示出的不是Html文档,所以连trace图标都没有。查看源代码的话也是json.
11、以前没模板技术时,只能采用<?php ?>的方式将PHP与HTML糅合。有空的时候找一部原生PHP视频看看, 有助于深入理解。
12、在模板中,在href和action中地址到底怎么写?
写方法名的话,需要写全模块和控制器,如action="index/user/json"。如果不写全,就报错,这里是从模块开始解析的,跟路由相反。
如果用url函数的话,不需要写全,即如果在相同控制器下,只写方法就行,如action="{:url('hello')}"。
暂时只验证出这些,没测试在方法中跳转地址是否也是这样!
注意:url是tp的助手函数;而这里的用法属于“模板变量输出”的内容,更详细的可参考手册中“模板变量输出”
通过百度:
<form>的action和<a>的href中到底写什么?当然是写URL!
URL可能的值:
绝对URL:指向其它站点,如"http://www.baidu.com/"
相对URL:指向站点内的文件,如"index/index/hello",自己开必的站点,一般内部用相对地址。
上面这种相对地址的写法就是PATH_INFO模式,必然可以链接成功。而tp提供了更好的方式,即url函数(3.2版本时叫U方法),格式如{:url('hello')}。
这属于模板变量输出的内容,同时也属于路由生成URL的内容。变量输出时使用了函数,理论上写可以写{'hello'|url}。
使用url函数的好处在于,一旦环境变化或者参数设置改变,不需要更改模板中的任何代码。
!!!先不管路由不路由,即使出于“不用写全模块和控制器”的角度,自己也该在模板中使用url方法。而且在控制器的方法中也应该使用URL生成。
13、AJAX。
ajax全称是asynchronos javascript and xml,是一种前后端数据交互的手段。
原生的ajax实现比较麻烦,需要借助xmlHttpRequest对象来构建,分五步。
可以使用jQuery封装好的$.13、AJAX。
get无参请求,一般用来向后台获取数据,不会发送任何内容给后台。
get带参请求,一般给后台发送id等信息,请求指定数据,参数一般不大。
post请求,一般用于登陆注册保密性较高的地方。
14、tp中的很多操作都有多种方法。一定要挑一种自己喜欢且熟练的,而且只记一种。