- 前言
这是一份旨在增强团队的开发协作,提高代码质量和打造开发基石的编码风格规范。当一个团队开始指定并实行编码规范的话,错误就会变得更加显而易见。如果一段特定的代码不符合规范的话,它有可能只是代码风格错误,而也有可能会是 bug,更可能出现安全问题。早期指定规范就使得代码审核得以更好的开展,并且可以更精确的地定位到错误。只要开发者们能够保证源代码源文件都严格遵循规范,那接下来的维护工作就可以变得轻松简单。
总之,我们的目标就是遵循同一套编码规范,不管有多少人共同参与同一项目,都可以确保每一行代码都像是同一个人编写的,每一个功能都是安全的。
源码文件必须采用UTF-8编码,且不得有BOM头,某些历史遗留的GBK模块除外。
编码风格没有太多的好坏之分, 最重要的是风格保持一致,编码规范有助于规范我们编码的风格,使代码具有更好的可读性。
PHP在公司内部应用得越来越广泛,但是却缺乏相应的编码规范支持,编码风格百家齐放,不利于我们代码的维护和传承, 根据大家平时的开发情况,制定了此PHP编码规范。
每项规范前面的[强制]代表该规范需要强制执行,[建议]代表推荐执行但不强制。
注: 文中所有的变量名前面为了方便没有加”$”, 示意即可。
本文档风格约定部分可能跟你的喜好有冲突,请尽量用包容的心态来阅读。有任何问题或建议,欢迎跟讨论
1. 排版
1.1. [强制] 程序块要采用缩进风格编写,缩进的空格数建议为4个,单模块内必须统一。
解释
不同的缩进风格对代码的可读性影响很大,以tab为缩进单位在不同的tab step 下可读性也相差很多,所以将缩进定为一个soft tab即4个空格,这样在所有环境下缩进都会保持一致。
1.2. [建议]关键字与其后的左括号之间有一个空格,而函数名与左括号之间不应有任何字符包括空格。
解释
虽然很多情况下编辑器的highlight已经做了区分,但是从格式上区分关键字和函数适用于所有的情况。
示例
关键字 if (a > b)
函数名 funcA()
1.3. [建议]开始的大括号位于一行的末尾,结束的括号位于最末一行后,且独占一行。首括号也可另起一行,但一个模块内必须统一。
示例
if (a > b) {
}
1.4. [强制] if/while等结构体,即使只有一行,也必须加上花括号,不得写成一行。
解释
这样做可读性更好,并且方便修改。
示例
if (a > b) {
a = 1;
}
1.5. [建议]一行代码不得超过120个字节,建议控制在80字节内;一个函数不得超过500行,建议控制在100行以内。
解释
代码更美观, 可读性更好
1.6. [建议]else-if语句使用else if形式,不使用elseif形式。
1.7. [建议]函数名与其后的左括号之间不应有任何字符(包括空格) 函数调用的左括号与其第一个参数之间不应有任何字符(包括空格) 最后一个参数与右括号之间不应有任何字符(包括空格) 参数列表的逗号后面应有一个空格
示例
funcA(a, b, c) {
}
1.8. [建议]避免由于对错误的条件做判断带来if的嵌套。
解释
减少if/else嵌套, 更利于代码逻辑的理解。
示例
不推荐的方式:
if (a === false) {
// error handle
} else {
if (b === false) {
// handle
}
}
推荐的方式:
if (a === false) {
// error handle
}
if (b === false) {
// handle
}
1.9. [建议]如果过长的话需要另起一行。if 语句的条件若较多较长,应折行;新行以逻辑运算符起始,与第一行 if 左括号后的第一个字符对齐;折行后,每行条件具有独立而明确的语义
解释
这样做逻辑更一目了然。
示例
if (a > b && c > d
&& e > f && h > j
&& z > x) {
}
1.10. [建议]多行的”=”可能的话尽量用空格对齐。
示例
a = 1;
ab = 2;
abc = 3;
1.11. [强制] Switch语句中每个case的break必须和case间有缩进。
示例
case ‘A’:
a = 2;
break;
1.12. [强制] 初始化array如果采用多行结构时,数据项部分需要缩进,且最后一个数据项后面的逗号不可省略。
解释
这样做在修改代码增加数据项的时候不容易出现语法错误。
示例
$a = array(
'a' => 'b',
'b' => 'c',
'c' => 'd',
);
1.13. [建议] 复杂的表达式, 使用括号表明优先级, 而不完全依赖运算符优先级。
示例
不推荐方式:
if ($a && $b || $c + $b && $e) {
}
推荐方式:
if (($a && $b) || (($c + $b) && $e)){
}
1.14. [建议] 同一个代码块的变量定义, 应该尽可能集中在块开始位置,提高可读性。
1.15. [建议] 除模板外,不允许使用?>标记结尾, 避免其后误加的字符干扰页面渲染。
2. 命名
2.1. [强制] 全局变量以g_开头。
解释
全局变量对代码影响很大,以g_开头便能在代码中一眼看出是全局变量。
示例
g_count;
2.2. [强制] 常量命名使用全部大写字符,单词之间以’_’连接。
示例
PAGE_NUM
2.3. [建议]对于代码中的常量,建议用常量或define表示,不应直接写在代码中。
示例
define('PAGE_NUM', 3);
2.4. [强制]关键字true、false、null必须小写
2.5. [强制] [PHP026] 类method命名采用驼峰命名, 普通function采用过程函数风格命名。
示例
类method:
public function getName() {
}
普通function:
function show_me_the_money() {
}
2.6. [强制·]类成员变量和局部变量必须采用驼峰命名法,建议增加三字节的类型前缀:arr、str、int、bol、obj等
示例
$strName, $intAge
这里并不探讨各种写法的优缺点,只是风格需要保持统一,不要混用。
2.7. [建议]文件(除了类)命名使用小写字母,单词之间以’_’连接。
示例
show_lemma.php
2.8. [建议]配置文件的名称为配置文件名 + .conf.php, 不涉及类的都小写通过”_”连接。
示例
good_version.conf.php
2.9. [建议]类名应以大写字母开头,每个单词的首字母大写。
示例
ActionController
2.10. [建议]final放在访问控制符的前面、访问控制符放在static的前面
示例
final public static function getInstance(){
}
3. 注释
3.1. [建议]文件、函数、类以及成员变量都应包含注释,关键代码必须有注释。
类文件/普通文件的注释, 说明该文件的主要作用。
示例
"A simple class describing employees" 说明类文件的主要作用。
"@package Employee" 说明namespace(如果有)
"@author George Schlossnagle" 说明作者信息
/**
* A simple class describing employees
*
* @package Employee
* @author George Schlossnagle
*/
类的注释, 说明该类的主要作用。
示例
"An example of documenting a class" 说明类的主要作用。
"The employees annual salary" 说明变量的作用。
"@var number" 说明变量的类型。
"The class constructor" 说明方法的作用。
"@param" 说明参数类型。
"@access" 说明访问权限。
"@return" 说明返回值。
/**
* An example of documenting a class
*/
class Employee
{
/**
* @var string
*/
private $name;
/**
* The employees annual salary
* @var number
*/
private $salary;
/**
* @var number
*/
private $employee_id;
/**
* The class constructor
* @param number
*/
public function Employee($employee_id = false) {
if ($employee_id) {
$this->employee_id = $employee_id;
$this->_fetchInfo();
}
}
3.2. [强制] [PHP027] 不能使用#作为单行注释, 多行注释/ * **/不能出现在同一行。
3.3. [强制] [PHP028] 函数必须通过param和return标记指明其参数和返回值。
3.4. [建议] 注释需要遵守phpDocumentor等注释规范,同一团队内部必须保持一致。
3.5、注释规范方法和函数
在方法和函数的上一行必须用双斜线注释注明该方法或函数的功能,在难以理解的代码语句后面适当添加注释说明
4. 编码原则
4.1. [建议]对传入或返回的参数进行类型检查和显式转换。
示例
$intSalary = (int) $salary;
4.2. [强制]对于函数返回值的判断,特别是true/false, 必须用===或!==
4.3. [强制]生成对象时,必须使用new Classname(),不能用new Classname
4.4. [强制]所有文件路径都需要利用框架提供的宏写成绝对路径。
4.5. [建议]对于长时间运行的CLI程序,需要及时unset无用变量,尤其是PHP5.2上。
4.6. [建议]配置项与PHP代码分离,不随CVS/SVN发布
4.7. [强制]预定义变量一律使用短格式,即:$_POST、$_GET、$_SERVER、$_ENV等,不再使用长格式:$_HTTP_POST_VARS、$_HTTP_GET_VARS。
4.8. [建议]除模板外,尽量不要在php代码中出现html标签。
4.9. [建议]能用foreach的就不要用for,能用for的就不要用while。
4.10. [强制]每个前端访问请求必须有且仅有一条notice日志。
4.12. [建议]数据库写操作必须有日志记录;记录条数应与操作一一对应。
4.13. [建议]字符串尽量用’’而不是””进行引用,一个是效率问题,一个是安全问题。
示例
define('PAGE_NUM', 3);
4.14. [强制] 所有的全局变量应该写在函数的最开头,并且和后面的代码以空行隔开。
示例
function a() {
global g_count;
global g_time;
a = 1;
}
4.15. [强制] 禁止使用and, or, 而是使用&&, ||
4.16 [建议]避免使用$i, $j这样无意义的变量名, 除非是用作循环计数变量。
4.17. [建议]进行==判断时,建议把常量放在前面, 避免误写成赋值操作。
示例
不推荐形式:
if ($a == 1){
}
推荐形式:
if (1 == $a){
}
5. 代码性能
5.1. [强制] [PHP034] 把重复调用放在循环体外。
示例
不推荐形式:
for($i = 0; $i < count($arr); $i++)
推荐形式:
$arrCount = count($arr);
for($i = 0; $i < $arrCount; $i++)
6.数据库相关命名规范
6.1 数据库命名
数据库的命名一般采用小写字母命名,如 testdb;
6.2 数据表命名
数据表命名格式为 表前缀+下划线(_)+表名 组合方式命名。同一数据库表前缀相同,且使用小写字母命名,表名使用小写字母和下划线命名。例如 wll_setting_record,其中wll为前缀,setting_record为表名。
6.3 字段命名
字段的命名采用小写字母和下划线命名,如 coin_type。
6.4 无意义的命名
例如:$abcd、qqqqqqq()、test1111111等,命名应该使用对应的英文翻译;
6.5 拼音命名(特别注意的)
例如添加用户(tjyh)或中英混合的(tjUser),如果使用这种方式,其他开发人员会很难看得懂,加大维护难度。
6.6 单字母命名
ThinkPHP中封装了很多名称为单字母的快捷方法,例如A( )、C( )、M( )。如果再定义可能会发生冲突。
6.7禁止出现多行空格
//公共方法
public function common(){
$name='tom'; //定义姓名
$this->assign('name',$name);
$this->display();
}
7.安全性
7.1输入框
所有的输入框请设置最大长度,必填项请做必填的限制。例如帐号CHN00000001,只能输入11位,那么最大输入长度只能为11,并且添加required="required"属性。
7.2 文本编辑器
前台页面尽量不要使用富文本编辑器,因为富文本编辑器可以输入代码,有很大的安全隐患。如果要使用,必须对提交内容进行过滤,例如使用htmlspecialchars()进行过滤。
7.3 后台接收参数
PHP从表单获取url中获取参数必须进行字段类型判断。例如接收数字的参数不能含有其他字符,只能是数字,建议编写公共的检查接收post和get参数的函数,对每一个参数值进行验证,防止注入恶意代码。
接收长篇内容的变量,例如留言内容等变量时,必须进行特殊字符的过滤操作。比如strip_tags()、htmlspecialchars()、htmlentities()等函数可以起作用,防止用户注入恶意代码进行跨站脚本攻击。
7.4 数据库查询
数据库查询语句禁止直接使用sql语句和传递的参数直接执行,例如下面这种的写法是禁止使用的
$id = 1;
$User = M
$id = 1;
$User = M("User"); // 实例化User对象
$User->where('id='.$id)->select(); //错误写法1
$User->where("id=$id")->select(); //错误写法2
("User"); // 实例化User对象
$User->where('id='.$id)->select(); //错误写法1
$User->where("id=$id")->select(); //错误写法2
如果传递过来的参数含有SQL注入代码,这两种写法都是非常危险的。正确的写法有以下两种:
(1)数组条件,例如:
$where['id'] = 1;
$User =
$where['id'] = 1;
$User = M("User"); // 实例化User对象
$User->where($where)->select();
M("User"); // 实例化User对象
$User->where($where)->select();
或者
$U
$User = M("User"); // 实例化User对象
$User->where(['id'=>1])->select();
ser = M("User"); // 实例化User对象
$User->where(['id'=>1])->select();
(2)预处理机制,例如:
$Use
$User = M("User"); // 实例化User对象
$User->where("id=%d",$id)->select();
r = M("User"); // 实例化User对象
$User->where("id=%d",$id)->select();
7.5 权限控制
凡是需要登录之后才可以访问的页面加载页面之前必须做好登录检测,登录超时的必须重新登录。需要权限访问的页面和功能必须做好权限控制和检测。
7.6 越权访问
比如邮件列表的页面 http://119.28.56.83/user/xq/id/5 ,只需要修改最后面的邮件id就可以直接访问的,必须加入当前用户判断,只能查看自己的邮件,禁止越权访问。
7.7 数据导出
数据的导出和下载必须做好登录状态验证和权限的控制,没有权限或没有登录的禁止下载导出。
7.8 防暴力破解
暴力破解是指使用数据字典等枚举方式逐个帐号进行密码的尝试破解,为了防止这种破解,在登录、注册、找回密码等页面必须设置好图形验证码或者其他验证,最好是做好错误次数的限制,例如密码输错5次,一个小时内禁止登录等。
7.9短信邮件安全
用到发送短信和发送邮件的页面,必须设置图形验证码,点击发送之前,验证图形验证码正确后再发送。最好限制每个手机号每天的发送数量。
密码明文传输
一般在前端页面表单提交的时候,用户输入密码是明文的。在服务器没有配置SLL证书(https)的情况下,必须先进行加密操作再提交,参数传递到后台接收后再进行解密操作。服务器配置SLL证书(https)可以自动进行加密传输,安全性更高。
7.10 使用外部文件
在前端页面中,禁止引入其他网站的静态文件(javascript、css、网络图片等),如果需要用到,必须下载到本地,使用相对地址引入文件。使用本地静态文件的优点:加载更快,安全,确保不失效。
7.11 密码安全
禁止将明文密码存储在数据库中,所有密码必须加密处理,防止用户数据被导出造成密码泄露。禁止在日志、调试信息文件、cookies中记录密码口令、银行账号、通信内容等敏感数据。为了保障帐号的安全,所有密码的长度强制限制在6位以上。
8、并发和大流量处理
8.1 重复提交
为了防止用户在提交表单时重复提交,表单必须设置提交验证。例如商城提交订单时,必须防止用户重复提交。Thinkphp框架中提供表单令牌的功能可以防止重复提交,原生php也可以在打开页面前生成token,并保存在session,然后将token传递到页面表单字段,在表单提交时将token一起提交,在后台接收时验证token,验证后销毁session的保存的token。
附文档链接 提取码 vn9d