Manual:Development Covenant/zh
From Kiwiphp
kiwiphp倡导契约开发,因此其中有不少约定,仔细阅读这个开发规范有助于更好的使用kiwiphp。
注:此规范是2006年为一个初级工程师组成的小型团队订立,教条性的语句较多,请读者遇到这类语句自动无视-_-
Contents |
数据库设计规范
范式
所有数据表都必须满足第一范式。
大部分表还满足第三范式,小部分表出于性能的因素设置部分缓存字段,不满足第三范式。
表名和字段名
参见命名规范部分。
数据类型
对MySQL只有以下数据类型会被用到:
char
定长字段,比如32位密码,在对性能要求高的定长表使用。
int和bigint
凡数字字段都使用int类型,极少数情况下可以用bigint,但不使用MySQL独有的tinyint, smallint, mediumint。仅作为开关使用的字段,也用int类型,不用enum类型。
text
凡超过一定长度(255字节)的文本字段都使用text类型。
varchar
小于255字节的文本字段使用varchar类型。
decimal
用于在对数据精确性要求较高的程序中存储带小数的数据。
是否允许为空
除了主键ID设置为NOT NULL,其他字段都允许为NULL。
默认值
无需指定默认值,改在PHP框架或者Controller中实现。
索引
创建数据库的时候,除了主键默认就有唯一索引,其他所有都不加。等性能测试和调优的时候统一加。
注释
除了按照下面的命名规范做到见名知意外,每个数据表,以及表中除了特殊字段之外的其他字段,都必须写注释。尤其是int类型用作开关(模拟枚举,enum)的时候,哪个数字代表什么意思必须有完整的注释。
特殊字段
每一个表至少应该有如下三个字段:
id
主键ID。
INT(多数情况下)或者BIGINT类型,多数情况下自增。
created
创建时间。
int类型,存储unix timestamp。
modified
最后修改。
int类型,存储unix timestamp。
树型表的特殊字段
我们还有一种特殊的表,用于存储树型结构的层次化数据。此模型请参考Windows资源管理器的文件夹树,或者MSDN左边框的导航树。
这种表还应该具有如下几个特殊字段:
family_id
家族ID。
INT类型(多数情况下)或者BIGINT类型。
同一个树上的所有节点拥有相同的家族ID,这个ID的值就是根节点的主键ID值。
x
水平方向上的位置。
INT类型。
值越大,越靠右。
y
竖直方向上的位置。
INT类型。
值越大,越靠下。
ACL表的特殊字段
role_id
角色ID。
INT类型。
controller
控制器名称。
VARCHAR类型。
action
(控制器)方法名。
VARCHAR类型。
switch
是否允许操作的开关。
INT类型。
字符集
都使用UTF-8编码。
命名规范
两种命名风格
C Style
一种是C style的underscore_words风格,所有的单词都小写,中间用下划线隔开,这种风格,我们下面简称C Style。
CPP Style
另外一种是C++ Style的camelizedWord风格,不使用下划线,每个单词的首字母大写,第一个单词除外。我们下面简CPP Style。
字符组成
若采用C Style的命名规则,一个名字只能由小写字母和下划线组成。
若采用CPP Style的命名规则,一个名字只能由大小写字母组成。不能出现下划线。
我们的各种命名目前还没有数字出现。
基本原则
见名知意
一个名字应当准确无误的反应它的功用,因此:
必须使用恰当的英文单词或者约定俗成的英文缩写
loopTimes arrayLength memberTotal article httpClient 这些都是符合要求的好名字。
不准使用汉语拼音或者拼音缩写
yonghu(用户) grjl(个人简历) 这样的汉语拼音或者拼音缩写不能使用。
长短适中
名字过长会造成输入工作量大,且容易出现拼写错误(在某些没有IDE提示和自动完成的场合),名字太短则容易违反“见名知意”的原则。
一个合适的名字应该在6-16个字符。
不准使用过短且没有意义的名字
如a = 1; b = 2; c = a + b;
只有约定俗成的i,j,k和x,y等例外。
i,j,k一般是循环计数变量,x,y一般是坐标系中的坐标。
不要使用不必要的缩写
如rec(record)、 resu(result)、fh(fileHandle)、rs(recordset)、conn(connection) 这些名字,如果写完整的单词也不会太长,省略这么几个字母,录入量没有减轻多少,可读性却急剧下降。
但,info(information),id(identification),min(minimum),max(maximum)等生活中常用缩略词是允许使用的。
尽量控制在3个单词,16个字符以内
如果名字太长,在不违背上述原则的前提下,考虑用简洁的等价方案。
数据库
采用C Style。
类
采用CPP Style,第一个单词的第一个字母也大写。就像这样:class Demo
方法
采用CPP Style。
前缀
方法名一般拥有下面这样的前缀:
add
增加。
对应数据库层的insert。
del
删除。
对应数据库层的delete。
get
获取。
对应数据库层的select。
set
修改。
对应数据库层的update。
后缀
Controller里面用于处理表单的方法(即form action指向的方法)和显示该表单的方法名称存在这样的关系:显示该表单的方法名称+Action后缀=用于处理表单的方法名称
比如,修改个人资料的界面是调用这个方法:User::setProfile(),则处理用户提交的资料修改请求的方法名称是:User::setProfileAction()。
特殊方法
构造器
构造器的方法名称都是:__construct()
注意,是两个下划线。
到目前为止,Controller都是没有自定义的构造器的,构造器方法只出现在框架里面。
私有方法
私有方法以下划线开头,如:_getAttachment($mime)
变量
采用CPP Style。
例外:因为字符串索引严格区分大小写,为了避免麻烦,数组变量的字符串索引采用C Style,就像这样:$certainArray['a_good_index_name']
文件
采用C Style。
Windows平台对文件名的大小写不敏感,但是Unix/Linux平台严格区分大小写,因此,所有文件(注意,是所有,不仅仅PHP,资源文件也受此限制)一律使用C Style的命名。
Controller类的文件名就是其类名去掉“Controller”后缀之后的C Style版本,比如UserController控制器的文件名是:user.php
Model类的文件名是其类名的C Style版本,比如EntryTagMapping模型的文件名是:entry_tag_mapping.php View的文件名比较特殊,原则上,它应该是方法名的C Style版本,且存储在以Controller名的子目录下——绝大部分情况下也的确如此,比如,UserController::setProfile()方法对应的View就是:views/user/set_profile.php。
URL
采用C Style。
URL中非用户输入部分(比如查询的关键字)全部小写,URL看起来都是这个样子的:
项目根目录/controller名(C Style)/方法名(C Style)/参数名1/值1/参数名2/值2
例如:magic/passport/user/view/id/3/username/chin
URL 中的参数名若将被传递给Model层,则应该和数据库中对应的字段名保持一致。
表单
采用C Style。
表单元素名若将被传递给Model层,则应该和数据库中对应的字段名保持一致。
编码规范
文件格式
文件开头
除了View,其他php文件必须以
哪怕是个空文件。 不准使用
文件结尾
除了View,其他PHP文件省略PHP闭合标签
除了View文件,其他PHP文件不存在任何输出,整个文件都是一段PHP代码,只有一个标签,这种情况下省略这唯一一个在文件末尾的PHP闭合标签“?>”,不会影响PHP的正常执行。 闭合标签“?>”之后的空白不易被察觉,容易导致包含它的文件往浏览器发送header时出错。
举例说明:
设有一个config.php,存储数据库连接信息:
<?php //config.php $config['db']['host'] = 'localhost'; ?> 这里才是文件结尾,从“?>”到这里有若干个空格和一个空行,这在编辑器里面是不容易被察觉的。用鼠标拖选就能看到效果了。
有一个admin.php是后台管理界面,需要包含config.php以取得数据库连接信息:
<?php //admin.php include('config.php'); if ('administrator' != $_SESSION['role']) { header('Location: login.php'); } ?>在这里例子里面,config.php文件末尾多余的空格会被当作Content被发送给客户端,当admin.php再发送Header的时候就会出错:在发送Header之前发送了Content。
文件末尾空行
根据Unix环境下的习惯和规范,每个文件的末尾都有一个空行。
换行
用unix标准的LF换行
标准的Unix换行符是LineFeed,它的十六进制代码是0x0A。
行尾不要有多于的空格
有的编辑器会自动去除行尾的空格,因此我们不在行尾加空格。
代码格式
目标:不依赖特定的编辑器,无论在Linux还是Windows下打开,无论是记事本还是Eclipse,都应该看到统一的,一样的格式。
换行
每个语句在单独的一行
除了for循环的圆括号里面可以有三个语句
for($i = 0; $i < $loopTimes; $i ++)
一个语句应该控制在一行之内
下列情况特别注意:
语句包含比较复杂的数组定义
$config = array( 'db' => array( 'host' => 'devserver', 'dbname' => 'base' ) );
不要用array()构造数组,改为给数组变量赋值:
$config['db']['host'] = 'devserver'; $config['db']['dbname'] = 'base';
空行
只有Controller中属性(function之外定义的变量)或方法(function)之间可以空一行。其他地方不能空行。
缩进
缩进单位
使用一个tab作为缩进单位,而不是四个空格。
如果需要输出tab,用\t而不是一个tab。
缩进场合
花括号中的代码块
class DemoClass { public function demoMethod() { if ($condition) { for (;;) { doSomething(); } } } }
switch case中的代码块
switch ($condition) { case 1: doSomething(); break; case 2: doSomething(); break; }
数组元素分行
$statusMessage = array( 200 => 'OK.', 401 => 'Unauthorized.', 403 => 'Forbidden.', 404 => 'Not found.', 406 => 'Not acceptable.', 417 => 'Expectation failed.', 500 => 'Internal server error.' );
空格
操作符的前后空一格
$key = $value; $this->template = 'xmlhttp' == $this->userAgent ? null : 'form'; $i ++;
if, switch, for, foreach, while等控制结构的左括号(即'(')前空一格
if (isset($status)) switch ($condition) foreach ($vars as $key => $value) while ($score_segment < 101)
参数列表的逗号后面空一格
$uriArray = explode('/', $uriString);
花括号
花括号用于界定代码块。
即便代码块只有一个语句甚至为空,也不可省略花括号
代码块只有一个语句的时候,省略花括号是被编译器允许的,但是日后给这个代码块增加一行的时候,很可能会忘记加上花括号。因此,无论代码块有多少行,都不能省去花括号。
if ($condition) { echo "there is only one statement"; } for (;;) { //do nothing }
一个花括号单独占一行
尤其是起始花括号,不用Pear和C风格的。 C和Pear是这样的:
if ($condition) { for (;;) { doSomething(); } }
配对的花括号在同一列上
如果缩进是正确的,则代码必然满足这个要求。 花括号之前有且仅有n个tab,之后没有空格(或者tab),之前的n个tab是缩进产生的。
综合实例
class DemoClass { public function demoMethod() { if ($condition) { for (;;) { doSomething(); } } } }
注释
综合权衡写注释的时间成本和收益,规定只在两种地方写注释:
被其他人调用的类和方法
注释格式:PHPDoc
/** * Database Table abstract */ abstract class DbTable { /** * The primary key field name * * @var string */ protected $_primaryKey; /** * A shortcut to SELECT COUNT(*) FROM table WHERE condition * * @param array $args * @return integer */ public function count($args = null) { return $row['total']; } }
算法或者业务逻辑非常复杂的代码段
注释格式:comment
/** * get application root directory, who contains controllers, models, views * then load the application configuration * Note: SCRIPT_FILENAME = /path/to/index.php, not /path/to/kiwi.php */ $appDir = dirname($_SERVER['SCRIPT_FILENAME']) . '/app/';

