ThinkPHP8开发规范完整版_第1页
ThinkPHP8开发规范完整版_第2页
ThinkPHP8开发规范完整版_第3页
ThinkPHP8开发规范完整版_第4页
ThinkPHP8开发规范完整版_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

ThinkPHP8开发规范PHP版本选择如果是新的项目,目前应该尽量选择PHP8.2+作为你的PHP版本,可以拥有更好的性能,ThinkPHP8.0版本的最低版本要求是PHP8.0。有些PHP扩展可能不支持PHP的高版本,这个时候你要做出选择,使用低版本还是寻求更好的扩展解决方案。基本命名规范ThinkPHP遵循PSR-2命名规范以及PSR-4自动加载规范,并注意如下规范:如果你没有遵循某些规范,可能会导致部分功能的异常。类(包括接口、rait)文件名和类名保持一致,并且使用首字母大写的驼峰命名;函数文件、配置文件、路由定义文件等文件名使用小写规范;无论类还是普通文件都使用.php后缀;目录名统一使用小写规范,并且使用单数规范;模板文件使用小写规范;配置参数名统一使用小写规范;常量定义统一使用大写规范;环境变量定义统一使用大写规范;\hThinkPHP知识库函数的命名使用小写字母和下划线(小写字母开头)的方式,例如get_client_ip;方法的命名使用驼峰法(首字母小写),例如getUserName;属性的命名使用驼峰法(首字母小写),例如tableName、instance;特例:以双下划线__打头的函数或方法作为魔术方法,例如__call和__callStatic;使⽤统⼀的IDE以及代码规范配置或者插件项目团队应当尽量使用统一的IDE作为开发工具,并规范一致的代码规范配置项,如果使用的第三方代码规范及自动完成插件。如果团队成员较多而无法完全统一,最低限度,项目代码风格必须遵循PSR-1和PSR-2规范。助⼿函数助手函数的初衷是为了简化代码和更方便记忆,但如果不是很清楚助手函数的内部实现原理,很容易导致滥用,由于现代的IDE提示和自动完成功能之强大,助手函数的作用非常有限,而且只会用助手函数对于框架的原理认识较浅,因此建议是掌握助手函数的内部实现原理后再来决定在项目规范中是否需要使用助手函数,以及如何使用。毕竟有些场景下,助手函数是非常简单实用的,例如:publicfunctiongetUser($id){$user=User::getOrEmpty($id);returnjson($user);}产品交付给客户的时候,有些时候助手函数能够让客户自定义模板的时候更方便。如果你需要额外定义或者覆盖原有的助手函数,可以直接在应用的common.php公共文件中定义。配置规范线上环境和本地测试环境应该使用一致的配置文件,差异化的配置使用环境变量方式处理。本地环境可以通过定义.env文件(注意添加到忽略文件列表)来模拟环境变量。在你需要差异化配置的参数中使用env函数定义,例如:\hThinkPHP知识库然后在环境变量中或者本地.env中定义DB_HOST=2尽量不要在配置文件以外使用env函数获取配置参数。统一使用config函数获取配置参数。除了定义配置文件之外,避免使用动态配置功能,保持仅读取配置参数的良好习惯。如果需要提高配置文件的性能,可以考虑使用Yaconf扩展。安装composertopthink/think-yaconf⽇志规范日志记录建议直接使用PSR-3规范提供的接口方法记录,例如:Log::record('测试日志','error');Log::record('测试日志','info');应当改为Log::error('测试日志');Log::info('测试日志');支持的方法包括debug, info, notice, warning, error, critical, alert,emergency以及用于SQL日志记录的sql方法。默认情况下,日志是延时写入的,如果要实时写入日志信息,可以使用write方法Log::wirte('测试日志','info');确保设置日志的最大数量限制,避免日志空间过大导致存储空间占满。\hThinkPHP知识库超过设置的数量后,最早的日志将会被自动清理。如果需要把日志接入阿里云,可以设置为单一日志文件,具体可以参考\h:thinkphp日志接入阿里\h云日志系统规范部署请务必把你的WEB根目录指向public目录而不是应用根目录,并且不要随意更改入口文件的位置。public目录下面不要放除了入口文件和资源文件以外的其它应用文件。保持测试环境和部署环境的⼀致性在开发过程中,应该尽量保持你的测试环境和正式部署环境的一致性,包括运行环境和版本,无论在本地测试环境还是部署环境,都应当统一使用域名方式访问,本地可以使用测试域名,例如你的正式部署域名为,那么本地测试环境可以使用thinkphp或者thinkphp.test作为测试域名,避免使用localhost或者这种测试地址。对于有多个域名的部署应用,本地也要尽量模拟多个域名。关闭调试模式在部署到生产环境的时候,确保你已经关闭了调试模式,可以通过修改环境变量的方式关闭调试模式。APP_DEBUG=false无论是本地开发还是生产环境部署,都建议保持统一的配置文件,然后通过修改环境变量的方式(本地开发可以通过定义.env文件)设置区别部分。关闭调试模式后,系统的健康状态和运行监控主要依靠日志或者你使用的监控服务。所以,要养成定时检查日志和运行状态的习惯。部署忽略清单项目根目录下面有一个.gitignore文件,用于定义提交版本库的时候哪些文件或者目录需要忽略,设置忽略的文件不会被同步到远程服务器,只是用于本地开发。\hThinkPHP知识库/.idea/.vscode/vendor*.log.env项目使用的核心框架以及composer安装的扩展,不应当被同步到版本库中,只需要同步composer.json以及composer.lock文件。然后在服务器端进行composer更新。做好应⽤优化⼯作参考\h如何有效提高ThinkPHP的应用性能一文做好相关优化工作。使⽤持续集成/持续部署构建你的项⽬如果条件允许,请使用\h持续集成/持续部署,并添加自动化测试。\hTravisCI或\h者PHPCI都是不错的选择。编写项⽬文档每个项目都应该在根目录添加readme.md文件,并遵循Markdown规范写作,对项目做简要的说明(尤其是目录和代码规范),如果项目比较复杂,可以附上一个项目详细说明或者规范的文档地址(托管到\h顶想云知识管理上是一个很好的选择),如果你的项目是前后端完全分离开发的话,应该事先规划好后台的API接口,然后创建一个API文档,便于指导前端开发人员进行接口调用,以及方便在线调试。路由规范所有的路由规则是不支持普通URL参数的,必须是PATHINFO地址。路由定义文件不一定是route.php,事实上可以是任何文件名。如果你开启了路由强制模式,那么未定义的路由访问将会抛出异常。统一使用路由方法注册路由而不要再使用返回数组配置,路由规则不区分大小写,因此统一使用小写定义。尽量避免使用闭包定义路由规则(注意不要和分组路由的闭包定义搞混淆),优先使用资源路由定义,在不适用资源路由的情况下也要多使用路由分组,不仅可以简化路由定义和提高性能,也\hThinkPHP知识库大部分情况下,建议开启全局路由完整匹配,个别不需要完整匹配的路由规则可以在定义的时候使用completeMatch方法单独关闭。Route::get('user/<name>','user/info')->completeMatch(false);如果需要使用伪静态地址,可以全局配置URL访问后缀,对于个别特殊后缀的路由可以在路由定义的时候单独指定。Route::get('hello/<name>','index/hello')->ext('htm');明确你的路由变量规则,不要忽略路由变量的规则定义,避免可能的解析错误。例如,当你的路由变量中使用了小数点或者斜线的情况,必须严格定义你的变量规则。优先在路由定义的时候指定中间件、进行数据验证和请求缓存等操作,原则就是在路由里面能做的事情尽量提前不要等到控制器里面才执行。为了方便查看当前项目定义的路由规则,可以使用下面的指令生成路由规则查看文件。phpthinkroute:list然后可以在runtime目录下的route_list.php可以查看所有的路由列表。关闭路由如果某个应用需要关闭路由功能,可以在应用的app.php配置文件中定义'with_route' => false,但需要注意的是,即使关闭路由,也会走默认路由(即控制器/操作)解析,因此路由全局中间件仍然有效。明确定义请求类型尽量明确定义路由的请求类型,提高路由解析的效率。推荐使用:\hThinkPHP知识库替代:Route::rule('hello/:name','index/hello');不需要添加开头斜线除了首页外,其它路由规则定义不需要添加开头的斜线。推荐使用:Route::get('hello/:name','index/hello');不建议使用:Route::get('/hello/:name','index/hello');除非是首页路由Route::get('/','index/index');或者直接访问分组名的情况Route::group('blog',function(){Route::get('/','index/blog');Route::get(':id$','blog/read');Route::get(':id/edit$','blog/edit');});多使⽤路由分组可能的情况下,尽可能多使用路由分组。可以充分利用分组的匹配机制提高路由解析性能。推荐使用\hThinkPHP知识库Route::get(':id/edit$','blog/edit');});不建议使用Route::get('blog/:id$','blog/read');Route::get('blog/:id/edit$','blog/edit');路由分组传入额外参数可以统一给分组路由传入额外的参数Route::group('blog',function(){Route::get(':id$','blog/read');Route::get(':id/edit$','blog/edit');})->ext('html')->pattern(['id'=>'\d+'])->append(['group_id'=>1]);上面的分组路由统一传入了group_id参数,该参数的值可以通过Request类的param方法获取。路由变量定义规范对于新版的路由变量定义来说,不再区分普通变量和组合变量,哪怕你使用:name方式定义,内部也会统一转换成<name>这种方式,因此为了提高路由解析性能,建议统一使用:Route::group('blog',function(){Route::get('<id>$','blog/read');Route::get('<id>/edit$','blog/edit');});尽量明确定义路由变量的规则Route::group('blog',function(){Route::get('<id>$','blog/read');\hThinkPHP知识库路由变量规则采用正则表达式方式定义,但无需在开头添加^或者在最后添加$,也无需使用模式修饰符,系统会自动添加。不能使用$_GET方法或者Request类的get方法获取路由变量,而应该使用param方法或者参数绑定。如果开启了路由合并解析的话,分组下面的多个路由规则是通过一次解析匹配完成的,如果路由规则较多性能会有大幅提升。变量分隔符你可以很随意的使用路由变量分隔符,只要注意不要和你的变量规则冲突。Route::get('item/<date><name>-<id>','product/item')->pattern(['date'=>'\d{8}','name'=>'\w+','id'=>'\d+']);默认路由变量规则默认情况下,如果你没有定义变量规则,则使用\w+作为变量规则Route::get('hello/<name>','index/hello');其实等同于Route::get('hello/<name>','index/hello')->pattern(['name' => '\w+',]);如果你希望改变默认的路由变量规则,可以在应用配置文件中设置//默认的路由变量规则'default_route_pattern'=>'[a-z0-9\-\_\.]+',路由完全匹配\hThinkPHP知识库所以Route::get('hello/:name','index/hello');可以匹配下面的URL地址hello/thinkhello/think/thinkphphello/think/thinkphp/demo但如果添加了路由完全匹配后Route::get('hello/:name$','index/hello');上面的三个URL地址中就只会匹配hello/think建议你开启全局路由完全匹配//路由完全匹配'route_complete_match'=>true,如果有个别路由仍然希望不要完全匹配,你可以使用Route::get('hello/:name','index/hello')->completeMatch(false);关闭当前路由规则(或者路由分组)的完整匹配。定义路由标识路由标识的作用只是用于URL地址生成,而且默认会使用当前的路由地址作为路由标识。\hThinkPHP知识库如果指定了路由标识的话,url方法的用法就需要调整为:Route::get('hello/:name','index/hello')->name('hello');echourl('hello',['name'=>'think']);如果你希望简化URL地址的生成调用,可以在项目规范中强制统一规范,而不要使用URL地址这种默认标识,使用路由标识的优势是即使路由地址发生了变化,也无需改变url方法的代码。⽤⽅法定义路由参数出于语义化考虑,路由参数建议使用方法而不是参数。推荐使用(支持IDE)Route::get('new/:id','News/read')->ext('html')->https();不建议使用:Route::get('new/:id','News/read')->option(['ext'=>'html','https'=>true]);MISS路由一旦你设置了全局的MISS路由,相当于开启了强制路由模式。Route::miss('public/miss');MISS路由可以针对不同路由分组(或者域名)设置,也可以针对不同的请求类型设置,Route::group('blog',function(){Route::get(':name$','blog/read');...Route::miss('blog/error','get');\hThinkPHP知识库Route::group('user',function(){Route::get(':id','user/info');...Route::miss('user/error');});控制器规范为了避免命名冲突,可以在路由配置文件中统一开启控制器类库后缀。'controller_suffix' =>true,优先使用资源控制器,可以通过命令行快速生成一个资源控制器类phpthinkmake:controllerBlog控制器建议继承一个公共的控制器类,便于统一调整和增加通用逻辑。默认安装后,系统提供了一个app\BaseController实例基础控制器类,你可以根据自己项目的需求进行调整,包括改变命名空间。对于控制器操作方法的拦截以及统一处理应当使用中间件独立操作,原来的初始化方法已经废弃。控制器中间件不需要继承任何的基础控制器类即可使用,仅仅需要你定义middleware属性即可。控制器的代码应当尽量少,以确保逻辑清晰和可读性。并始终保持controller层作为访问控制器层的名称。请求数据的验证操作统一使用验证器进行验证。操作方法中的对象使用依赖注入,其它的必要参数使用参数自动绑定。不要在操作方法中输出除了调试信息之外的任何内容,而是通过return返回需要输出的内容。操作方法中始终明确响应输出的类型,默认的return方式使用的是HTML输出类型。命名规范\hThinkPHP知识库数据表和字段采用小写加下划线方式命名,例如think_user表和user_name字段,禁止使用驼峰、中文或者拼音作为数据表及字段命名。主键统一使用id;外键统一使用resource_id形式(例如user_id);模型数据字段统一使用小写+下划线命名,和数据表字段规范一致;数据表统一添加系统时间字段(create_time和update_time),并使用datetime类型;使用软删除并添加时间字段delete_time,类型和系统时间字段保持一致;模型类应该继承一个统一的公共类,便于调整和统一设置;模型类应当通过定义autoWriteTimestamp属性明确时间字段类型;查询规范不要在数据库配置文件以外的地方配置或者动态设置数据库连接信息,包括模型内部。尽量不使用原生SQL查询,而应当使用查询构造器。不要使用任何数据库工具创建、修改数据表和填充数据,应当使用\h数据迁移并同步版本库给所有成员。每次数据查询都用Db类或者模型类的静态方法。避免在模型方法中直接写复杂的查询条件,而应当使用查询范围或者搜索器统一定义后调用。用查询表达式方式替代传统的数组查询。查询数据的处理统一使用获取器定义,而不要直接处理数据。对写入数据需要额外处理的话统一使用修改器。对于使用了SQL函数的用法,使用fieldRaw、orderRaw和whereRaw/whereExp替代field、order和where用法。仅在使用字符串查询条件,以及调用whereExp和whereRaw方法的时候需要使用手动参数绑定,其余情况下都会自动进行参数绑定,禁止手动调用bind方法。\hThinkPHP知识库查询值为Null的数据查询值为Null的数据应当使用whereNull或者whereNotNull方法//查询email为空,并且name不为空的用户数据User::whereNull('email')->whereNotNull('name')->select();使⽤快捷⽅法对于一些常用的查询,尽量使用系统封装的快捷查询方法,例如:User::whereIn('id',[1,2,3])->whereLike('name','think%')->select();相当于下面的查询User::where('id','in',[1,2,3])->where('name','like','think%')->select();更多的方法可以参考官方手册或者使用IDE的自动提示。获取字段值和列数据对于一些简单的数据获取,你完全不需要查询整个表的数据,例如查询某个字段(满足条件的)值或者列数据。//获取id为10的用户名称User::where('id',10)->value('name');//获取状态为1的用户名称列表User::where('status',1)\hThinkPHP知识库//获取分数大于80的用户分数列表,以用户ID为索引User::where('score','>',80)->column('score','id');聚合查询如果你的min/max查询的是一个字符串类型字段,记得加上第二个参数并传入false。//获取name字段的最大值User::max('name',false);时间区间查询时间查询主要用于时间字段的区间查询,whereTime方法的优势是支持自动识别时间字段类型并进行转换处理。//大于某个时间User::whereTime('birthday','>=','2008-10-1')->select();//小于某个时间User::whereTime('birthday','<','2000-10-1')->select();//时间区间查询User::whereBetweenTime('birthday','1990-10-1','2000-10-1')->select();//不在某个时间区间User::whereNotBetweenTime('birthday','1970-10-1','2000-10-1')->select();年/⽉/⽇/周查询对于年/月/日/周的时间查询,推荐使用whereYear/whereMonth/whereDay/whereWeek方法查询,例如://查询本月注册的用户Db::name('user')->whereMonth('create_time')\hThinkPHP知识库//查询上月注册用户Db::name('user')->whereMonth('create_time','lastmonth')->select();//查询2018年6月注册的用户Db::name('user')->whereMonth('create_time','2018-06')->select();//查询当天注册的用户Db::name('user')->whereDay('create_time')->select();//查询昨天注册的用户Db::name('user')->whereDay('create_time','yesterday')->select();//查询2018年6月1日注册的用户Db::name('user')->whereDay('create_time','2018-06-01')->select();时间表达式查询高级的时间表达式查询可以使用PHP的\h相对时间格式,例如://查询两天以内的博客Blog::whereTime('create_time','-2days')->select();//查询昨天中午后发的博客Blog::whereTime('create_time','yesterdaynoon')->select();更多的时间表达式查询你可以自由发挥。时间字段范围查询\hThinkPHP知识库//查询有效期内的活动Event::whereBetweenTimeField('start_time','end_time')->select();//查询没有开始或者已经过期的活动Event::whereNotBetweenTimeField('start_time','end_time')->select();字段比较可以直接比较两个字段的大小进行查询User::whereColumn('update_time','>','create_time')->select();User::whereColumn('score1','>','score2')->select();如果需要比较两个字段相同,可以使用User::whereColumn('score1','score2')->select();条件查询应当使用条件查询替代在组装查询条件的时候写大量的if和else。User::when($condition,function($query){//满足条件后执行$query->where('score','>',80)->limit(10);})->select();并且支持不满足条件的分支查询,并且支持多次调用when方法。User::when($condition,function($query){//满足条件后执行\hThinkPHP知识库//不满足条件执行$query->where('score','>',60);})->select();JSON查询如果你的字段类型使用的是JSON类型,那么可以直接使用框架提供的JSON查询支持。User::where('info->nickname','ThinkPHP')->find();注意,需要在模型里面定义JSON字段属性。<?phpnamespaceapp\index\model;usethink\Model;classUserextendsModel{//设置json类型字段protected$json=['info'];}如果使用Db查询的话,可以改为$user=Db::name('user')->json(['info'])->where('info->nickname','ThinkPHP')->find();SQL函数查询如果需要对某个字段使用SQL函数表达式查询,可以使用User::whereExp('nickname',"=CONCAT(name,'-',id)")->whereRaw('LEFT(nickname,5)=?',['think'])\hThinkPHP知识库注意whereExp和whereRaw方法的区别,前者是对某个字段使用SQL函数表达式,后者是整个查询就是一个SQL函数表达式。字段递增/递减可以使用://博客的阅读数递增1评论数递减2Blog::where('id',10)->inc('read_count')->dec('comment_count',2)->save();新版已经取消了延时更新功能。指定字段值排序如果你需要按照指定字段的值的顺序来排序,可以使用User::where('status',1)->orderField('id',[1,2,3])->select();从主库读取如果你使用了数据库的主从分离,当刚写入数据后,数据库的主从同步可能还没来得及同步,这个时候立刻查询数据可能会出错,你可以使用下面的方法从主库读取。$user=User::create($data);$user->readMaster()->select();你可以全局配置数据写入后自动读取主库\hThinkPHP知识库获取⾃增ID使用Db类的insert方法或者模型的save方法返回的不是自增主键,不过你可以使用。$userId=Db::name('user')->insertGetId($data);如果使用模型的话,自增主键的值会自动赋值给模型对象,可以直接获取。$user=User::create($data);echo$user->id;模型查询为空的处理模型查询数据不存在的话返回值为Null,所以必须要添加返回值判断然后进行数据处理,建议使用下面的方法查询,如果数据不存在则返回空的模型对象。//始终返回模型对象$user=User::where('id',10)->findOrEmpty();⾃动分批写入如果你需要一次写入大量数据,建议使用limit方法自动分批多次写入。//自动分批多次写入数据库每次最多写入1000条Db::name('user')->limit(1000)->insertAll($dataList);如果是使用模型的话,建议直接使用saveAll方法而不需要limit方法。$user=newUser;$user->saveAll($dataList);\hThinkPHP知识库对于大量数据的处理操作,建议使用chunk分批处理方法。//每次处理100个数据User::chunk(100,function($users){foreach($usersas$user){//处理数据}});游标查询对于内存开销比较大的应用,在做大量数据查询和处理的时候,建议使用cursor方法进行游标查询,可以利用PHP的生成器特性,减少内存占用。$cursor=User::cursor();foreach($cursoras$user){//处理数据}关联查询关联方法定义应该始终使用小驼峰规范,但关联查询的时候支持驼峰或者小写加下划线方法,但区别在于关联属性的名称不同。是否需要模型分层一般情况下,仅仅使用Model层已经够用,但如果项目比较大,建议对模型进行分层,例如使用数据层、逻辑层和服务层等等,视项目需求而定,原则就是避免某一层过大导致结构杂乱,尽量让各个层分工明确,各司其职。请求变量过滤永远不要相信用户的输入,这是一句至理名言。尽可能的过滤请求变量能有效防范大部分的漏洞和隐患。框架建议的获取请求变量的方法是Request类的param方法(如非必要不要再使用get或者post方法获取,更不要使用原生的$_GET/$_POST等方法获取)。\hThinkPHP知识库$name=$request->param('name');//在这里可以根据你的业务需求进行更严谨的过滤//例如$name=$request->param('name','','htmlentities,strtolower');//或者使用验证器进行专门的验证}对于有明确类型的请求变量,可以在使用param方法的时候使用类型强制转换,例如:publicfunctionindex(Request$request){//强制转换字符串数据$name=$request->param('name/s');//强制转换整型数据$name=$request->param('id/d');//强制转换浮点型数据$name=$request->param('score/f');}或者直接使用方法参数获取请求变量publicfunctionindex(string$name){//在这里可以根据你的业务需求进行更严谨的过滤//或者使用验证器进行专门的验证}如果你需要对所有数据进行处理,可以设置全局的过滤方法。对不同的应用需求设置default_filter过滤规则(默认没有任何过滤规则),常见的安全过滤函数包括stripslashes、htmlentities、htmlspecialchars和strip_tags等,请根据业务场景选择最合适的过滤方法。如果需要获取多个数据,建议使用only方法指定需要获取的变量名称,避免有些不怀好意的数据提交导致权限问题。\hThinkPHP知识库//指定表单数据名称$data=$request->only(['name','title']);}当你使用数据库或者模型操作写入数据的时候,也可以指定字段,避免非法和不希望的字段写入数据库。//模型User::a

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论