基础规范
这是由 PHP-FIG(PHP Framework Interoperability Group)制定的 PHP 编码规范(PSR:Proposing a Standards Recommendation)
文档列表:PHP Standards Recommendations
重点阅读:PSR-1: Basic Coding Standard、PSR-12: Extended Coding Style
团队规范
代码注释
行注释:
块注释:
/**
* 标题后空一行
*
* @param $user
* @return array
*/
类方法及属性顺序
- 方法顺序:
public
、protected
、private
- 属性顺序:
const
> public
> proteced
> private
class Demo
{
const TIMEOUT = 60;
public $table = 'user';
protected $status = 1;
private $field = 'age';
public function func1()
{
}
protected function func2()
{
}
private function func3()
{
}
}
字符串中无变量时使用单引号
$str = ' ';
$str = 'Hello World';
$str = "Hello $name";
// SQL 语句例外(语法包含单引号)
$sql = "select * from table where company='四三九九网络股份有限公司'";
数组最后一个元素加逗号
增减数组元素后提交 Git,版本记录中不会产生干扰信息:
$array = [
'a',
'b',
'c',
];
Laravel 项目规范
日志规范
Laravel 遵循 RFC 5424
定义了八个日志级别,为了避免错误使用,我们使用并重新定义其中四个等级:
级别 |
说明 |
Error |
错误日志(出现错误,会导致系统不可用或功能中断) |
Warning |
警告日志(出现异常,但还不会影响功能的正常运行) |
Info |
信息日志(运行时的一些重要信息,默认日志输出级别) |
Debug |
调试日志(开发调试过程中一些详细的运行信息,默认不输出) |
单数 or 复数?
为了不必要的混淆,控制器、路由、模型、数据表均使用单数命名
资源型控制器
假设需要处理 “用户” 相关的 HTTP 请求,使用 Artisan 命令创建控制器:
php artisan make:controller UserController --resource
接下来给控制器注册一个资源路由:
Route::resource('user', 'UserController');
此时资源的相关操作对应关系如下:
HTTP 方法 |
URI |
动作(方法) |
说明 |
GET |
/user/create |
create |
创建用户页面 |
GET |
/user/{user}/edit |
edit |
编辑用户页面 |
GET |
/user |
index |
读取所有用户接口 |
POST |
/user |
store |
保存用户接口 |
GET |
/user/{user} |
show |
读取用户接口 |
PUT/PATCH |
/user/{user} |
update |
更新用户接口 |
DELETE |
/user/{user} |
destroy |
删除用户接口 |
自定义命令必须使用命名空间
所有的自定义命令,都必须使用项目命名空间,如:
php artisan namespace:send-email
php artisan namespace:clear-token
错误的例子:
php artisan send-email
php artisan clear-token
变量
使用见字知意的变量名
坏:
$ymdstr = $moment->format('y-m-d');
好:
$currentDate = $moment->format('y-m-d');
同一个实体要用相同的变量名
坏:
getUserInfo();
getUserData();
getUserRecord();
getUserProfile();
好:
getUser();
使用可读性高的名称
代码是用来读的,命名时如果没有有意义、不好理解,那就是在伤害读者。
坏:
$result = $serializer->serialize($data, 448);
好:
$JSON = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
使用自解释型变量
坏:
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches[1], $matches[2]);
不错(好一些,但强依赖于正则表达式的熟悉程度):
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);
[, $city, $zipCode] = $matches;
saveCityZipCode($city, $zipCode);
好(使用带名字的子规则,不用懂正则也能看的懂):
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+)\s*(?<zipCode>\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches['city'], $matches['zipCode']);
避免深层嵌套,尽早返回
坏:
function fibonacci(int $n)
{
if ($n < 50) {
if ($n !== 0) {
if ($n !== 1) {
return fibonacci($n - 1) + fibonacci($n - 2);
} else {
return 1;
}
} else {
return 0;
}
} else {
return 'Not supported';
}
}
好:
function fibonacci(int $n): int
{
if ($n === 0 || $n === 1) {
return $n;
}
if ($n >= 50) {
throw new \Exception('Not supported');
}
return fibonacci($n - 1) + fibonacci($n - 2);
}
少用无意义的变量名
别让读你的代码的人猜你写的变量是什么意思。写清楚好过模糊不清。
坏:
$l = ['Austin', 'New York', 'San Francisco'];
for($i = 0; $i < count($l); $i++) {
$li = $l [$i];
doStuff();
doSomeOtherStuff();
dispatch($li);
}
好:
$locations = ['Austin', 'New York', 'San Francisco'];
foreach($locations as $location) {
doStuff();
doSomeOtherStuff();
dispatch($location);
}
不要添加不必要上下文
如果从你的类名、对象名已经可以得知一些信息,就别再在变量名里重复。
坏:
class Car
{
public $carMake;
public $carModel;
public $carColor;
}
好:
class Car
{
public $make;
public $model;
public $color;
}
表达式
使用恒等式
不好(简易对比会将字符串转为整型):
$a = '42';
$b = 42;
if ($a != $b) {
}
对比 $a != $b 返回了 false
但应该返回 true
! 字符串 '42' 跟整数 42 不相等
好(使用恒等判断检查类型和数据):
$a = '42';
$b = 42;
if ($a !== $b) {
}
函数
函数参数(最好少于 2 个)
限制函数参数个数极其重要,超过 3 个可选参数会有很多参数组合要测试。
无参数是理想情况,通常如果你的函数有超过 2 个参数,说明它要处理的事太多了。 如果必须要传入很多数据,建议封装一个高级别对象作为参数。
坏:
function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void
{
}
好:
class MenuConfig
{
public $title;
public $body;
public $buttonText;
public $cancellable = false;
}
$config = new MenuConfig();
$config->title = 'Foo';
$config->body = 'Bar';
$config->buttonText = 'Baz';
$config->cancellable = true;
function createMenu(MenuConfig $config): void
{
}
函数应该只做一件事
这是迄今为止软件工程里最重要的一个规则。当一个函数做超过一件事的时候,他们就难于实现、测试和理解。当你把一个函数拆分到只剩一个功能时,他们就容易被重构,然后你的代码读起来就更清晰。如果你遵循这条规则,你就领先于大多数开发者了。
坏:
function emailClients(array $clients): void
{
foreach($clients as $client) {
$clientRecord = $db->find($client);
if ($clientRecord->isActive()) {
email($client);
}
}
}
好:
function emailClients(array $clients): void
{
$activeClients = activeClients($clients);
array_walk($activeClients, 'email');
}
function activeClients(array $clients): array
{
return array_filter($clients, 'isClientActive');
}
function isClientActive(int $client): bool
{
$clientRecord = $db->find($client);
return $clientRecord->isActive();
}
函数名应体现它做了什么事
坏:
class Email
{
public function handle(): void
{
mail($this->to, $this->subject, $this->body);
}
}
$message = new Email(...);
$message->handle();
好:
class Email
{
public function send(): void
{
mail($this->to, $this->subject, $this->body);
}
}
$message = new Email(...);
$message->send();
不要用 flag 作为函数的参数
flag 就是在告诉大家,这个方法里处理很多事。前面刚说过,一个函数应当只做一件事。 把不同 flag 的代码拆分到多个函数里。
坏:
function createFile(string $name, bool $temp = false): void
{
if ($temp) {
touch('./temp/'.$name);
} else {
touch($name);
}
}
好:
function createFile(string $name): void
{
touch($name);
}
function createTempFile(string $name): void
{
touch('./temp/'.$name);
}
封装条件语句
坏:
if ($article->state === 'published') {
}
好:
if ($article->isPublished()) {
}
避免用反义条件判断
坏:
function isDOMNodeNotPresent(\DOMNode $node): bool
{
}
if (!isDOMNodeNotPresent($node))
{
}
好:
function isDOMNodePresent(\DOMNode $node): bool
{
}
if (isDOMNodePresent($node)) {
}
避免条件判断
这看起来像一个不可能的任务。但是当有很多 if
语句的类和函数时,函数做了不止一件事。
坏:
class Airplane
{
public function getCruisingAltitude(): int
{
switch($this->type) {
case '777':
return $this->getMaxAltitude() - $this->getPassengerCount();
case 'Air Force One':
return $this->getMaxAltitude();
case 'Cessna':
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
}
好:
interface Airplane
{
public function getCruisingAltitude(): int;
}
class Boeing777 implements Airplane
{
public function getCruisingAltitude(): int
{
return $this->getMaxAltitude() - $this->getPassengerCount();
}
}
class AirForceOne implements Airplane
{
public function getCruisingAltitude(): int
{
return $this->getMaxAltitude();
}
}
class Cessna implements Airplane
{
public function getCruisingAltitude(): int
{
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
移除僵尸代码
僵尸代码和重复代码一样坏。没有理由保留在你的代码库中。如果从来没被调用过,就删掉!因为还在代码版本库里,很安全。
坏:
function oldRequestModule(string $url): void
{
}
function newRequestModule(string $url): void
{
}
$request = newRequestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');
好:
function requestModule(string $url): void
{
}
$request = requestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');
别写重复代码(DRY)
试着去遵循 DRY 原则。
尽你最大的努力去避免复制代码,它是一种非常糟糕的行为,复制代码通常意味着当需要变更一些逻辑时,修改不止一处。
坏:
function showDeveloperList(array $developers): void
{
foreach($developers as $developer) {
$expectedSalary = $developer->calculateExpectedSalary();
$experience = $developer->getExperience();
$githubLink = $developer->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
function showManagerList(array $managers): void
{
foreach($managers as $manager) {
$expectedSalary = $manager->calculateExpectedSalary();
$experience = $manager->getExperience();
$githubLink = $manager->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
好:
function showList(array $employees): void
{
foreach($employees as $employee) {
$expectedSalary = $employee->calculateExpectedSalary();
$experience = $employee->getExperience();
$githubLink = $employee->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
极好(最好让代码紧凑一点):
function showList(array $employees): void
{
foreach($employees as $employee) {
render([
$employee->calculateExpectedSalary(),
$employee->getExperience(),
$employee->getGithubLink()
]);
}
}