一. 前言
二. 项目规范
2.1 Laravel版本选择
以下是 Laravel 的版本路线图:
版本 | 发布日期 | 版本类型 | 维护周期 |
---|---|---|---|
Laravel 5.1 | 2015 年 6 月 | LTS 长久支持 | Bug 修复 2017 年 6 月份,安全修复 2018 年 6 月份 |
Laravel 5.2 | 2015 年 12 月 | 一般发行 | 提供 6 个月的 Bug 修复支持,一年的安全修复支持 |
Laravel 5.3 | 2016 年 8 月 | 一般发行 | 提供 6 个月的 Bug 修复支持,一年的安全修复支持 |
Laravel 5.4 | 2017 年 1 月 | 一般发行 | 提供 6 个月的 Bug 修复支持,一年的安全修复支持 |
Laravel 5.5 | 2017 年 8 月 | LTS 长久支持 | Bug 修复 2019 年 8 月份,安全修复 2020 年 8 月份 |
选择 Laravel 版本时,应该 优先考虑 LTS 版本,因为安全性和稳定性考虑,商业项目开发中 不应该 使用最新版本的 『Laravel 一般发行版』 。扩展阅读:如何选择 Laravel 框架版本。
请使用以下命令来创建指定版本的 Laravel 项目:
composer create-project laravel/laravel project-name --prefer-dist "5.5.*"
2.2 开发和线上环境
2.3 开发专用扩展包
2.4 配置信息与环境变量
假如我们有个『CDN 域名』的变量,在 Laravel 中有以下几种方法:
- 硬代码,直接写死。- ❌ 可维护性低
- 写死在
config/app.php
文件中。 - ❌ 无法区分环境进行配置 - 存储于
.env
文件中,使用env()
方法直接读取。 - ❌ 虽然解决了环境变量问题但是不推荐 - 存储在
.env
和config/app.php
文件中,然后使用config()
函数来读取。- ✅ 最佳实践
代码示例
.env
文件中设置:
CDN_DOMAIN=cdndomain.com
config/app.php
文件中设置:
'cdn_domain' => env('CDN_DOMAIN', null),
程序中两种获取 相同配置
的方法:
env('CDN_DOMAIN')
config('app.cdn_domain')
在此统一规定:所有程序配置信息 必须 通过 config()
来读取,所有的 .env
配置信息 必须 通过 config()
来读取,绝不 在配置文件以外的范围使用 env()
。
2.5 辅助函数
Laravel 提供了很多 辅助函数,有时候我们也需要创建自己的辅助函数。
必须 把所有的『自定义辅助函数』存放于 bootstrap
文件夹中。
并在 bootstrap/app.php
文件的最顶部进行加载:
<?php
require __DIR__ . '/helpers.php';
...
2.6 项目文档编写规范
文档页面排版 必须 遵循 中文文案排版指北 ,在此基础上。
2.7 工具统一
三. 编码规范
3.1 代码风格
代码风格 必须 严格遵循 PSR-2 规范。
PhpStorm设置PSR-2代码规范,具体操作如下:
Preferences -> Editor -> Code style[PHP] -> Set From -> Predefined Style[PSR1/PSR2]
3.2 路由器
路由闭包:
绝不 在路由配置文件里书写『闭包路由』或者其他业务逻辑代码,因为一旦使用将无法使用 路由缓存 。
路由器要保持干净整洁,绝不 放置除路由配置以外的其他程序逻辑。
路由命名:
必须 优先使用 Restful 路由,配合资源控制器使用,见 文档。
超出 Restful 路由的,应该 模仿上图的方式来定义路由
3.3 数据模型
位置:
所有的数据模型文件,都 必须 存放在:app/Models/
文件夹中。
命名空间:
namespace App\Models;
使用基类:
所有的 Eloquent 数据模型 都 必须 继承统一的基类 App/Models/Model
,此基类存放位置为 /app/Models/Model.php
,内容参考以下:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model as EloquentModel;
class Model extends EloquentModel
{
public function scopeRecent($query)
{
return $query->orderBy('created_at', 'desc');
}
}
以 Photo 数据模型作为例子继承 Model 基类:
<?php
namespace App\Models;
class Photo extends Model
{
protected $fillable = ['id', 'user_id'];
public function user()
{
return $this->belongsTo(User::class);
}
}
命名规范:
- 数据模型类名
必须
为「单数」, 如:App\Models\Photo
- 类文件名
必须
为「单数」,如:app/Models/Photo.php
- 数据库表名字
必须
为「复数」,多个单词情况下使用「Snake Case」 如:photos
,my_photos
- 数据库表迁移名字
必须
为「复数」,如:2014_08_08_234417_create_photos_table.php
- 数据填充文件名
必须
为「复数」,如:PhotosTableSeeder.php
- 数据库字段名
必须
为「Snake Case」,如:view_count
,is_vip
- 数据库表主键
必须
为「id」 - 数据库表外键
必须
为「resource_id」,如:user_id
,post_id
- 数据模型变量
必须
为「resource_id」,如:$user_id
,$post_id
关于SQL文件:
- 绝不 使用命令行或者 PHPMyAdmin 直接创建索引或表。必须 使用 数据库迁移 去创建表结构,并提交版本控制器中;
- 绝不 为了共享对数据库更改就直接导出 SQL,所有修改都 必须 使用 数据库迁移 ,并提交版本控制器中;
- 绝不 直接向数据库手动写入伪造的测试数据。必须 使用 数据填充 来插入假数据,并提交版本控制器中。
全局作用域:
Laravel 的 Model 全局作用域 允许我们为给定模型的所有查询添加默认的条件约束。
所有的全局作用域都 必须 统一使用 闭包定义全局作用域
,如下:
/**
* 数据模型的启动方法
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope('age', function(Builder $builder) {
$builder->where('age', '>', 200);
});
}
3.4 控制器
必须 使用资源的复数形式,如:
- 类名:PhotosController
- 文件名:PhotosController.php
3.5 视图
https://fsdhub.com/books/laravel-specification/506/view
3.6 表单验证
https://fsdhub.com/books/laravel-specification/507/form-validation
3.7 授权策略
授权策略:
必须 使用 授权策略 类来做用户授权。
https://fsdhub.com/books/laravel-specification/508/policy
3.8 数据填充
factory 辅助函数:
必须
使用 factory
方法来做数据填充,因为是框架提倡的,并且可以同时为测试代码服务。
运行效率:
开发数据填充时,必须
特别注意 php artisan db:seed
的运行效率,否则随着项目的代码量越来越大,db:seed
的运行时间会变得越来越长,有些项目多达几分钟甚至几十分钟。
原则是:
Keep it lighting speed.
只有当 db:seed
运行起来很快的时候,才能完全利用数据填充工具带来的便利,而不是累赘。
批量入库:
所有假数据入库操作,都 必须 是批量操作,配合 factory
使用以下方法:
$users = factory(User::class)->times(1000)->make();
User::insert($users->toArray());
以上只执行一条数据库语句,推荐阅读 大批量假数据填充的正确方法。
3.9 日期和时间
必须 使用 Carbon 来处理日期和时间相关的操作。
Laravel 5.1 中文的 diffForHumans
可以使用 jenssegers/date。
Laravel 5.3 及以上版本的 diffForHumans
,只需要在 config/app.php
文件中配置 locale
选项即可 :
'locale' => 'zh-CN',
四. 其他
4.1 Laravel 安全实践
关闭DEBUG:
Laravel Debug 开启时,会暴露很多能被黑客利用的服务器信息,所以,生产环境下请 必须 确保:
APP_DEBUG=false
防XSS:
跨站脚本攻击(cross-site scripting,简称 XSS),具体危害体现在黑客能控制你网站页面,包括使用 JS 盗取 Cookie 等,关于 XSS 的介绍请前往 IBM 文档库:跨站点脚本攻击深入解析 。
默认情况下,在无法保证用户提交内容是 100% 安全的情况下,必须 使用 Blade 模板引擎的 {{ $content }}
语法会对用户内容进行转义。
Blade 的 {!! $content !!}
语法会直接对内容进行 非转义 输出,使用此语法时,必须 使用 HTMLPurifier for Laravel 5 来为用户输入内容进行过滤。使用方法参见: 使用 HTMLPurifier 来解决 Laravel 5 中的 XSS 跨站脚本攻击安全问题
防SQL注入:
Laravel 的 查询构造器 和 Eloquent 是基于 PHP 的 PDO,PDO 使用
prepared
来准备查询语句,保障了安全性。
在使用 raw()
来编写复杂查询语句时,必须 使用数据绑定。
正确操作,利用 select 方法 的第二个参数做数据绑定:
Route::get('sql-injection', function() {
$name = "admin"; // 假设用户提交
$password = "xx' OR 1='1"; // // 假设用户提交
$result = DB::select(
DB::raw("SELECT * FROM users WHERE name =:name and password = :password"),
[
'name' => $name,
'password' => $password,
]
);
dd($result);
});
DB
类里的大部分执行 SQL 的函数都可传参第二个参数 $bindings
,详见:API 文档 。
批量赋值:
Laravel 提供白名单和黑名单过滤($fillable
和 $guarded
),开发者 应该 清楚认识批量赋值安全威胁的情况下合理灵活地运用。
批量赋值安全威胁,指的是用户可更新本来不应有权限更新的字段。举例,users
表里的 is_admin
字段是用来标识用户『是否是管理员』,某不怀好意的用户,更改了『修改个人资料』的表单,增加了一个字段:
<input name="is_admin" value="1" />
这个时候如果你更新代码如下:
Auth::user()->update(Request::all());
此用户将获取到管理员权限。可以有很多种方法来避免这种情况出现,最简单的方法是通过设置 User 模型里的 $guarded
字段来避免:
protected $guarded = ['id', 'is_admin'];
**CSRF: **
CSRF 跨站请求伪造是 Web 应用中最常见的安全威胁之一,具体请见 Wiki - 跨站请求伪造 或者 Web 应用程序常见漏洞 CSRF 的入侵检测与防范。
Laravel 默认对所有『非幂等的请求』强制使用 VerifyCsrfToken
中间件防护,需要开发者做的,是区分清楚什么时候该使用『非幂等的请求』。
幂等请求指的是:'HEAD', 'GET', 'OPTIONS',既无论你执行多少次重复的操作都不会给资源造成变更。
- 所有删除的动作,必须 使用 DELETE 作为请求方法;
- 所有对数据更新的动作,必须 使用 POST、PUT 或者 PATCH 请求方法。
4.2 Laravel 程序优化
https://fsdhub.com/books/laravel-specification/527/laravel-optimization