[TOC]
# 前言
如果您已经决定向加入`山西创泰`并贡献代码,请详细阅读以下规范,并严格遵守。本规范由编程原则组成,融合并提炼了开发人员长时间积累下来的成熟经验,意在帮助开发者养成良好一致的编程风格。如果有需要,本文档会不定期更新。
# PHP编码规范与原则
文件编码及编辑器格式
请在开始编辑山西创泰代码之前调整你的编辑器设置。
本条规范同样适用于 `PHP`、`HTML`、`CSS`、`JavaScript`
## 编码
请调整您的编辑器文件编码为 `UTF-8`,并关闭 `UTF-8 BOM((Byte Order Mark))`的功能。我们要求使用`PHPStome` 等集成化的`IDE`。最好使用`PHPSTROM`。
切记请不要使用windows自带的记事本编辑项目文件。
注意:请确认你的编辑器不会有意或无意的保存文件为 `UTF-8 BOM` 格式, 否则可能造成山西创泰系统通信不正常。
## 缩进
每个缩进的单位约定是一个`Tab`(禁止设置为空格替代,Tab宽度应表示为4个空白字符宽度),需每个参与项目的开发人员在编辑器(`PHPSTROM`)中进行强制设定,以防在编写代码时遗忘而造成格式上的不规范。
## 换行
山西创泰项目中使用`Unix`风格的换行符,即只有换行( `LF` 或 `“\n”` )没有回车( `CR` 或 `“\r”` ),请在你的编辑器内调整
## 代码标记
PHP程序需使用 `<?php ?>` 来界定 `PHP` 代码,在`HTML`页面中嵌入纯变量时,可以使用 `<?php echo $variablename;?>` 这样的形式。
注意:为了使代码规范化和标准化,山西创泰开发中禁止使用 `<? ?>` 和 `<?=$variablename?>` 这种速记形式。
## 注释
注释是对于那些容易忘记作用的代码添加简短的介绍性内容。请使用`“/* */”`和`“//”`。
尽量使用变量名,整体逻辑结构来表达程序中的意图,对于特别难懂的逻辑或是算法可以添加注释以便于其他人员更方便理解意图,注释应该是解释 “为什么这么做” 而不是 “这是什么东西” 。
代码中不得包含任何调试性的代码(注释后也不可以),如果有一些未完成或是待完善的功能必须添加统一的注释标记“//debug”并后跟完整的注释信息
例如:
```
/**
* 多行注释请用这种格式
* @param string $memo 备注
* @return array
*/
$num = 1;
$flag = TRUE; //debug 这里不能确定是否需要对$flag进行赋值
if(empty($flag)) {
//Statements
}
```
# 书写规则
## 大括号{}、if和switch
首括号与关键词同行,尾括号与关键字同列;
`if` 结构中,`if`、`else` 和 `elseif` 与前后两个大括号同行,左右各一个空格。另外,即便 `if` 后只有一行语句,仍然需要加入大括号,以保证结构清晰;
`switch` 结构中,通常当一个 `case` 块处理后,将跳过之后的 `case` 块处理,因此大多数情况下需要添加 `break`。`break` 的位置视程序逻辑,与 `case` 同在一行,或新起一行均可,但同一 `switch` 体中,`break` 的位置格式应当保持一致。
以下是符合上述规范的例子:
```
if ($condition) {
//Statements
} else {
switch ($str) {
case 'abc':
$result = 'abc';
break;
default:
$result = 'unknown';
break;
}
}
```
## 运算符、小括号、空格、关键词和函数
每个运算符与两边参与运算的值或表达式中间要有一个空格;
左括号“(” 应和函数关键词紧贴在一起,除此以外应当使用空格将“(”同前面内容分开;
右括号“)”除后面是“)”,其他一律用空格隔开它们;
除字符串中特意需要,一般情况下,在程序以及HTML中不出现两个连续的空格;
任何情况下,PHP程序中不能出现空白的带有TAB或空格的行,即:这类空白行应当不包含任何TAB或空格。同时,任何程序行尾也不能出现多余的TAB或空格;
每段较大的程序体,上、下应当加入空白行,两个程序块之间只使用1个空行,禁止使用多行。
程序块划分尽量合理,过大或者过小的分割都会影响他人对代码的阅读和理解。一般可以以较大函数定义、逻辑结构、功能结构来进行划分。少于15行的程序块,可不加上下空白行;
说明或显示部分中,内容如含有中文、数字、英文单词混杂,应当在数字或者英文单词的前后加入空格。
根据上述原则,以下举例说明正确的书写格式:
```
$result = (($a + 1) * 3 / 2 + $num)) . 'Test';
$condition ? func1($var) : func2($var);
$condition ? $long_statement : $another_long_statement;
if ($flag) {
//Statements
//More than 15 lines
}
showmessage('请使用 restore.php 工具恢复数据。');
```
## 函数定义
参数的名字和变量的命名规范一致;
函数定义中的左小括号,与函数名紧挨,中间无需空格;
开始的左大括号与函数定义为同一行,中间加一个空格,不要另起一行;
具有默认值的参数应该位于参数列表的后面;
函数调用与定义的时候参数与参数之间加入一个空格;
必须仔细检查并切实杜绝函数起始缩进位置与结束缩进位置不同的现象;
例如,符合标准的定义:
```
function message($string, $operation, $key = '') {
if ($flag) {
//Statement
}
//函数体
}
```
不符合标准的定义:
```
function authcode($string,$operation,$key = '') {
//函数体
}
```
## 引号
由于PHP中单引号和双引号具有不同的含义,因此在使用时有如下原则:
在能使用单引号的情况下,禁止使用双引号。
字符串为固定值,不包含换号、制表等特殊转义时,需使用单引号。
字符串作为数据索引时,需使用单引号。
字符串不需要带入变量,需使用单引号。
数据库SQL语句中,所有数据必须加单引号,无论数值还是字串,以避免可能的注入漏洞和SQL错误。
```
$sql = "UPDATE " . tablename('members') . " SET adminid='1' WHERE AND adminid='2'";
```
## 数据库操作
为保证数据操作安全,数据库操作有以下处理及书写原则:
所有数据库查询时,尽量使用封装的 `pdo_getXXX` 系列函数,如果无法满足再考虑书写`SQL`语句使用 `pdo_fetchXXX` 系列函数
所有`SQL查询`关键字大写,方便代码审查
所有`SQL对象`(表名,字段名,索引名等)必须用反引号包括
所有编码参数查询,必须使用PDO的参数绑定机制处理
不能绑定参数处理的查询,必须处理好变量检测及字符串转义
这是一个完整的数据库操作示例:
```
//pdo_getXXX 系列函数,推荐使用
$trades = pdo_getall('trades', array('username' => $_GPC['username'], 'tid' => $_GPC['select']));
//直接书写SQL,需要自行处理参数转义等问题
$tids = array();
if(!empty($_GPC['select'])) {
foreach($_GPC['select'] as $t) {
$tids[] = intval($t); //---- 必须将输入参数转换为无安全隐患的格式,数字列必须转换为数字列,字符串列必须使用 addslashes
}
}
if(!empty($tids)) {
$sql = 'SELECT * FROM ' . tablename('trades') . ' WHERE `username`=:username AND `tid` IN (' . implode($tids) . ')';
$pars = array();
$pars[':username'] = $_GPC['username'];
$trades = pdo_fetchall($sql, $pars);
}
```
# 命名原则
命名是程序规划的核心。古人相信只要知道一个人真正的名字就会获得凌驾于那个人之上的不可思议的力量。只要你给事物想到正确的名字,就会给你以及后来的人带来比代码更强的力量。
名字就是事物在它所处的生态环境中一个长久而深远的结果。总的来说,只有了解系统的程序员才能为系统取出最合适的名字。如果所有的命名都与其自然相适合,则关系清晰,含义可以推导得出,一般人的推想也能在意料之中。
就一般约定而言,类、函数和变量的名字应该总是能够描述让代码阅读者能够容易的知道这些代码的作用。形式越简单、越有规则,就越容易让人感知和理解。应该避免使用模棱两可,晦涩不标准的命名。
## 变量、函数名
变量、函数名一律为`小写格式`;
变量与函数命名时一切使用单数形式。如果需要表达“多”的概念,可以使用 $goods_list 等数量词
以标准计算机英文为蓝本,杜绝一切拼音、或拼音英文混杂的命名方式;
## 变量名命名方式
变量名要完全、准确地描述出该变量所代表的事物。这种名字很容易阅读,没有晦涩的缩写,同时也没有歧义。
一个好记的名字反映的通常都是问题,而不是解决方案。一个好的名字通常表达的是“什么”,而不是“如何”。
比如:文章推荐列表应该命名成 `$article_recommend_list` 而且 `$data_list`
## 单词分隔
在函数中一定是使用下划线 `_` 分隔多个词
变量中比较短少的词组合尽量使用分隔符,比如:`商品id`可以直接写成 `$goods_id`,`$multi_id`,`$create_time`
变量中一些较长的,或是有两个以上含义的用下划线`_`分隔,比如:`$default_groupid`,`$today_recharge`,`$yestday_recharge`
## 循环中变量定义
循环内的局部变量也应该避免使用无意义的`$i`、`$j`等变量,比如循环商品 `$goods as $i => $goods_row`,如果键值不是存放着自增索引,也可以使用 `$goods as $goods_id => $goods_row`,比如循环文件 `$files as $i => $filename`。
## 常用缩写列表
除下方列表外,一切缩写皆不可使用
```
Argument -> arg
Command -> cmd
Configuration -> config
Identification -> id
Previous -> prev
Synchronize -> sync
Initialize -> init
Minimum -> min
Maximum -> max
```
## 类和接口名
类和接口的命名采用混合大小写字母的Pascal命名法
接口的命名与类相似,但需要以大写字母“I”开头,以作区分。
对象成员的命名则采用混合大小写字母的Camel命名法
```
class Loader {
function func($function_name) {
}
function fileCache() {
}
}
```
## JavaScript代码
`JavaScript`中类和全局对象应使用混合大小写字母的`Pascal`命名法
`JavaScript`中变量、函数名应采用混合大小写字母的`Camel`命名法。
`JavaScript`中与 `PHP` 类中书写规范一致
## 常量
常量是增强代码语义重要的一个手段,大多数我们在保存类型或是其它数据时会采用`int`数字来表示,这些数字是没有意义的,这时候我们可以用常量的方式来处理这些数据。
常量应该总是全部使用`大写字母`命名,必要的情况下,可使用划线来分隔单词;
`PHP` 的内建值 `true`、`false` 和`null`必须全部采用小写字母书写。
# 封装
增加语义上的理解,使结构更清晰。
增加复用,减少冗余,方便更改。
封装功能并不是简单的把代码复制到函数里,要进行功能的抽象和拆解,比如:上传微信图文素材和本地图文素材转换成微信素材,这两个功能中“将某些数据保存至微信”是他们两个共有的功能,则会封装 `material_local_news_upload()` ,封装的时候,尽量的不太过多的参与到业务中,外部是负责把数据结构拼接好,函数内部只要处理自己的功能就好,而且要遵循“单一原则”规则。写好函数后,自己思考一下,这个函数如果脱离这个业务是否还可以在其它地方使用?
## 封装时机
获取整体信息时
获取目标对象的信息时,一定要考虑是否还关联着其它信息。比如获取公众号时,公众号除主体信息外,还会关联着可用模块、所有者、权限等数据,在获取时应该一并获取出来。
## 重复使用的代码
如果相同的代码在不同位置出现两次及以上,就要考虑封装。封装时要把公共的功能抽象出来进行封装,而不是单纯的复制+粘帖。比如:切换公众号和切换小程序,会将`uni_account_save_switch()`和 `uni_account_last_switch()`进行封装
## 晦涩难理解的代码
在写代码时提倡优雅的书写代码,实现功能。但是某些时候可能还是会有一些难以理解的代码,就要考虑封装,封装时一定要注意函数的命名。
## 大段的功能操作
一个功能如果需要大量的代码,比如添加公众号,需要先处理主体信息,还要根据不同的类型处理不同的表,还要处理用户权限,模块等等,此时就要考虑封装。
## 其它功能可能会用到
功能相互间不可能是独立的,总是会在一些功能里用到额外的一些功能或是数据。我们在书写功能时,要长远的考虑一下,比如一些基本信息,基本的操作外部可能会调用,就要考虑封装。比如,会员数据的获取,更新等等
# 参数传递
无论是在功能函数中还是控制中,我们都需要接收外部传来的某一些参数来实现功能,我们可以理解成这些参数是我们内部提供给外部的一些访问接口。
外部提供过来的数据是不可靠也不可信的,外部过来的数据我们必须要进行验证,传递的数据越多,要验证的数据就越多,代码也就越复杂。
通常我们要尽量减少外部传递的参数,以最小设计(最小传递)来做,能传一个不传两个,更多的值通过内部来获取
# 变量的初始化与逻辑检查
任何变量在进行累加、直接显示或存储前必需进行初使化 ,例如:
```
$number = 0; // 数值型初始化
$string = ''; // 字符串初始化
$array = array(); // 数组初始化
```
判断一个无法确定(不知道是否已被赋值)的变量时,可用 `empty()` 或 `isset()`,而不要直接使用`if($switch)`的形式。
`empty()`和`isset()`的区别为:请参阅`PHP手册`。
如果已经使用 `unset()` 释放了一个变量之后,它将不再是 `isset()`。若使用 `isset()` 测试一个被设置成 `NULL` 的变量,将返回 `FALSE`。同时要注意的是一个 `NULL` 字节 `"\0"` 并不等同于 `PHP` 的 `NULL` 常数。
判断一个变量是否为数组,请使用`is_array()`,这种判断尤其适用于对数组进行遍历的操作,例如`foreach()`,因为如果不事先判断,`foreach()`会对非数组类型的变量报错;
判断一个数组元素是否存在,可使用`isset($array['key'])`,也可使用`empty()`。
# 安全性
PHP中的变量不并不像C语言那样需要事先声明,解释器会在第一次使用时自动创建他们,同样类型也不需要指定,解释器会根据上下文环境自动确定。从开发人员的角度来看,这无疑是一种极其方便的处理方法。
一个变量被创建了,就可以在程序中的任何地方使用。这导致的结果就是开发人员工经常不注意初始化变量。因此,为了提高程序的安全性,我们不能相信任何没有明确定义的变量。所有的变量在定义使用前要初使化以防止恶意构造提交的变量覆盖程序中使用的变量。
不要相信任何客户端提交的数据是安全的。(包括:`$_GET`、`$_POST`、`$_COOKIE`、`$_FILES`、`$_SERVER`、`$_REQUEST`),获取数据时必须使用安全相关函数获取值,请查看
# 其他细节问题
山西创泰中每个文件的结构如下,必须严格按照此规范
```
<?php
/**
* 当前文件功能说明
* [WeEngine System] Copyright (c) 2013 - 2017 WE7.CC
*/
define('IN_SYS', true); //入口常量定义
require '../framework/bootstrap.inc.php'; //加载引导文件
require IA_ROOT . '/web/common/bootstrap.sys.inc.php';
load()->web('common'); //各种载加函数
load()->func('communication');
load()->model('cloud');
load()->classs('coupon');
$dos = array('post', 'delete', 'display'); //文件中操作访问控制
$do = in_array($do, $dos) ? $do : 'post';
$_W['page']['title'] = '页面标题'; //页面标题及一些公共代码
$id = intval($_GPC['id']);
if ($do == 'post') { //各分支代码
} elseif ($do == 'delete') {
}
```
# 其他注意要点
正则表达式操作请使用`perl`兼容正则表达式,即 `preg_` 系列的函数。以提升效率。
尽量使用高版本的函数。
本项目不使用命名空间,因此需要按照传统的方式使用文件名和文件夹来规划代码结构。