《PHP程序编码规范标准.doc》由会员分享,可在线阅读,更多相关《PHP程序编码规范标准.doc(44页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、PHP程序编码规范标准最后修改日期: 2002-01-23PHP编程标准是经由Todd Hoff许可,基于C+ 编程标准为PHP而重写的,作者为Fredrik Kristiansen,使用本标准,如果您想拷贝一份留做自用的话,那是完全免费的,这也是我们制作它的原因。假如您发现了任何的错误又或者是有任何的改进,请您给笔者发一个email,以便笔者将它们合并到最新更新中去。目录 介绍 o 标准化的重要性 o 解释 o 认同观点 命名规则 o 合适的命名 o 缩写词不要全部使用大写字母 o 类命名 o 类库命名 o 方法命名 o 类属性命名 o 方法中参数命名 o 变量命名 o 引用变量和函数返回引
2、用 o 全局变量 o 定义命名 / 全局常量 o 静态变量 o 函数命名 o php文件扩展名 文档规则 o 评价注释 o Comments Should Tell a Story o Document Decisions o 使用标头说明 o Make Gotchas Explicit o Interface and Implementation Documentation o 目录文档 复杂性管理规则 o 层 o Open/Closed Principle o sever configuration 类规则 o Different Accessor Styles o 别在对象架构期做实际的
3、工作 o Thin vs. Fat Class Interfaces o 短方法 进程规则 o Use a Design Notation and Process o Code Reviews o Create a Source Code Control System Early and Not Often o Create a Bug Tracking System Early and Not Often o Honor Responsibilities 格式化 o 大括号 规则 o 缩进/制表符/空格 规则 o 小括号、关键词和函数 规则 o If Then Else 格式 o switc
4、h 格式 o continue,break 和 ? 的使用 o 每行一个语句 o 声明块的定位 杂项 o 不要不可思议的数字 o 错误返回检测规则 o 不要采用缺省值测试非零值 o 布尔逻辑类型 o 通常避免嵌入式的赋值 o 重用您和其他人的艰苦工作 o 使用if (0)来注释外部代码块 o 其他杂项 PEAR编码标准o Pear-Indenting_o Pear-Control Structures_o Pear-Function Calls o Pear-Function Definitionso Pear-Comments_o Pear-Including Codes o Pear-PH
5、P Code Tags_o Pear-Header Comment Blocks_o Pear-Using CVSo Pear-Example URLso Pear-Naming Conventions_介绍标准化的重要性 标准化问题在某些方面上让每个人头痛,让人人都觉得大家处于同样的境地。这有助于让这些建议在许多的项目中不断演进,许多公司花费了许多星期逐子字逐句的进行争论。标准化不是特殊的个人风格,它对本地改良是完全开放的。优点当一个项目尝试着遵守公用的标准时,会有以下好处: 程序员可以了解任何代码,弄清程序的状况 新人可以很快的适应环境 防止新接触php的人出于节省时间的需要,自创一套风格
6、并养成终生的习惯 防止新接触php的人一次次的犯同样的错误 在一致的环境下,人们可以减少犯错的机会 程序员们有了一致的敌人 :-) 缺点现在轮到坏处了: 因为标准由一些不懂得php的人所制定,所以标准通常看上去很傻 因为标准跟我做的不一样,所以标准通常看上去很傻 标准降低了创造力 标准在长期互相合作的人群中是没有必要的 标准强迫太多的格式 总之人们忽视标准 讨论许多项目的经验能得出这样的结论:采用编程标准可以使项目更加顺利地完成。标准是成功的关键么?当然不。但它们可以帮助我们,而且我们需要我们能得到的所有的帮助!老实说,对一个细节标准的大部分争论主要是源自自负思想。对一个合理的标准的很少决定能
7、被说为是缺乏技术性的话,那只是口味的原因罢了。所以,要灵活的控制自负思想,记住,任何项目都取决于团队合作的努力。解释 惯例在本文档中使用“要”字所指的是使用本规范的所有项目需要遵守规定的标准。 使用“应该”一词的作用是指导项目定制项目细节规范。因为项目必须适当的包括 (include),排除(exclude)或定制(tailor)需求。 使用“可以”一词的作用与“应该”类似,因为它指明了可选的需求。在本文档中使用“要”字所指的是使用本规范的所有项目需要遵守规定的标准。 使用“应该”一词的作用是指导项目定制项目细节规范。因为项目必须适当的包括 (include),排除(exclude)或定制(t
8、ailor)需求。 使用“可以”一词的作用与“应该”类似,因为它指明了可选的需求。 标准实施 首先应该在开发小组的内部找出所有的最重要的元素,也许标准对你的状况还不够恰当。它可能已经概括了 重要的问题,也可能还有人对其中的某些问题表示强烈的反对。无论在什么情况下,只要最后顺利的话,人们将成熟的明白到这个标准是合理的,然后其他的程序员们也会发现它的合理性,并觉得带着一些保留去遵循这一标准是值得的。如果没有自愿的合作,可以制定需求:标准一定要经过代码的检验。如果没有检验的话,这个解决方案仅仅是一个建立在不精确的基础上的一大群可笑的人。 认同观点 1. 这行不通; 2. 也许可行吧,但是它既不实用又
9、无聊; 3. 这是真的,而且我也告诉过你啊; 4. 这个是我先想到的; 5. 本来就应该这样。 如果您带着否定的成见而来看待事物的话,请您保持开放的思想。你仍可以做出它是废话的结论,但是做出结论的方法就是你必须要能够接受不同的思想。请您给自己一点时间去做到它。命名规则 合适的命名命名是程序规划的核心。古人相信只要知道一个人真正的名字就会获得凌驾于那个人之上的不可思议的力量。只要你给事物想到正确的名字,就会给你以及后来的人带来比代码更强的力量。别笑!名字就是事物在它所处的生态环境中一个长久而深远的结果。总的来说,只有了解系统的程序员才能为系统取出最合适的名字。如果所有的命名都与其自然相适合,则关
10、系清晰,含义可以推导得出,一般人的推想也能在意料之中。如果你发觉你的命名只有少量能和其对应事物相匹配的话, 最好还是重新好好再看看你的设计吧。类命名 在为类(class )命名前首先要知道它是什么。如果通过类名的提供的线索,你还是想不起这个类是什么的话,那么你的设计就还做的不够好。 超过三个词组成的混合名是容易造成系统各个实体间的混淆,再看看你的设计,尝试使用(CRC Se-ssion card)看看该命名所对应的实体是否有着那么多的功用。 对于派生类的命名应该避免带其父类名的诱惑,一个类的名字只与它自身有关,和它的父类叫什么无关。 有时后缀名是有用的,例如:如果你的系统使用了代理(agent
11、 ),那么就把某个部件命名为“下载代理”(DownloadAgent)用以真正的传送信息。 方法和函数命名 通常每个方法和函数都是执行一个动作的,所以对它们的命名应该清楚的说明它们是做什么的:用CheckForErrors()代替ErrorCheck(),用DumpDataToFile()代替DataFile()。这么做也可以使功能和数据成为更可区分的物体。 有时后缀名是有用的: o Max - 含义为某实体所能赋予的最大值。 o Cnt - 一个运行中的计数变量的当前值。 o Key - 键值。 例如:RetryMax 表示最多重试次数,RetryCnt 表示当前重试次数。 有时前缀名是有用
12、的: o Is - 含义为问一个关于某样事物的问题。无论何时,当人们看到Is就会知道这是一个问题。 o Get - 含义为取得一个数值。 o Set - 含义为设定一个数值 例如:IsHitRetryLimit。 缩写词不要全部使用大写字母 无论如何,当遇到以下情况,你可以用首字母大写其余字母小写来代替全部使用大写字母的方法来表示缩写词。使用: GetHtmlStatistic. 不使用: GetHTMLStatistic.理由 当命名含有缩略词时,人们似乎有着非常不同的直觉。统一规定是最好,这样一来,命名的含义就完全可以预知了。 举个NetworkABCKey的例子,注意C是应该是ABC里面
13、的C,还是key里面的C,这个是很令人费解的。有些人不在意这些,其他人却很讨厌这样。所以你会在不同的代码里看到不同的规则,使得你不知道怎么去叫它。例如 class FluidOz / 不要写成 FluidOZ class GetHtmlStatistic / 不要写成 GetHTMLStatistic类命名 使用大写字母作为词的分隔,其他的字母均使用小写 名字的首字母使用大写 不要使用下划线(_) 理由 根据很多的命名方式,大部分人认为这样是最好的方式。 例如 class NameOneTwo class Name类库命名 目前命名空间正在越来越广泛的被采用,以避免不同厂商和团体类库间的类名冲
14、突。 当尚未采用命名空间的时候,为了避免类名冲突,一般的做法是在类名前加上独特的前缀,两个字符就可以了,当然多用一些会更好。 例如John Johnson的数据结构类库可以用Jj做为前缀,如下: class JjLinkList 方法命名 采用与类命名一致的规则 理由 使用所有不同规则的大部分人发现这是最好的折衷办法。 例如 class NameOneTwo function DoIt() ; function HandleError() ; 类属性命名 属性命名应该以字符m为前缀。 前缀m后采用于类命名一致的规则。 m总是在名字的开头起修饰作用,就像以r开头表示引用一样。 理由 前缀m防止类
15、属性和方法名发生任何冲突。你的方法名和属性名经常会很类似,特别是存取元素。 例如 class NameOneTwo function VarAbc() ; function ErrorNumber() ; var mVarAbc; var mErrorNumber; var mrName; 方法中参数命名 第一个字符使用小写字母。 在首字符后的所有字都按照类命名规则首字符大写。 理由 你可以随时知道那个变量对应那个变量。 Example class NameOneTwo function StartYourEngines(&$someEngine, &$anotherEngine) $this
16、-mSomeEngine = $someEngine; $this-mAnotherEngine = $anotherEngine; var $mSomeEngine; var $mAnotherEngine;变量命名 所有字母都使用小写。 使用_作为每个词的分界。理由 通过这一途径,代码中变量的作用域是清晰的。 所有的变量在代码中都看起来不同,容易辨认。例如function HandleError($errorNumber) $error = OsErr(); $time_of_error = $error-getTimeOfError(); $error_processor = $erro
17、r-getErrorProcessor();数组要素数组要素命名遵循变量命名规则。 使用_作为每个词的分界。 不使用-作为每个词的分界。理由 如果使用-作为每个词的分界,数组元素可通过不可思议引号被访问。例如$myarrfoo_bar = Hello;$myarrfoo-bar = Hello;print $myarrfoo_bar world; / will output: Hello worldprint $myarrfoo-bar world; / parse errorSingle or Double Quotes 访问数组元素不需要单引号或双引号.理由 在不可思议的引号中,元素可被访
18、问。 在不可思议的引号中,元素名可是一个变量。例如$myarrfoo_bar = Hello;$element_name = foo_bar;print $myarrfoo_bar world; / will output: Hello worldprint $myarr$element_name world; / will output: Hello worldprint $myarr$element_name world; / parse errorprint $myarr$element_name world; / parse error引用变量和函数返回引用 引用必须带r前缀 理由 使
19、得类型不同的变量容易辨认 它可以确定哪个方法返回可更改对象,哪个方法返回不可更改对象。 例如class Test var $mrStatus; function DoSomething(&$rStatus) ; function &rStatus() ; 全局变量 全局变量应该带前缀g。 理由 知道一个变量的作用域是非常重要的。 例如global $gLog;global &$grLog;定义命名 / 全局常量 全局常量用_分隔每个单词。 理由这是命名全局常量的传统。你要注意不要与其它的定义相冲突。 例如define(A_GLOBAL_CONSTANT, Hello world!);静态变量
20、静态变量应该带前缀s。 理由 知道一个变量的作用域是非常重要的。 例如function test() static $msStatus = 0;函数命名 函数名字采用C GNU的惯例,所有的字母使用小写字母,使用_分割单词。 理由 这样可以更易于区分相关联的类名。 例如function some_bloody_function()错误返回检测规则 检查所有的系统调用的错误信息,除非你要忽略错误。 为每条系统错误消息定义好系统错误文本以便include。 大括号 规则在三种主要的大括号放置规则中,有两种是可以接受的,如下的第一种是最好的: 将大括号放置在关键词下方的同列处: if ($condi
21、tion) while ($condition) . . 传统的UNIX的括号规则是,首括号与关键词同行,尾括号与关键字同列: if ($condition) while ($condition) . . 理由 引起剧烈争论的非原则的问题可通过折衷的办法解决,两种方法任意一种都是可以接受的,然而对于大多数人来说更喜欢第一种。原因就是心理研究学习范畴的东西了。 对于更喜欢第一种还有着更多的原因。如果您使用的字符编辑器支持括号匹配功能的话(例如vi),最重要的就是有一个好的样式。为什么?我们说当你有一大块的程序而且想知道这一大块程序是在哪儿结束的话。你先移到开始的括号,按下按钮编辑器就会找到与之对
22、应的结束括号,例如: if ($very_long_condition & $second_very_long_condition) . else if (.) . 从一个程序块移动到另一个程序块只需要用光标和你的括号匹配键就可以了,不需要来回的移动到行末去找匹配的括号。 缩进/制表符/空格 规则 使用三到四个空格为每层次缩进。 不用再使用制表符,大多编辑程序可以用空格代替制表符。 不再使用只要一有需要就缩排的方法。对与最大缩进层数,并没有一个固定的规矩,假如缩进层数大于四或者五层的时候,你可以考虑着将代码因数分解(factoring out code)。理由 当人们使用差异太大的制表符标准的
23、话,会使阅读代码变得很费力。 没有人同意正确的空格数,只是保持一致。总体上,使用3或4个空格为每层缩进。 如此多的人愿意限定最大的缩进层数,它通常从未被看作是一件工作。我们相信程序员们会明智的选择嵌套的深度。 例如function func() if (something bad) if (another thing bad) while (more input) 小括号、关键词和函数规则 不要把小括号和关键词紧贴在一起,要用空格隔开它们。 不要把小括号和函数名紧贴在一起。 除非必要,不要在Return返回语句中使用小括号。 理由 关键字不是函数。如果小括号紧贴着函数名和关键字,二者很容易被看
24、成是一体的。 例如if (condition) while (condition) strcmp($s, $s1);return 1;别在对象架构期做实际的工作别在对象架构期做真实的工作,在架构期初始化变量和/或做任何不会有失误的事情。 当完成对象架构时,为该对象建立一个Open()方法,Open()方法应该以对象实体命名。 理由 构造不能返回错误 。 例如class Device function Device() /* initialize and other stuff */ function Open() return FAIL; ; $dev = new Device; if (FA
25、IL = $dev-Open() exit(1);Make Functions Reentrant rs函数不能保持避免函数重新导入的静态变量。If Then Else 格式布局这由程序员决定。不同的花括号样式会产生些微不同的样观。一个通用方式是: if (条件1) / 注释 else if (条件2) / 注释 else / 注释 如果你有用到else if 语句的话,通常最好有一个else块以用于处理未处理到的其他情况。可以的话放一个记录信息注释在else处,即使在else没有任何的动作。 条件格式总是将常量放在等号/不等号的左边,例如: if ( 6 = $errorNum ) . 一个
26、原因是假如你在等式中漏了一个等号,语法检查器会为你报错。第二个原因是你能立刻找到数值而不是在你的表达式的末端找到它。需要一点时间来习惯这个格式,但是它确实很有用。 switch 格式 Falling through a case statement into the next case statement shall be permitted as long as a comment is included. default case总应该存在,它应该不被到达,然而如果到达了就会触发一个错误。 如果你要创立一个变量,那就把所有的代码放在块中。 例如switch (.) case 1: . /
27、FALL THROUGH case 2: $v = get_week_number(); . break; default: continue,break 和 ? 的使用: Continue 和 Break Continue 和 break 其实是变相的隐蔽的 goto方法。 Continue 和 break 像 goto 一样,它们在代码中是有魔力的,所以要节俭(尽可能少)的使用它们。使用了这一简单的魔法,由于一些未公开的原因,读者将会被定向到只有上帝才知道的地方去。 Continue有两个主要的问题: 它可以绕过测试条件。 它可以绕过等/不等表达式。 看看下面的例子,考虑一下问题都在哪儿发
28、生: while (TRUE) . / A lot of code . if (/* some condition */) continue; . / A lot of code . if ( $i+ STOP_VALUE) break;注意:A lot of code是必须的,这是为了让程序员们不能那么容易的找出错误。通过以上的例子,我们可以得出更进一步的规则:continue 和 break 混合使用是引起灾难的正确方法。 ?: 麻烦在于人民往往试着在 ? 和 : 之间塞满了许多的代码。以下的是一些清晰的连接规则: 把条件放在括号内以使它和其他的代码相分离。 如果可能的话,动作可以用简单的
29、函数。 把所做的动作,“?”,“:”放在不同的行,除非他们可以清楚的放在同一行。 例如(condition) ? funct1() : func2(); or (condition) ? long statement : another long statement;声明块的定位 声明代码块需要对齐。 理由 清晰。 变量初始化的类似代码块应该列表。 The &token should be adjacent to the type, not the name. 例如var $mDate var& $mrDate var& $mrName var $mName $mDate = 0; $mrDa
30、te = NULL; $mrName = 0;$mName = NULL;每行一个语句除非这些语句有很密切的联系,否则每行只写一个语句。 短方法 方法代码要限制在一页内。 理由 这个思想是,每一个方法代表着一个完成单独目的的技术。 从长远来说,过多的无效参数是错误的。 调用函数比不调用要慢,但是这需要详细考虑做出决定(见premature optimization 未完善的优化)。 记录所有的空语句总是记录下for或者是while的空块语句,以便清楚的知道该段代码是漏掉了,还是故意不写的。 while ($dest+ = $src+) ; / VOID不要采用缺省方法测试非零值不要采用缺省值测
31、试非零值,也就是使用: if (FAIL != f()比下面的方法好: if (f()即使 FAIL 可以含有 0 值 ,也就是PHP认为false的表示。在某人决定用-1代替0作为失败返回值的时候,一个显式的测试就可以帮助你了。就算是比较值不会变化也应该使用显式的比较;例如:if (!($bufsize % strlen($str)应该写成:if ($bufsize % strlen($str) = 0)以表示测试的数值(不是布尔)型。一个经常出问题的地方就是使用strcmp来测试一个字符等式,结果永远也不会等于缺省值。 非零测试采用基于缺省值的做法,那么其他函数或表达式就会受到以下的限制:
32、 只能返回0表示失败,不能为/有其他的值。 命名以便让一个真(true)的返回值是绝对显然的,调用函数IsValid()而不是Checkvalid()。 布尔逻辑类型 大部分函数在FALSE的时候返回0,但是发挥非0值就代表TRUE,因而不要用1(TRUE,YES,诸如此类)等式检测一个布尔值,应该用0(FALSE,NO,诸如此类)的不等式来代替: if (TRUE = func() .应该写成: if (FALSE != func() .通常避免嵌入式的赋值有时候在某些地方我们可以看到嵌入式赋值的语句,那些结构不是一个比较好的少冗余,可读性强的方法。 while ($a != ($c = g
33、etchar() process the character +和-操作符类似于赋值语句。因此,出于许多的目的,在使用函数的时候会产生副作用。使用嵌入式赋值提高运行时性能是可能的。无论怎样,程序员在使用嵌入式赋值语句时需要考虑在增长的速度和减少的可维护性两者间加以权衡。例如:$a = $b + $c; $d = $a + $r;不要写成:$d = ($a = $b + $c) + $r;虽然后者可以节省一个周期。但在长远来看,随着程序的维护费用渐渐增长,程序的编写者对代码渐渐遗忘,就会减少在成熟期的最优化所得。 重用您和其他人的艰苦工作跨工程的重用在没有一个通用结构的情况下几乎是不可能的。对象
34、符合他们现有的服务需求,不同的过程有着不同的服务需求环境,这使对象重用变得很困难。 开发一个通用结构需要预先花费许多的努力来设计。当努力不成功的时候,无论出于什么原因,有几种办法推荐使用: Dont be Afraid of Small Libraries对于代码重用,一个常见的问题就是人们不从他们做过的代码中做库。一个可重用的类可能正隐蔽在一个程序目录并且决不会有被分享的激动,因为程序员不会把类分拆出来加入库中。 这样的其中一个原因就是人们不喜欢做一个小库,对小库有一些不正确感觉。把这样的感觉克服掉吧,电脑才不关心你有多少个库呢。 如果你有一些代码可以重用,而且不能放入一个已经存在的库中,那
35、么就做一个新的库吧。如果人们真的考虑重用的话,库不会在很长的一段时间里保持那么小的。If you are afraid of having to update makefiles when libraries are recomposed or added then dont include libraries in your makefiles, include the idea of services. Base level makefiles define services that are each composed of a set of libraries. Higher leve
36、l makefiles specify the services they want. When the libraries for a service change only the lower level makefiles will have to change. 在库被重新组合或增加后,如果你害怕要升级makefiles,那么,就不要将库和服务方法放入你的makefiles中。基层的makefiles定义一系列被组合的库中的服务,而高层的makefiles详细指出他们想要的服务。当库为了服务而改变时,只须低层的makefiles改变就可以了。Keep a Repository 大多公司
37、对他们所拥有的代码无计可施。大多程序员依旧不能传达他们已经作完的,或询问当前存在的。解决办法是使存储代码可行。在理想世界中,程序员能浏览任何一个页面、路由器或检索一系列打包的库,拿走他们所需要的。如果你能建立一个程序员自动保存地系统,很棒。如果你有一个可探查可重用的库,更棒.另一种方法是在代码源中自动产生数据仓库。这样做需要通过使用普通类、方法、库和能够备份人性化页面和仓库实体的子系统标头。评价注释 注释应该是讲述一个故事试想你在评论一个描述这个系统的论述。除非,你的注释是被自动选中的并且形成了人性化界面。类注释是故事的一部分,方法特征是其中的一部分,方法理由是故事一部分,并且,方法的完成也是
38、故事的一部分。所有的这些应该整合在一起,并使他人知道,在那段时间你具体在做什么,为什么这样做?文档决定文档决定应当注释,在你选择的每一个点上都放置一个注释描述,使你能知道到这个点的作用,考古学家将发现这些最有用的信息。使用标头说明利用类似ccdoc的文档抽取系统。在这一文档的其他部分描述的是怎么利用ccdoc记录一个类和方法。这些标头说明可以以这样的一个方式来提取并分析和加以组织,它们不像一般的标头一样是无用的。因此花时间去填上他吧。 注释布局 工程的每部分都有特定的注释布局。 Make Gotchas Explicit 明确变量注释在正常的控制流或其他可能在存取中被损坏的代码中改变。内含的关
39、键词总是用来指出问题或潜在的问题。考虑到机器将根据关键词来查找你得注释。提炼出他们,并且生成人们需要的具有特殊效果的报告Gotcha Keywords :TODO: topic意思是这里有一些东西需要做,不要忘记 :BUG: bugid topic意思是有个知道的错误,说明它并给出一个错误标识 :KLUDGE:你有一些事情很难说清楚或解释清楚你是如何做的,如果你有时间下一次应当改正 :TRICKY:告诉程序员下列代码非常灵敏不要随意改变它 :WARNING:应当小心处理的东西 :PHARSER:有时你需要解决常见问题,记录它,最后在完成它 :ATTRIBUTE: value在注释中内含属性的通
40、式,你可以设置你自己的属性,它们可以被引用Gotcha Formatting 注释的第一个符号应当是gotcha关键词 注释由多行组成,但是第一行应当是自我说明,就是摘要 作者的名字和标志日期应当是注释的一部分,这些信息是源程序的一部分,但是它应当提供相当的通过额外的途径可以找得到方法,Often gotchas stick around longer than they should.应当允许程序员在文档最后放入日期,将个人信息加入文档,让我们知道谁在问例如/ :TODO: tmh : possible performance problem / We should really use a
41、 hash table here but for now well / use a linear search. / :KLUDGE: tmh : possible unsafe type cast / We need a cast here to recover the derived type. It should / probably use a virtual method or template.See Also 在Interface and Implementation Documentation 找到应如何放置文档的更多细节.接口和执行文档文档有两个主要的读者: 类用户 类执行者经过预算,我们从源代码中直接摘选各类型文档。类用户类的用户需要能够在结构正确的时候从标头文件得到此类的接口信息。当填写类的标头注释块时,只包括程序员需要的信息。不必钻研运算法则的执行细节,除非类用户需要。考虑标头文件的注释类执行者 类执行者需要关于类是怎样执行的信息。注释类型在执行类的源文件中。不要担心接口问题,源文件中的标头注释块以包括算法问题和其他设计目的。在方法的执行的注释块中应能解释更多内容。目录文档所有的目录下都需要具有README文档,其中包括: 该目录的功能及其包含内容
限制150内