PHPAppl Coding Standard
Last Modified: 2001-10-12
The PHPAppl Coding Standard is with permission
based on PHP Coding Standard from Todd Hoff and Fredrik Kristiansen
Contents
Names
Make Names Fit
Names are the heart of programming. In the past people
believed knowing someone's true name gave them magical power over that person.
If you can think up the true name for something, you give yourself and the
people coming after power over the code. Don't laugh!
A name is the result of a long deep thought process about the ecology it
lives in. Only a programmer who understands the system as a whole can create a
name that "fits" with the system. If the name is appropriate everything fits
together naturally, relationships are clear, meaning is derivable, and reasoning
from common human expectations works as expected.
If you find all your names could be Thing and DoIt then you should probably
revisit your design.
Class Names
- Name the class after what it is. If you can't think of what it is that is
a clue you have not thought through the design well enough.
- Compound names of over three words are a clue your design may be confusing
various entities in your system. Revisit your design. Try a CRC card session
to see if your objects have more responsibilities than they should.
- Avoid the temptation of bringing the name of the class a class derives
from into the derived class's name. A class should stand on its own. It
doesn't matter what it derives from.
- Suffixes are sometimes helpful. For example, if your system uses agents
then naming something DownloadAgent conveys real information.
Method and Function Names
- Usually every method and function performs an action, so the name should
make clear what it does: CheckForErrors() instead of ErrorCheck(),
DumpDataToFile() instead of DataFile(). This will also make functions and data
objects more distinguishable.
- Suffixes are sometimes useful:
- Max - to mean the maximum value something can have.
- Cnt - the current count of a running count variable.
- Key - key value.
For example: RetryMax to mean the maximum number of retries, RetryCnt to
mean the current retry count.
- Prefixes are sometimes useful:
- Is - to ask a question about something. Whenever someone sees
Is they will know it's a question.
- Get - get a value.
- Set - set a value.
For example: IsHitRetryLimit.
No All Upper Case Abbreviations
- When confronted with a situation where you could use an all upper case
abbreviation instead use an initial upper case letter followed by all lower
case letters. No matter what.
Do use: GetHtmlStatistic.
Do not use:
GetHTMLStatistic.
Justification
Example
class FluidOz // NOT FluidOZ
class GetHtmlStatistic // NOT GetHTMLStatistic
Class Names
- Use upper case letters as word separators, lower case for the rest of a
word
- First character in a name is upper case
- No underbars ('_')
Justification
- Of all the different naming strategies many people found this one the best
compromise.
Example
class NameOneTwo
class Name
Class Library Names
- Now that name spaces are becoming more widely implemented, name spaces
should be used to prevent class name conflicts among libraries from different
vendors and groups.
- When not using name spaces, it's common to prevent class name clashes by
prefixing class names with a unique string. Two characters is sufficient, but
a longer length is fine.
Example
John Johnson's complete data structure library could use
JJ as a prefix, so classes would be: class JjLinkList
{
}
Method Names
- Use the same rule as for class names.
Justification
- Of all the different naming strategies many people found this one the best
compromise.
Example
class NameOneTwo
{
function DoIt() {};
function HandleError() {};
}
Class Attribute Names
- Class member attribute names should be prepended with the character 'm'.
- After the 'm' use the same rules as for class names.
- 'm' always precedes other name modifiers like 'r' for reference.
Justification
- Prepending 'm' prevents any conflict with method names. Often your methods
and attribute names will be similar, especially for accessors.
Example
class NameOneTwo
{
function VarAbc() {};
function ErrorNumber() {};
var $mVarAbc;
var $mErrorNumber;
var $mrName;
}
Method Argument Names
- The first character should be lower case.
- All word beginnings after the first letter should be upper case as with
class names.
Justification
- You can always tell which variables are passed in variables.
Example
class NameOneTwo
{
function StartYourEngines(&$someEngine, &$anotherEngine) {
$this->mSomeEngine = $someEngine;
$this->mAnotherEngine = $anotherEngine;
}
var $mSomeEngine;
var $mAnotherEngine;
}
Variable Names
- use all lower case letters
- use '_' as the word separator.
Justification
- With this approach the scope of the variable is clear in the code.
- Now all variables look different and are identifiable in the code.
Example
function HandleError($errorNumber)
{
$error = new OsError;
$time_of_error = $error->GetTimeOfError();
$error_processor = $error->GetErrorProcessor();
}
Reference Variables and Functions Returning References
- References should be prepended with 'r'.
Justification
- The difference between variable types is clarified.
- It establishes the difference between a method returning a modifiable
object and the same method name returning a non-modifiable object.
Example
class Test
{
var $mrStatus;
function DoSomething(&$rStatus) {};
function &rStatus() {};
}
Global Variables
- Global variables should be prepended with a 'g'.
Justification
- It's important to know the scope of a variable.
Example
global $gLog;
global &$grLog;
Define Names / Global Constants
- Global constants should be all caps with '_' separators.
Justification
It's tradition for global constants to named this way.
You must be careful to not conflict with other predefined globals.
Example
define("A_GLOBAL_CONSTANT", "Hello world!");
Static Variables
- Static variables may be prepended with 's'.
Justification
- It's important to know the scope of a variable.
Example
function test()
{
static $msStatus = 0;
}
Function Names
- For PHP functions use the C GNU convention of all lower case letters with
'_' as the word delimiter.
Justification
- It makes functions very different from any class related names.
Example
function some_bloody_function()
{
}
Error Return Check Policy
- Check every system call for an error return, unless you know you wish to
ignore errors.
- Include the system error text for every system error message.
Braces {} Policy
Justification
Indentation/Tabs/Space Policy
- Indent using 4 spaces for each level.
- Do not use tabs, use spaces. Most editors can substitute spaces for tabs.
- Indent as much as needed, but no more. There are no arbitrary rules as to
the maximum indenting level. If the indenting level is more than 4 or 5 levels
you may think about factoring out code.
Justification
- When people using different tab settings the code is impossible to read or
print, which is why spaces are preferable to tabs.
- As much as people would like to limit the maximum indentation levels it
never seems to work in general. We'll trust that programmers will choose
wisely how deep to nest code.
Example
function func() {
if (something bad) {
if (another thing bad) {
while (more input) {
}
}
}
}
Parens () with Key Words and Functions Policy
- Do not put parens next to keywords. Put a space between.
- Do put parens next to function names.
- Do not use parens in return statements when it's not necessary.
Justification
- Keywords are not functions. By putting parens next to keywords keywords
and function names are made to look alike.
Example
if (condition) {
}
while (condition) {
}
strcmp($s, $s1);
return 1;
Do Not do Real Work in Object Constructors
Do not do any real work in
an object's constructor. Inside a constructor initialize variables only and/or
do only actions that can't fail.
Create an Open() method for an object which completes construction. Open()
should be called after object instantiation.
Justification
- Constructors can't return an error.
Example
class Device {
function Device() { /* initialize and other stuff */ }
function Open() { return FAIL; }
};
$dev = new Device;
if (FAIL == $dev->Open()) exit(1);
Make Functions Reentrant
Functions should not keep static variables
that prevent a function from being reentrant.
If Then Else Formatting
Layout
It's up to the programmer. Different bracing styles will yield
slightly different looks. One common approach is: if (condition) { // Comment
} else if (condition) { // Comment
} else { // Comment
}
If you have else if statements then it is usually a good idea to
always have an else block for finding unhandled cases. Maybe put a log message
in the else even if there is no corrective action taken.
Condition Format
Always put the constant on the left hand side of an
equality/inequality comparison. For example:
if ( 6 == $errorNum ) ...
One reason is that if you leave out one of the = signs, the pharser will find
the error for you. A second reason is that it puts the value you are looking for
right up front where you can find it instead of buried at the end of your
expression. It takes a little time to get used to this format, but then it
really gets useful.
switch Formatting
- Falling through a case statement into the next case statement shall be
permitted as long as a comment is included.
- The default case should always be present and trigger an error if
it should not be reached, yet is reached.
- If you need to create variables put all the code in a block.
Example
switch (...) {
case 1:
...
// FALL THROUGH
case 2: {
$v = get_week_number();
...
}
break;
default:
}
Use of continue,break and ?:
Continue and Break
Continue and break are really disguised gotos so
they are covered here.
Continue and break like goto should be used sparingly as they are magic in
code. With a simple spell the reader is beamed to god knows where for some
usually undocumented reason.
The two main problems with continue are:
- It may bypass the test condition
- It may bypass the increment/decrement expression
Consider the following example where both problems occur:
while (TRUE) {
...
// A lot of code
...
if (/* some condition */) {
continue;
}
...
// A lot of code
...
if ( $i++ > STOP_VALUE) break;
}
Note: "A lot of code" is necessary in order that the problem cannot be
caught easily by the programmer.
From the above example, a further rule may be given: Mixing continue with
break in the same loop is a sure way to disaster.
?:
The trouble is people usually try and stuff too much code in between
the ? and :. Here are a couple of clarity rules to follow:
- Put the condition in parens so as to set it off from other code
- If possible, the actions for the test should be simple functions.
- Put the action for the then and else statement on a separate line unless
it can be clearly put on one line.
Example
(condition) ? funct1() : func2();
or
(condition)
? long statement
: another long statement;
Alignment of Declaration Blocks
- Block of declarations should be aligned.
Justification
- Clarity.
- Similarly blocks of initialization of variables should be tabulated.
- The ‘&’ token should be adjacent to the type, not the name.
Example
var $mDate
var& $mrDate
var& $mrName
var $mName
$mDate = 0;
$mrDate = NULL;
$mrName = 0;
$mName = NULL;
One Statement Per Line
There should be only one statement per line
unless the statements are very closely related.
Short Methods
- Methods should limit themselves to a single page of code.
Justification
- The idea is that the each method represents a technique for achieving a
single objective.
- Most arguments of inefficiency turn out to be false in the long run.
- True function calls are slower than not, but there needs to a thought out
decision (see premature optimization).
Document Null Statements
Always document a null body for a for or while
statement so that it is clear that the null body is intentional and not missing
code.
while ($dest++ = $src++)
; // VOID
Do Not Default If Test to Non-Zero
Do not default the test for
non-zero, i.e.
if (FAIL != f())
is better than
if (f())
even though FAIL may have the value 0 which PHP considers to be
false. An explicit test will help you out later when somebody decides that a
failure return should be -1 instead of 0. Explicit comparison should be used
even if the comparison value will never change; e.g., if (!($bufsize %
strlen($str))) should be written instead as if (0 == ($bufsize %
strlen($str))) to reflect the numeric (not boolean) nature of the test. A
frequent trouble spot is using strcmp to test for string equality, where the
result should never ever be defaulted.
The non-zero test is often defaulted for predicates and other functions or
expressions which meet the following restrictions:
- Returns 0 for false, nothing else.
- Is named so that the meaning of (say) a true return is absolutely
obvious. Call a predicate IsValid(), not CheckValid().
The Bull of Boolean Types
Do not check a boolean value for equality with 1 (TRUE, YES, etc.); instead
test for inequality with 0 (FALSE, NO, etc.). Most functions are guaranteed to
return 0 if false, but only non-zero if true. Thus,
if (TRUE == func()) { ...
must be written
if (FALSE != func()) { ...
Usually Avoid Embedded Assignments
There is a time and a place for
embedded assignment statements. In some constructs there is no better way to
accomplish the results without making the code bulkier and less readable.
while ($a != ($c = getchar())) {
process the character
}
The ++ and -- operators count as assignment statements. So, for many
purposes, do functions with side effects. Using embedded assignment statements
to improve run-time performance is also possible. However, one should consider
the tradeoff between increased speed and decreased maintainability that results
when embedded assignments are used in artificial places. For example,
$a = $b + $c;
$d = $a + $r;
should not be replaced by
$d = ($a = $b + $c) + $r;
even though the latter may save one cycle. In the long run the time
difference between the two will decrease as the optimizer gains maturity, while
the difference in ease of maintenance will increase as the human memory of
what's going on in the latter piece of code begins to fade.
HTTP_*_VARS
HTTP_*_VARS are either enabled or disabled. When enabled
all variables must be accessed through $HTTP_*_VARS[key]. When disabled all
variables can be accessed by the key name.
- use HTTP_*_VARS when accessing variables.
- use enabled HTTP_*_VARS in PHP configuration.
Justification
- HTTP_*_VARS is available in any configuration.
- HTTP_*_VARS will not conflict with exsisting variables.
- Users can't change variables by passing values.
PHP File Extensions
There is lots of different extension variants on
PHP files (.html, .php, .php3, .php4, .phtml, .inc, .class...).
- Use extension .html or .php.
- Always use the extension .php for your class and function libraries.
- Enable .html and .php files to be pharsed by PHP in your webserver
configuration.
When you choose to use the .html extension on all your
web documents, you should put all your libraries in files with the extension
.php. When compared with the c language, the .c becomes .html and .h becomes
.php.
Justification
- The extension describes what data the user will recive. Pharsed PHP
becomes HTML. (Example: If you made a software who encoded mp3 files, you
wouldn't use the extension .mysoft for the encoded files)
- The use of .inc or .class can be a security problem. On most servers these
extensions aren't set to be run by a pharser. If these are accessed they will
be displayed in clear text.
Use if (0) to Comment Out Code Blocks
Sometimes large blocks of code
need to be commented out for testing. The easiest way to do this is with an if
(0) block: function example() {
great looking code
if (0) {
lots of code
}
more code
}
You can't use /**/ style comments because comments can't contain
comments and surely a large block of your code will contain a comment, won't it?
Different Accessor Styles
Implementing Accessors
There are two major idioms for creating
accessors.
Get/Set
class X {
function GetAge() { return $this->mAge; }
function SetAge($age) { $this->mAge = $age; }
var $mAge;
}
Get/Set is ugly. Get and Set are strewn throughout the code cluttering it
up.
But one benefit is when used with messages the set method can transparently
transform from native machine representations to network byte order.
Attributes as Objects
class X {
function Age() { return $this->mAge; }
function Name() { return $this->mName; }
var $mAge;
var $mName;
}
$x = new X;
// Example 1
$age = $x->Age();
$r_age = &$x->Age(); // Reference
// Example 2
$name = $x->Name();
$r_name = &$x->Name(); // Reference
Attributes as Objects is clean from a name perspective. When possible use
this approach to attribute access.
PHP Code Tags
PHP Tags are used for delimit PHP from html in a file.
There are serval ways to do this. <?php ?>, <? ?>, <script
language="php"> </script>, <% %>, and <?=$name?>. Some of
these may be turned off in your PHP settings.
Justification
- <?php ?> is always avaliable in any system and setup.
Example
<?php print "Hello world"; ?> // Will print "Hello world"
<? print "Hello world"; ?> // Will print "Hello world"
<script language="php"> print "Hello world"; </script> // Will print "Hello world"
<% print "Hello world"; %> // Will print "Hello world"
<?=$street?> // Will print the value of the variable $street
No Magic Numbers
A magic number is a bare-naked number used in source
code. It's magic because no-one has a clue what it means including the author
inside 3 months. For example:
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(); }
In the above example what do 22 and 19 mean? If there was a number change
or the numbers were just plain wrong how would you know?
Heavy use of magic numbers marks a programmer as an amateur more than
anything else. Such a programmer has never worked in a team environment or has
had to maintain code or they would never do such a thing.
Instead of magic numbers use a real name that means something. You should use
define(). For example:
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(); }
Now isn't that better?