PHP 编程标准(English version)
目录
介绍标准化的重要性标准化问题在某些方面上让每个人头痛,让人人都觉得大家处于同样的境地。这有助于让这些建议在许多的项目中不断演进,许多公司花费了许多星期逐子字逐句的进行争论。标准化不是特殊 的个人风格,它对本地改良是完全开放的。 优点当一个项目尝试着遵守公用的标准时,会有以下好处:
缺点现在轮到坏处了:
讨论许多项目的经验能得出这样的结论:采用编程标准可以使项目更加顺利地完成。标准是成功的关 解释惯例在本文档中使用“要”字所指的是使用本规范的所有项目需要遵守规定的标准。使用“应该”一词的作用是指导项目定制项目细节规范。因为项目必须适当的包括 (include), 使用“可以”一词的作用与“应该”类似,因为它指明了可选的需求。
标准实施首先应该在开发小组的内部找出所有的最重要的元素,也许标准对你的状况还不够恰当。它可能已经概括了 重要的问题,也可能还有人对其中的某些问题表示强烈的反对。 无论在什么情况下,只要最后顺利的话,人们将成熟的明白到这个标准是合理的,然后其他的程序员们 也会发现它的合理性,并觉得带着一些保留去遵循这一标准是值得的。 如果没有自愿的合作,可以制定需求:标准一定要经过代码的检验。 如果没有检验的话,这个解决方案仅仅是一个建立在不精确的基础上的一大群可笑的人。 认同观点
出结论的方法就是你必须要能够接受不同的思想。请您给自己一点时间去做到它。 项目的四个阶段命名规则合适的命名命名是程序规划的核心。古人相信只要知道一个人真正的名字就会获得凌驾于那个人之上的不可思议的力
类命名
方法和函数命名
缩写词不要全部使用大写字母
理由
例如class FluidOz // 不要写成 FluidOZ class GetHtmlStatistic // 不要写成 GetHTMLStatistic
类命名
理由
例如class NameOneTwo class Name
类库命名
例如John Johnson的数据结构类库可以用Jj做为前缀,如下:class JjLinkList { }
方法命名
理由
例如class NameOneTwo { function DoIt() {}; function HandleError() {}; }
类属性命名
理由
例如class NameOneTwo { function VarAbc() {}; function ErrorNumber() {};
方法中参数命名
理由
例如class NameOneTwo { function StartYourEngines( &$rSomeEngine, &$rAnotherEngine); }
变量命名
理由
例如function HandleError($errorNumber) { $error = OsErr(); $time_of_error = OsErr->getTimeOfError; $error_processor = OsErr->getErrorProcessor; }
引用变量和函数返回引用
理由
例如class Test { var mrStatus;
全局变量
理由
例如global $gLog; global &$grLog;
定义命名 / 全局常量
理由这是命名全局常量的传统。你要注意不要与其它的定义相冲突。例如
静态变量
理由
例如function test()
函数命名
理由
例如function some_bloody_function() { }
错误返回检测规则
大括号 {} 规则在三种主要的大括号放置规则中,有两种是可以接受的,如下的第一种是最好的:
理由
缩进/制表符/空格 规则
理由
例如function func() { if (something bad) { if (another thing bad) { while (more input) { } } } }
小括号、关键词和函数 规则
理由
例如if (condition) { } while (condition) { } strcmp($s, $s1); return 1;
RCS关键词、更改记录和历史记录规则直接使用RCS关键词的规则必须改变,其中包括使用CVS等类似的支持RCS风格关键词的源代码控制系统:
理由
别在对象架构期做实际的工作别在对象架构期做真实的工作,在架构期初始化变量和/或做任何不会有失误的事情。当完成对象架构时,为该对象建立一个Open()方法,Open()方法应该以对象实体命名。 理由
例如class Device { function Device() { /* initialize and other stuff */ } function Open() { return FAIL; } }; $dev = new Device; if (FAIL == $dev->Open()) exit(1);
If Then Else 格式布局这由程序员决定。不同的花括号样式会产生些微不同的样观。一个通用方式是: if (条件1) // 注释 { } else if (条件2) // 注释 { } else // 注释 { }如果你有用到else if 语句的话,通常最好有一个else块以用于处理未处理到的其他情况。可以的话 放一个记录信息注释在else处,即使在else没有任何的动作。
条件格式总是将恒量放在等号/不等号的左边,例如:if ( 6 == $errorNum ) ... 一个原因是假如你在等式中漏了一个等号,语法检查器会为你报错。第二个原因是你能立刻找到数值
switch 格式
例如switch (...) { case 1: ... // FALL THROUGH case 2: { $v = get_week_number(); ... } break; default: }
continue,break 和 ? 的使用:Continue 和 BreakContinue 和 break 其实是变相的隐蔽的 goto方法。Continue 和 break 像 goto 一样,它们在代码中是有魔力的,所以要节俭(尽可能少)的使用它们。 Continue有两个主要的问题:
看看下面的例子,考虑一下问题都在哪儿发生: 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 混合使用是引起灾难的正确方法。
?:麻烦在于人民往往试着在 ? 和 : 之间塞满了许多的代码。以下的是一些清晰的连接规则:
例如(condition) ? funct1() : func2(); or (condition) ? long statement : another long statement;
声明块的定位
理由Justification
例如var $mDate var& $mrDate var& $mrName var $mName $mDate = 0; $mrDate = NULL; $mrName = 0; $mName = NULL;
每行一个语句除非这些语句有很密切的联系,否则每行只写一个语句。
短方法
理由
记录所有的空语句总是记录下for或者是while的空块语句,以便清楚的知道该段代码是漏掉了,还是故意不写的。while ($dest++ = $src++) ; // VOID
不要采用缺省方法测试非零值不要采用缺省值测试非零值,也就是使用:if (FAIL != f())比下面的方法好: if (f())即使 FAIL 可以含有 0 值 ,也就是PHP认为false的表示。在某人决定用-1代替0作为失败返回值的时候, 一个显式的测试就可以帮助你了。就算是比较值不会变化也应该使用显式的比较;例如:if (!($bufsize % strlen($str))) 应该写成:if (($bufsize % strlen($str)) == 0)以表示测试的数值(不是布尔)型。一个经常出 问题的地方就是使用strcmp来测试一个字符等式,结果永远也不会等于缺省值。 非零测试采用基于缺省值的做法,那么其他函数或表达式就会受到以下的限制:
布尔逻辑类型大部分函数在FALSE的时候返回0,但是发挥非0值就代表TRUE,因而不要用1(TRUE,YES,诸如此类)等式检测一个布尔值,应该用0(FALSE,NO,诸如此类)的不等式来代替: if (TRUE == func()) { ...应该写成: if (FALSE != func()) { ...
通常避免嵌入式的赋值有时候在某些地方我们可以看到嵌入式赋值的语句,那些结构不是一个比较好的少冗余,可读性强的方法。
while ($a != ($c = getchar())) { process the character } ++和--操作符类似于赋值语句。因此,出于许多的目的,在使用函数的时候会产生副作用。使用嵌入式赋值 a = b + c; d = a + r;不要写成: d = (a = b + c) + r;虽然后者可以节省一个周期。但在长远来看,随着程序的维护费用渐渐增长,程序的编写者对代码渐渐遗忘, 就会减少在成熟期的最优化所得。
重用您和其他人的艰苦工作跨工程的重用在没有一个通用结构的情况下几乎是不可能的。对象符合他们现有的服务需求,不同的过程有着不同的服务需求环境,这使对象重用变得很困难。 开发一个通用结构需要预先花费许多的努力来设计。当努力不成功的时候,无论出于什么原因,有几种办法推
请教!给群组发Email求助这个简单的方法很少被使用。因为有些程序员们觉得如果他向其他人求助,会显得自己水平低,这多傻啊!做新的有趣的工作,不要一遍又一遍的做别人已经做过的东西。 如果你需要某些事项的源代码,如果已经有某人做过的话,就向群组发email求助。结果会很惊喜哦! 在许多大的群组中,个人往往不知道其他人在干什么。你甚至可以发现某人在找一些东西做,并且自愿为你写代 告诉!当你在做事的时候,把它告诉所有人如果你做了什么可重用的东西的话,让其他人知道。别害羞,也不要为了保护自豪感而把你的工作成果藏起来。一旦养成共享工作成果的习惯,每个人都会获得更多。 Don't be Afraid of Small Libraries对于代码重用,一个常见的问题就是人们不从他们做过的代码中做库。一个可重用的类可能正隐蔽在一个程序目录并且决不会有被分享的激动,因为程序员不会把类分拆出来加入库中。 这样的其中一个原因就是人们不喜欢做一个小库,对小库有一些不正确感觉。把这样的感觉克服掉吧,电脑才不 如果你有一些代码可以重用,而且不能放入一个已经存在的库中,那么就做一个新的库吧。如果人们真的考虑重 If you are afraid of having to update makefiles when libraries are recomposed or added then don't 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 level makefiles specify the services they want. When the libraries for a service change only the lower level makefiles will have to change.
Keep a RepositoryMost companies have no idea what code they have. And most programmers still don't communicate what they have done or ask for what currently exists. The solution is to keep a repository of what's available.In an ideal world a programmer could go to a web page, browse or search a list of packaged libraries, taking what they need. If you can set up such a system where programmers voluntarily maintain such a system, great. If you have a librarian in charge of detecting reusability, even better. Another approach is to automatically generate a repository from the source code. This is done by using common class, method, library, and subsystem headers that can double as man pages and repository entries.
评价注释注释应该是讲述一个故事Consider your comments a story describing the system. Expect your comments to be extracted by a robot and formed into a man page. Class comments are one part of the story, method signature comments are another part of the story, method arguments another part, and method implementation yet another part. All these parts should weave together and inform someone else at another point of time just exactly what you did and why.Document DecisionsComments should document decisions. At every point where you had a choice of what to do place a comment describing which choice you made and why. Archeologists will find this the most useful information.使用标头说明利用类似ccdoc的文档抽取系统。在这一文档的其他部分描述的是怎么利用ccdoc记录一个类和方法。
注释布局工程的每部分都有特定的注释布局。 Make Gotchas ExplicitExplicitly comment variables changed out of the normal control flow or other code likely to break during maintenance. Embedded keywords are used to point out issues and potential problems. Consider a robot will parse your comments looking for keywords, stripping them out, and making a report so people can make a special effort where needed.
Gotcha Keywords
Gotcha Formatting
Example// :TODO: tmh 960810: possible performance problem // We should really use a hash table here but for now we'll // use a linear search. // :KLUDGE: tmh 960810: possible unsafe type cast // We need a cast here to recover the derived type. It should // probably use a virtual method or template. See AlsoSee Interface and Implementation Documentation for more details on how documentation should be laid out.
Interface and Implementation DocumentationThere are two main audiences for documentation:
Class UsersClass users need class interface information which when structured correctly can be extracted directly from a header file. When filling out the header comment blocks for a class, only include information needed by programmers who use the class. Don't delve into algorithm implementation details unless the details are needed by a user of the class. Consider comments in a header file a man page in waiting.Class ImplementorsClass implementors require in-depth knowledge of how a class is implemented. This comment type is found in the source file(s) implementing a class. Don't worry about interface issues. Header comment blocks in a source file should cover algorithm issues and other design decisions. Comment blocks within a method's implementation should explain even more.
目录文档所有的目录下都需要具有README文档,其中包括:
工程的源代码目录树,阅读说明文件,源文件的标头说明等等做为地图,他应该有能力穿越整个工程。
Use a Design Notation and ProcessProgrammers need to have a common language for talking about coding, designs, and the software process in general. This is critical to project success.Any project brings together people of widely varying skills, knowledge, and experience. Even if everyone on a project is a genius you will still fail because people will endlessly talk past each other because there is no common language and processes binding the project together. All you'll get is massive fights, burnout, and little progress. If you send your group to training they may not come back seasoned experts but at least your group will all be on the same page; a team. There are many popular methodologies out there. The point is to do some research, pick a method, train your people on it, and use it. Take a look at the top of this page for links to various methodologies. You may find the CRC (class responsibility cards) approach to teasing out a design useful. Many others have. It is an informal approach encouraging team cooperation and focusing on objects doing things rather than objects having attributes. There's even a whole book on it: Using CRC Cards by Nancy M. Wilkinson. Using Use CasesA use case is a generic description of an entire transaction involving several objects. A use case can also describe the behaviour of a set of objects, such as an organization. A use case model thus presents a collection of use cases and is typically used to specify the behavior of a whole application system together with one or more external actors that interact with the system.An individual use case may have a name (although it is typically not a simple name). Its meaning is often written as an informal text description of the external actors and the sequences of events between objects that make up the transaction. Use cases can include other use cases as part of their behaviour. Requirements CaptureUse cases attempt to capture the requirements for a system in an understandable form. The idea is by running through a set of use case we can verify that the system is doing what it should be doing.Have as many use cases as needed to describe what a system needs to accomplish. The Process
Open/Closed PrincipleThe Open/Closed principle states a class must be open and closed where:
In practice the Open/Closed principle simply means making good use of our old friends abstraction and polymorphism. Abstraction to factor out common processes and ideas. Inheritance to create an interface that must be adhered to by derived classes.
Design by ContractThe idea of design by contract is strongly related to LSP . A contract is a formal statement of what to expect from another party. In this case the contract is between pieces of code. An object and/or method states that it does X and you are supposed to believe it. For example, when you ask an object for its volume that's what you should get. And because volume is a verifiable attribute of a thing you could run a series of checks to verify volume is correct, that is, it satisfies its contract.The contract is enforced in languages like Eiffel by pre and post condition statements that are actually part of the language. In other languages a bit of faith is needed. Design by contract when coupled with language based verification mechanisms is a very powerful idea. It makes programming more like assembling spec'd parts.
其他杂项这一部分包含着各种各样的该做的和不该做的。
使用if (0)来注释外部代码块有时需要注释大段的测试代码,最简单的方法就是使用if (0)块:function example() { great looking code if (0) { lots of code } more code } 你不能使用/**/,因为注释内部不能包含注释,而大段的程序中可以包含注释,不是么? Different Accessor StylesWhy Accessors?Access methods provide access to the physical or logical attributes of an object. We disallow direct access to attributes to break dependencies, the reason we do most things. Directly accessing an attribute exposes implementation details about the object.To see why ask yourself:
Implementing AccessorsThere are three major idioms for creating accessors.Get/Setclass X { function GetAge() { return $this->mAge; } function SetAge($age) { $mAge= $age; } var $mAge; } One Method Nameclass X { function Age() { return $mAge; } function Age($age) { $mAge= $age; } var $mAge; }Similar to Get/Set but cleaner. Use this approach when not using the Attributes as Objects approach. Attributes as Objectsclass X { function Age() { return $mAge; } function rAge() { return &$mAge; } function Name() { return mName; } function rName() { return &$mName; } var $mAge; var $mName; }The above two attribute examples shows the strength and weakness of the Attributes as Objects approach. When using rAge(), which is not a real object, the variable is set directly because rAge() returns a reference. The object can do no checking of the value or do any representation reformatting. For many simple attributes, however, these are not horrible restrictions. LayeringLayering is the primary technique for reducing complexity in a system. A system should be divided into layers. Layers should communicate between adjacent layers using well defined interfaces. When a layer uses a non-adjacent layer then a layering violation has occurred.A layering violation simply means we have dependency between layers that is not controlled by a well defined interface. When one of the layers changes code could break. We don't want code to break so we want layers to work only with other adjacent layers. Sometimes we need to jump layers for performance reasons. This is fine, but we should know we are doing it and document appropriately.
Code ReviewsIf you can make a formal code review work then my hat is off to you. Code reviews can be very useful. Unfortunately they often degrade into nit picking sessions and endless arguments about silly things. They also tend to take a lot of people's time for a questionable payback.My god he's questioning code reviews, he's not an engineer! Not really, it's the form of code reviews and how they fit into normally late chaotic projects is what is being questioned. First, code reviews are way too late to do much of anything useful. What needs reviewing are requirements and design. This is where you will get more bang for the buck. Get all relevant people in a room. Lock them in. Go over the class design and requirements until the former is good and the latter is being met. Having all the relevant people in the room makes this process a deep fruitful one as questions can be immediately answered and issues immediately explored. Usually only a couple of such meetings are necessary. If the above process is done well coding will take care of itself. If you find problems in the code review the best you can usually do is a rewrite after someone has sunk a ton of time and effort into making the code "work." You will still want to do a code review, just do it offline. Have a couple people you trust read the code in question and simply make comments to the programmer. Then the programmer and reviewers can discuss issues and work them out. Email and quick pointed discussions work well. This approach meets the goals and doesn't take the time of 6 people to do it.
Create a Source Code Control System Early and Not OftenA common build system and source code control system should be put in place as early as possible in a project's lifecycle, preferably before anyone starts coding. Source code control is the structural glue binding a project together. If programmers can't easily use each other's products then you'll never be able to make a good reproducible build and people will piss away a lot of time. It's also hell converting rogue build environments to a standard system. But it seems the right of passage for every project to build their own custom environment that never quite works right.Some issues to keep in mind:
SourcesIf you have the money many projects have found Clear Case a good system. Perfectly workable systems have been build on top of GNU make and CVS. CVS is a freeware build environment built on top of RCS. Its main difference from RCS is that is supports a shared file model to building software.
Create a Bug Tracking System Early and Not OftenThe earlier people get used to using a bug tracking system the better. If you are 3/4 through a project and then install a bug tracking system it won't be used. You need to install a bug tracking system early so people will use it.Programmers generally resist bug tracking, yet when used correctly it can really help a project:
FYI, it's not a good idea to reward people by the number of bugs they fix :-) Source code control should be linked to the bug tracking system. During the part of a project where source is frozen before a release only checkins accompanied by a valid bug ID should be accepted. And when code is changed to fix a bug the bug ID should be included in the checkin comments.
SourcesSeveral projects have found DDTS a workable system (I 've not verified this link for this PHP release, DDTS may not work for PHP). There is also a GNU bug tracking system available. Roll your own is a popular option but using an existing system seems more cost efficient.
Honor ResponsibilitiesResponsibility for software modules is scoped. Modules are either the responsibility of a particular person or are common. Honor this division of responsibility. Don't go changing things that aren't your responsibility to change. Only mistakes and hard feelings will result.Face it, if you don't own a piece of code you can't possibly be in a position to change it. There's too much context. Assumptions seemingly reasonable to you may be totally wrong. If you need a change simply ask the responsible person to change it. Or ask them if it is OK to make such-n-such a change. If they say OK then go ahead, otherwise holster your editor. Every rule has exceptions. If it's 3 in the morning and you need to make a change to make a deliverable then you have to do it. If someone is on vacation and no one has been assigned their module then you have to do it. If you make changes in other people's code try and use the same style they have adopted. Programmers need to mark with comments code that is particularly sensitive to change. If code in one area requires changes to code in an another area then say so. If changing data formats will cause conflicts with persistent stores or remote message sending then say so. If you are trying to minimize memory usage or achieve some other end then say so. Not everyone is as brilliant as you. The worst sin is to flit through the system changing bits of code to match your coding style. If someone isn't coding to the standards then ask them or ask your manager to ask them to code to the standards. Use common courtesy. Code with common responsibility should be treated with care. Resist making radical changes as the conflicts will be hard to resolve. Put comments in the file on how the file should be extended so everyone will follow the same rules. Try and use a common structure in all common files so people don't have to guess on where to find things and how to make changes. Checkin changes as soon as possible so conflicts don't build up. As an aside, module responsibilities must also be assigned for bug tracking purposes. PHP文件扩展名我见过许多种PHP文件的扩展名(.html, .php, .php3, .php4, .phtml, .inc, .class...)
理由
不要不可思议的数字一个在源代码中使用了的赤裸裸的数字是不可思议的数字,因为包括作者,在三个月内,没人它的含义。例如:
if (22 == $foo) { start_thermo_nuclear_war(); } else if (19 == $foo) { refund_lotso_money(); } else if (16 == $foo) { infinite_loop(); } else { cry_cause_im_lost(); }在上例中22和19的含义是什么呢?如果一个数字改变了,或者这些数字只是简单的错误,你会怎么想? 使用不可思议的数字是该程序员是业余运动员的重要标志,这样的程序员从来没有在团队环境中工作过, 你应该用define()来给你想表示某样东西的数值一个真正的名字,而不是采用赤裸裸的数字,例如: define("PRESIDENT_WENT_CRAZY", "22"); define("WE_GOOFED", "19"); define("THEY_DIDNT_PAY", "16"); if (PRESIDENT_WENT_CRAZY == $foo) { start_thermo_nuclear_war(); } else if (WE_GOOFED == $foo) { refund_lotso_money(); } else if (THEY_DIDNT_PAY == $foo) { infinite_loop(); } else { happy_days_i_know_why_im_here(); }现在不是变得更好了么? Promise of OOOO has been hyped to the extent you'd figure it would solve world hunger and usher in a new era of world peace. Not! OO is an approach, a philosophy, it's not a recipe which blindly followed yields quality.Robert Martin put OO in perspective:
Thin vs. Fat Class InterfacesHow many methods should an object have? The right answer of course is just the right amount, we'll call this the Goldilocks level. But what is the Goldilocks level? It doesn't exist. You need to make the right judgment for your situation, which is really what programmers are for :-)The two extremes are thin classes versus thick classes. Thin classes are minimalist classes. Thin classes have as few methods as possible. The expectation is users will derive their own class from the thin class adding any needed methods. While thin classes may seem "clean" they really aren't. You can't do much with a thin class. Its main purpose is setting up a type. Since thin classes have so little functionality many programmers in a project will create derived classes with everyone adding basically the same methods. This leads to code duplication and maintenance problems which is part of the reason we use objects in the first place. The obvious solution is to push methods up to the base class. Push enough methods up to the base class and you get thick classes. Thick classes have a lot of methods. If you can think of it a thick class will have it. Why is this a problem? It may not be. If the methods are directly related to the class then there's no real problem with the class containing them. The problem is people get lazy and start adding methods to a class that are related to the class in some willow wispy way, but would be better factored out into another class. Judgment comes into play again. Thick classes have other problems. As classes get larger they may become harder to understand. They also become harder to debug as interactions become less predictable. And when a method is changed that you don't use or care about your code will still have to be retested, and rereleased.
Recent Changes
© Copyright 1995-2000. Todd Hoff and Fredrik Kristiansen. All rights reserved. |
|