PHP 整洁之道 | Laravel China 社区


本站和网页 https://learnku.com/articles/32508 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

PHP 整洁之道 | Laravel China 社区
Laravel
话题列表
社区 Wiki
优质外文
招聘求职
Laravel 实战教程
社区文档
登录
注册
Laravel
首页
Laravel
Go
PHP
Vue.js
Python
Java
MySQL
Rust
LK
Elasticsearch
F2E 前端
Server
程序员
Database
DevTools
Computer Science
手机开发
AdonisJS
社区
Wiki
教程
Laravel 实战教程首页
《L01 Laravel 教程 - Web 开发实战入门》
《L02 Laravel 教程 - Web 开发实战进阶》
《L03 Laravel 教程 - 实战构架 API 服务器》
《L04 Laravel 教程 - 微信小程序从零到发布》
《L05 Laravel 教程 - 电商实战》
《L06 Laravel 教程 - 电商进阶》
《LX1 Laravel / PHP 扩展包视频教程》
《LX2 PHP 扩展包实战教程 - 从入门到发布》
《L07 Laravel 教程 - Laravel TDD 测试实战》
《LX3 Laravel 性能优化入门》
《LX4 Laravel / PHP 五分钟视频》
文档
社区文档首页
《Laravel 中文文档》
《Laravel 速查表》
《PHP 代码简洁之道》
《Laravel 编码技巧》
《Dcat Admin 中文文档》
《Laravel Nova 中文文档》
《Lumen 中文文档》
《Dingo API 中文文档》
《 Laravel 项目开发规范》
《构建 Laravel 开发环境》
登录
注册
微信登录
PHP 整洁之道
68
88
王子昊 的个人博客
6059
创建于 3年前
更新于 3年前
5 个改进
yangweijie/clean-code-php
clean-code-php
Table of Contents
介绍
变量
函数
介绍
本文由 yangweijie 翻译自clen php code,团建用,欢迎大家指正。
摘录自 Robert C. Martin的Clean Code 书中的软件工程师的原则
,适用于PHP。 这不是风格指南。 这是一个关于开发可读、可复用并且可重构的PHP软件指南。
并不是这里所有的原则都得遵循,甚至很少的能被普遍接受。 这些虽然只是指导,但是都是Clean Code作者多年总结出来的。
Inspired from clean-code-javascript
变量
使用有意义且可拼写的变量名
Bad:
$ymdstr = $moment->format('y-m-d');
Good:
$currentDate = $moment->format('y-m-d');
同种类型的变量使用相同词汇
Bad:
getUserInfo();
getClientData();
getCustomerRecord();
Good:
getUser();
使用易检索的名称
我们会读比写要多的代码。通过是命名易搜索,让我们写出可读性和易搜索代码很重要。
Bad:
// What the heck is 86400 for?
addExpireAt(86400);
Good:
// Declare them as capitalized `const` globals.
interface DateGlobal {
const SECONDS_IN_A_DAY = 86400;
addExpireAt(DateGlobal::SECONDS_IN_A_DAY);
使用解释型变量
Bad:
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches[1], $matches[2]);
Good:
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
list(, $city, $zipCode) = $matchers;
saveCityZipCode($city, $zipCode);
避免心理映射
明确比隐性好。
Bad:
$l = ['Austin', 'New York', 'San Francisco'];
for($i=0; $i<count($l); $i++) {
oStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// 等等`$l` 又代表什么?
dispatch($l);
Good:
$locations = ['Austin', 'New York', 'San Francisco'];
for($i=0; $i<count($locations); $i++) {
$location = $locations[$i];
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch($location);
});
不要添加不必要上下文
如果你的class/object 名能告诉你什么,不要把它重复在你变量名里。
Bad:
$car = [
'carMake' => 'Honda',
'carModel' => 'Accord',
'carColor' => 'Blue',
];
function paintCar(&$car) {
$car['carColor'] = 'Red';
Good:
$car = [
'make' => 'Honda',
'model' => 'Accord',
'color' => 'Blue',
];
function paintCar(&$car) {
$car['color'] = 'Red';
使用参数默认值代替短路或条件语句。
Bad:
function createMicrobrewery($name = null) {
$breweryName = $name ?: 'Hipster Brew Co.';
// ...
Good:
function createMicrobrewery($breweryName = 'Hipster Brew Co.') {
// ...
函数
函数参数最好少于2个
限制函数参数个数极其重要因为它是你函数测试容易点。有超过3个可选参数参数导致一个爆炸式组合增长,你会有成吨独立参数情形要测试。
无参数是理想情况。1个或2个都可以,最好避免3个。再多就需要加固了。通常如果你的函数有超过两个参数,说明他多做了一些事。 在参数多的情况里,大多数时候一个高级别对象(数组)作为参数就足够应付。
Bad:
function createMenu($title, $body, $buttonText, $cancellable) {
// ...
Good:
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) {
// ...
函数应该只做一件事
这是迄今为止软件工程里最重要的一个规则。当函数做超过一件事的时候,他们就难于实现、测试和理解。当你隔离函数只剩一个功能时,他们就容易被重构,然后你的代码读起来就更清晰。如果你光遵循这条规则,你就领先于大多数开发者了。
Bad:
function emailClients($clients) {
foreach ($clients as $client) {
$clientRecord = $db->find($client);
if($clientRecord->isActive()) {
email($client);
Good:
function emailClients($clients) {
$activeClients = activeClients($clients);
array_walk($activeClients, 'email');
function activeClients($clients) {
return array_filter($clients, 'isClientActive');
function isClientActive($client) {
$clientRecord = $db->find($client);
return $clientRecord->isActive();
函数名应当描述他们所做的事
Bad:
function addToDate($date, $month) {
// ...
$date = new \DateTime();
// It's hard to to tell from the function name what is added
addToDate($date, 1);
Good:
function addMonthToDate($month, $date) {
// ...
$date = new \DateTime();
addMonthToDate(1, $date);
函数应当只为一层抽象,当你超过一层抽象时,函数正在做多件事。拆分功能易达到可重用性和易用性。.
Bad:
function parseBetterJSAlternative($code) {
$regexes = [
// ...
];
$statements = split(' ', $code);
$tokens = [];
foreach($regexes as $regex) {
foreach($statements as $statement) {
// ...
$ast = [];
foreach($tokens as $token) {
// lex...
foreach($ast as $node) {
// parse...
Good:
function tokenize($code) {
$regexes = [
// ...
];
$statements = split(' ', $code);
$tokens = [];
foreach($regexes as $regex) {
foreach($statements as $statement) {
$tokens[] = /* ... */;
});
});
return tokens;
function lexer($tokens) {
$ast = [];
foreach($tokens as $token) {
$ast[] = /* ... */;
});
return ast;
function parseBetterJSAlternative($code) {
$tokens = tokenize($code);
$ast = lexer($tokens);
foreach($ast as $node) {
// parse...
});
删除重复的代码
尽你最大的努力来避免重复的代码。重复代码不好,因为它意味着如果你修改一些逻辑,那就有不止一处地方要同步修改了。
想象一下如果你经营着一家餐厅并跟踪它的库存: 你全部的西红柿、洋葱、大蒜、香料等。如果你保留有多个列表,当你服务一个有着西红柿的菜,那么所有记录都得更新。如果你只有一个列表,那么只需要修改一个地方!
经常你容忍重复代码,因为你有两个或更多有共同部分但是少许差异的东西强制你用两个或更多独立的函数来做相同的事。移除重复代码意味着创造一个处理这组不同事物的一个抽象,只需要一个函数/模块/类。
抽象正确非常重要,这也是为什么你应当遵循SOLID原则(奠定Class基础的原则)。坏的抽象可能比重复代码还要糟,因为要小心。在这个前提下,如果你可以抽象好,那就开始做把!不要重复你自己,否则任何你想改变一件事的时候你都发现在即在更新维护多处。
Bad:
function showDeveloperList($developers) {
foreach($developers as $developer) {
$expectedSalary = $developer->calculateExpectedSalary();
$experience = $developer->getExperience();
$githubLink = $developer->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
function showManagerList($managers) {
foreach($managers as $manager) {
$expectedSalary = $manager->calculateExpectedSalary();
$experience = $manager->getExperience();
$githubLink = $manager->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
Good:
function showList($employees) {
foreach($employees as $employe) {
$expectedSalary = $employe->calculateExpectedSalary();
$experience = $employe->getExperience();
$githubLink = $employe->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
通过对象赋值设置默认值
Bad:
$menuConfig = [
'title' => null,
'body' => 'Bar',
'buttonText' => null,
'cancellable' => true,
];
function createMenu(&$config) {
$config['title'] = $config['title'] ?: 'Foo';
$config['body'] = $config['body'] ?: 'Bar';
$config['buttonText'] = $config['buttonText'] ?: 'Baz';
$config['cancellable'] = $config['cancellable'] ?: true;
createMenu($menuConfig);
Good:
$menuConfig = [
'title' => 'Order',
// User did not include 'body' key
'buttonText' => 'Send',
'cancellable' => true,
];
function createMenu(&$config) {
$config = array_merge([
'title' => 'Foo',
'body' => 'Bar',
'buttonText' => 'Baz',
'cancellable' => true,
], $config);
// config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
createMenu($menuConfig);
不要用标志作为函数的参数,标志告诉你的用户函数做很多事了。函数应当只做一件事。 根据布尔值区别的路径来拆分你的复杂函数。
Bad:
function createFile(name, temp = false) {
if (temp) {
touch('./temp/'.$name);
} else {
touch($name);
Good:
function createFile($name) {
touch(name);
function createTempFile($name) {
touch('./temp/'.$name);
避免副作用
一个函数做了比获取一个值然后返回另外一个值或值们会产生副作用如果。副作用可能是写入一个文件,修改某些全局变量或者偶然的把你全部的钱给了陌生人。
现在,你的确需要在一个程序或者场合里要有副作用,像之前的例子,你也许需要写一个文件。你想要做的是把你做这些的地方集中起来。不要用几个函数和类来写入一个特定的文件。用一个服务来做它,一个只有一个。
重点是避免常见陷阱比如对象间共享无结构的数据,使用可以写入任何的可变数据类型,不集中处理副作用发生的地方。如果你做了这些你就会比大多数程序员快乐。
Bad:
// Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
$name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
$name = preg_split('/ /', $name);
splitIntoFirstAndLastName();
var_dump($name); // ['Ryan', 'McDermott'];
Good:
$name = 'Ryan McDermott';
function splitIntoFirstAndLastName($name) {
return preg_split('/ /', $name);
$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);
var_export($name); // 'Ryan McDermott';
var_export($newName); // ['Ryan', 'McDermott'];
不要写全局函数
在大多数语言中污染全局变量是一个坏的实践,因为你可能和其他类库冲突并且你api的用户不明白为什么直到他们获得产品的一个异常。让我们看一个例子:如果你想配置一个数组,你可能会写一个全局函数像config(),但是可能和试着做同样事的其他类库冲突。这就是为什么单例设计模式和简单配置会更好的原因。
Bad:
function config() {
return [
'foo': 'bar',
};
Good:
class Configuration {
private static $instance;
private function __construct($configuration) {/* */}
public static function getInstance() {
if(self::$instance === null) {
self::$instance = new Configuration();
return self::$instance;
public function get($key) {/* */}
public function getAll() {/* */}
$singleton = Configuration::getInstance();
封装条件语句
Bad:
if ($fsm->state === 'fetching' && is_empty($listNode)) {
// ...
Good:
function shouldShowSpinner($fsm, $listNode) {
return $fsm->state === 'fetching' && is_empty(listNode);
if (shouldShowSpinner($fsmInstance, $listNodeInstance)) {
// ...
避免消极条件
Bad:
function isDOMNodeNotPresent($node) {
// ...
if (!isDOMNodeNotPresent($node)) {
// ...
Good:
function isDOMNodePresent($node) {
// ...
if (isDOMNodePresent($node)) {
// ...
避免条件声明
这看起来像一个不可能任务。当人们第一次听到这句话是都会这么说。
"没有一个if声明" 答案是你可以使用多态来达到许多case语句里的任务。第二个问题很常见, “那么为什么我要那么做?” 答案是前面我们学过的一个整洁代码原则:一个函数应当只做一件事。当你有类和函数有很多if声明,你自己知道你的函数做了不止一件事。记住,只做一件事。
Bad:
class Airplane {
// ...
public function getCruisingAltitude() {
switch (this.type) {
case '777':
return $this->getMaxAltitude() - $this->getPassengerCount();
case 'Air Force One':
return $this->getMaxAltitude();
case 'Cessna':
return $this->getMaxAltitude() - $this->getFuelExpenditure();
Good:
class Airplane {
// ...
class Boeing777 extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude() - $this->getPassengerCount();
class AirForceOne extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude();
class Cessna extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude() - $this->getFuelExpenditure();
Avoid 避免类型检查 (part 1)
PHP是弱类型的,这意味着你的函数可以接收任何类型的参数。
有时候你为这自由所痛苦并且在你的函数渐渐尝试类型检查。有很多方法去避免这么做。第一种是考虑API的一致性。
Bad:
function travelToTexas($vehicle) {
if ($vehicle instanceof Bicycle) {
$vehicle->peddle($this->currentLocation, new Location('texas'));
} else if ($vehicle instanceof Car) {
$vehicle->drive($this->currentLocation, new Location('texas'));
Good:
function travelToTexas($vehicle) {
$vehicle->move($this->currentLocation, new Location('texas'));
避免类型检查 (part 2)
如果你正使用基本原始值比如字符串、整形和数组,你不能用多态,你仍然感觉需要类型检测,你应当考虑类型声明或者严格模式。 这给你了基于标准PHP语法的静态类型。 手动检查类型的问题是做好了需要好多的废话,好像为了安全就可以不顾损失可读性。保持你的PHP 整洁,写好测试,做好代码回顾。做不到就用PHP严格类型声明和严格模式来确保安全。
Bad:
function combine($val1, $val2) {
if (is_numeric($val1) && is_numeric(val2)) {
return val1 + val2;
throw new \Exception('Must be of type Number');
Good:
function combine(int $val1, int $val2) {
return $val1 + $val2;
移除僵尸代码
僵尸代码和重复代码一样坏。没有理由保留在你的代码库中。如果从来被调用过,见鬼去!在你的版本库里是如果你仍然需要他的话,因此这么做很安全。
Bad:
function oldRequestModule($url) {
// ...
function newRequestModule($url) {
// ...
$req = new newRequestModule();
inventoryTracker('apples', $req, 'www.inventory-awesome.io');
Good:
function newRequestModule($url) {
// ...
$req = new newRequestModule();
inventoryTracker('apples', $req, 'www.inventory-awesome.io');
clean-code
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 3年前 自动加精
举报
王子昊
课程读者
38 声望
暂无个人描述~
68 人点赞
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
推荐文章:
更多推荐...
博客
[扩展推荐] Laravel-Gii 快速创建基于 MySQL 表结构的 CRUD 后台
27
25
3年前
讨论数量: 6
排序:
时间
投票
lovecn
186 声望
返回顶部 不起作用啊
3年前
评论
评论
puzzle9
3年前
你又发现了盲点
liaohui5
3年前
本来我也是个写代码追求极致完美得人, 但是,见过了太多太多的 “能跑就行” 已经麻木了
举报
superfox
3 声望
谢谢楼主分享经验,写得不错,但是关于下面这一段
博客:PHP 整洁之道
foreach还能这样用?
另外改为for循环,count最好是在循环之前食用效果更好,
3年前
评论
评论
puzzle9
3年前
你发现了盲点
zulien
3年前
循环里最好不要用count,会有意想不到的结果出现
superfox
(作者)
3年前
@zulien 已经说了,是在循环之前
举报
zulien
课程读者
26 声望
个人觉得有些规则不太适合PHP,大部分用的是框架,为了简洁去新建类,这个维护起来更难。
3年前
评论
评论
举报
小丑路人
课程读者
27 声望
避免消极条件……为嘛?
有时候需要的就是 if(!……)
总不能
if(){
}else{
……逻辑
3年前
评论
评论
举报
iMactool
课程读者
46 声望
php @ ph
list(, $city, $zipCode) = $matchers;
应该是笔误吧
list(, $city, $zipCode) = $matches;
3年前
评论
评论
liuyuit
2年前
不是笔误,用 $matches[1] 和 $matches[2] 去赋值所以这样写
举报
zulien
课程读者
26 声望
一直记得Python的写法,遗忘了php,今天逛了一下。嗯,PHP的写法也要记住了
# python
city,zip_code = matchers
# php
list($city, $zipCode) = $matches;
3年前
评论
评论
举报
讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
<a href="javascript:;" class="mr-2 ui popover text-mute" data-html="黏贴或拖拽图片至输入框内皆可上传图片">
<a href="javascript:;" class="mr-2 ui popover text-mute hide-on-mobile" data-html="支持除了 H1~H6 以外的GitHub 兼容 Markdown">
支持 MD
帮助
关注本文
评论
王子昊
未填写
文章
13
粉丝
喜欢
69
收藏
88
排名:395
访问:2.3 万
关注
私信
所有博文
阅读模式
文章归档
1 篇
2019 年 8 月
1 篇
2019 年 6 月
7 篇
2019 年 5 月
1 篇
2019 年 3 月
3 篇
2019 年 2 月
最新文章
最受欢迎
3年前
PHP 整洁之道
3年前
获取 Laravel 路由 name
3年前
文件存储
3年前
Ajax 请求后,with('success','msg'),前台刷新没反应
3年前
表单请求 统一字段不同场景不同处理
68
PHP 整洁之道
工作踩坑系列——https 访问遇到 “已阻止载入混合活动内容”
安装其他版本的 Laravel
Laravel 辅助函数
[报错:字符太长] SQLSTATE [42000]: Syntax error or access violation: 1071 Specified key was t oo long;
博客标签
laravel
表单验证
php function
laravle string长度
sublime text3
formrequest
clean-code
社区赞助商
成为赞助商
社区赞助商
成为赞助商
关于 LearnKu
LearnKu 是终身编程者的修道场
做最专业、严肃的技术论坛
LearnKu 诞生的故事
资源推荐
《社区使用指南》
《文档撰写指南》
《LearnKu 社区规范》
《提问的智慧》
服务提供商
其他信息
成为版主
所有测验
联系站长(反馈建议)
粤ICP备18099781号-6
粤公网安备 44030502004330号
违法和不良信息举报
由 Summer 设计和编码 ❤
请登录
提交
忘记密码?
or
注册
第三方账号登录
微信登录
GitHub 登录
内容举报
匿名举报,为防止滥用,仅管理员可见举报者。
我要举报该,理由是:
垃圾广告:恶意灌水、广告、推广等内容
无意义内容:测试、灌水、文不对题、消极内容、文章品质太差等
违规内容:色情、暴利、血腥、敏感信息等
不友善内容:人身攻击、挑衅辱骂、恶意行为
科学上网:翻墙、VPN、Shadowsocks,政策风险,会被关站!
不懂提问:提问太随意,需要再做一遍《提问的智慧》测验
随意提问:提问没有发布在社区问答分类下
排版混乱:没有合理使用 Markdown 编写文章,未使用代码高亮
内容结构混乱:逻辑不清晰,内容混乱,难以阅读
标题随意:标题党、标题不释义
尊重版权:分享付费课程、破解软件(付费),侵犯作者劳动成果
其他理由:请补充说明
举报
取消