Coding Standard

Last Modified: October 4th, 2007

The PHP Coding Standard is with permission based on Todd Hoff's  C++ Coding Standard.

Rewritten for PHP by Fredrik Kristiansen / DB Medialab, Oslo 2000-2003.

I. Introduction

I.A. Standardization is Important

It helps if the standard annoys everyone in some way so everone feels they are on the same playing field. The proposal here has evolved over many projects, many companies, and literally a total of many weeks spent arguing. It is no particular person's style and is certainly open to local amendments.

Good Points

When a project tries to adhere to common standards, a few good things happen:

  • Programmers can go into any code and figure out what is going on
  • New people can get up to speed quickly
  • People new to PHP are spared the need to develop a personal style and defend it to the death
  • People new to PHP are spared making the same mistakes over and over again
  • People make fewer mistakes in consistent environments

Bad Points

Now the bad:

  • The standard is usually stupid because it was made by someone who doesn't understand PHP
  • The standard is usually stupid because it's not what I do
  • Standards reduce creativity
  • Standards are unnecessary as long as people are consistent
  • Standards enforce too much structure
  • People ignore standards anyway

Discussion

The experience of many projects leads to the conclusion that using coding standards makes the project go smoother. Are standards necessary for success? Of course not. But they help, and we need all the help we can get! Be honest, most arguments against a particular standard come from the ego. Few decisions in a reasonable standard really can be said to be technically deficient, just matters of taste. So be flexible, control the ego a bit, and remember any project is fundamentally a team effort.

I.B. Interpretation

Conventions

The use of the word "shall" in this document requires that any project using this document must comply with the stated standard.
The use of the word "should" directs projects in tailoring a project-specific standard, in that the project must include, exclude, or tailor the requirement, as appropriate.
The use of the word "may" is similar to "should", in that it designates optional requirements.

Standards Enforcement

First, any serious concerns about the standard should be brought up and worked out within the group. Maybe the standard is not quite appropriate for your situation. It may have overlooked important issues or maybe someone in power vehemently disagrees with certain issues

In any case, once finalized hopefully people will play the adult and understand that this standard is reasonable, and has been found reasonable by many other programmers, and therefore is worthy of being followed even with personal reservations.

Failing willing cooperation it can be made a requirement that this standard must be followed to pass a code inspection.

Failing that the only solution is a massive tickling party on the offending party.

I.C. Accepting an Idea

  1. It's impossible.
  2. Maybe it's possible, but it's weak and uninteresting
  3. It is true I told you so.
  4. I thought of it first.
  5. How could it be otherwise

If you come to objects with a negative preconception please keep an open mind. You may still conclude objects are bunk, but there's a road you must follow to accept something different. Allow yourself to travel it for a while.

II. Naming Convention

II.A. 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 ti 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.
    • init – init values.
    For example: isHitRetryLimit.

II.B. 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

People seem to have very different intuitions when making names containing abbreviations. It's best to settle on one strategy so the names are absolutely predictable.

Take for example NetworkABCKey. Notice how the C from ABC and K from key are confused. Some people don't mind this and others just hate it so you'll find different policies in different code so you never know what to call something.

Example

class FluidOz			//NOT FluidOZ
class GetHtmlStatistic		//NOT GetHTMLStatistic

II.C. 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 underscores ('_')

Justification

Of all the different naming strategies, many people found this one the best compromise.

Example

class NameOneTwo
class Name

    II.C.A. Interface Names


    • Interfaces use all the same standards of class, but are prefixed with "i"

    Justification

    Interfaces share a name space with classes and need a way to distinguish them.

    Example

    interface iNameOneTwo
    interface iName
    

    II.C.B. Abstact Names


    • Abstact use all the same standards of class, but are prefixed with "a"

    Justification

    Interfaces share a name space with classes and need a way to distinguish them.

    Example

    abstract aNameOneTwo
    abstract aName
    

II.D. Function Names

  • Function names use camel caps
  • No underscores ('_') in function names
  • First letter of function names must always be lowercase

Justification

It makes functions very different from any class related names, and keeps functions from being confused with php's built in functions

Example

function someBoringFunction() {
    
	//lots of boring code
	
}

II.E. Variable Names

  • Lower cause first letter, use all camel caps
  • Do not use underscores ('_')
  • Use naming convention described below for all variables

Justification

  • With this approach the scobe of the variables is clear in the code.
  • Now all variables look different and are identifieable in the code.
  • Prefixes make it easy to know each variable's data type

Example

function handleError($errorNumber) {
	$error = new OsError;
	$timeOfError = $error->getTimeOfError();
	$errorProcessor = $error->getErrorProcessor();
}

II.F. Arrays & Array Elements

Arrays are named according to the variable naming convention. Array element names follow the same naming conventions as those of variables, with the exception of not needing prefixes. Additionally:

  • Do not use '_' as the word separator
  • Do not use '-' as the word separator.

Justification

If '-' is used as a word separator, it will generate warnings used with magic quotes.

Example

$myArray['fooBar'] = 'Hello';
echo $myArray['fooBar'] . ' world';		//will output: Hello world

Single or Double Quotes

  • Access an array's elements with single quotes only.
  • Don't use quotes within magic quotes

Justification

Some PHP configurations will output warnings if arrays are used without quotes except when used within magic quotes.

Global Variables

Global variables should be declared and accessed via the superglobal array $GLOBALS

Justification

Globals will be available in all scopes throughout a script. There will be no need to do a global $something; to access a global variable within functions or methods

Example

$GLOBALS['fooBar'] = 'something interesting';
echo $GLOBALS['fooBar'];		//will output: something interesting

Defines / Constants

Defines and constants should be all capitals with '_' used to separate words. Constants and defines should be defined at the lowest possible scope of the code. For example, if you write a class that uses defined globals, declare them before you declare the class, not in the application header. If you have defined globals that functions and methods throughout the application use, define them in the application header or a config file.

Justification

It's tradition for global constants to be named this way. You must be careful not to conflict with other predefined globals. Keeping declarations at the lowest possible scope makes it easier to find the define statements after the code is written

Example

define('SITE_ROOT',   $_SERVER['DOCUMENT_ROOT'] . '/');
define('UPLOAD_PATH', SITE_ROOT . 'uploads/');

Assign values to a variables

When declaring variable into a class or in code you must use NULL instead of ''.

Also, the variables should be "cast" when variables are used.

Example

class Demo {
    private $string;
    private $valid;
    
    public function __construct() {
        $this->string = NULL;
    }
    
    public function getItem($id) {
        //[...]
        $sql = "
            select
                `product`.`name`,
                `product`.`description`,
                `product`.`price`
            from
                `product`
            where
                `product`.`id` = " . (int)$id;
        //[...]
    }
    
    public function isValid() {
        return (bool)$this->valid;
    }
}

or

function validate($string) {
    $value = NULL;
    if(is_string($string)) {
        // [...]
    }
    return $value;
}

III. Formatting / Coding Style

III.A. PHP Code Demarcation

PHP code must always be delimited by the full-form, standard PHP tags. Short tags are never allowed.

Justification

is always available in any system and setup

Example

<?php= 'Hello world'; ?>	//Will print "Hello world"

III.B. Braces {} Policy

Of the three major brace placement strategies, two are acceptable, with the one listed below as being preferable and the one we will use.

Traditional Unix policy (and Zend Studio method) of placing the initial brace on the same line as the keyword and the trailing brace inline on its own line with the keyword:

if(condition) {
    ...
}

while(condition) {
    ...
}

Justification

Since we will be using Zend Studio as our rpeferred editor, the above method is automatic.

III.C. 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 use different tab settings the code is impossible to read or print, which is why spaces are preferable to tabs.
  • Most PHP applications use 4 spaces.
  • Most editors use 4 spaces by default.
  • 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) {
                ...
            }
        }
    }
}

III.D. 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($foo, $bar);
return 1;

function someFunction() {

}

III.E. If, Then, Else Formatting

Layout

While there are many different bracing styles, the approach we will use is:

if(condition) {

}
elseif(condition) {

}
else {

}

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 aprse 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.

III.F. Switch Formatting

Control statements written with the "switch" construct must have a single space before the opening parens of the conditional statement, and also a single space after the clsoing parens.

All content within the "switch" statement must be indented four spaces. Content under each "case" statement must be indented an additional four space.

Example

switch($numPeople) {
    case 1:
        
        //break intentially omitted
        //fall-through
    case 2:
       break;
	
    default:
       break;
}

The construct default may never be omitted from a switch statement.

NOTE: It is sometimes useful to write a case statement which falls through to the next case by not including a break or return in that case. To distinguish these cases from bugs, any case statement where break or return are omitted must contain the comment "//break intentionally omitted".

III.G. Use of continue, break, and ?:

Continue and Break

Continue and break are relly 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 easily caught 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)
    ? long statement
    : another long statement;

III.H. Alignment of Declaration Blocks

Blocks of declarations should be aligned

Justification

  • Clarity.
  • Similar blocks of initialization variables should be tabulated.

Example

public  $date;
public  $name;

$date   = 0;
$rDate  = NULL;
$name   = 0;

III.I. Strings

String Literals

When a string is literal (contains no variable substitutions), the apostrophe or "single quote" must always be used to demarcate the string:

$a = 'Example String';

String Literals Containting Apostrophes

When a literal itself contains apostraphes, it is permitted to demarcate the string with quotation marks, or "double quotes". This especially encouraged for SQL statements:

$queryString = "
    SELECT
        `id`,
        `name`
    FROM
        `people`
    WHERE
        `name`='Fred'
    OR
        `name`='Susan'
";

Variable Substitution and Concatenation

Variable substitution is not permitted, and concatenation should always be used instead. Strings must be concatenated using the "." operator. A space must always be added before and after the "." to improve readablity:

$company = 'Live' . 'Office';

When concatenating strings with the "." operator, it is permitted to break statements into multiple lines to improve readablity. In these cases, each successive line should be paddid with whitespace such that the "." operator is aligned under the "=" operator:

$concatString =  "The dog ran "
                    . "through the "
                    . "woods very fast";

III.J. Classes

Class Declaration

Classes must be named by the following naming conventions:

  • The brace is always written on the same line as the class declaration.
  • Every class must have docmentation block that conforms to the PHPDocumentor standard.
  • Any code with in a class must be indented four spaces.
  • Only one class is permitted per PHP file.
  • Placing additional code in a class file is permitted but discouraged. In these files, two blank lines must separate the class any additional PHP code in the file.
/**
 * Documentation Block Here
 */
class SampleClass {
    //entire content of class
    //must be indented four spaces
}

Class member functions

Call class must be use by the following naming conventions:

  • Use parent:: when calling a parent function
  • Use self:: when calling a function in the current class
/**
 * Documentation Block Here
 */
class SampleClass extends MainClass{
    [...]
    
    public function __construct() {
        [...]
        self::validate();
    }
    
    public function validate() {
        return parent::validate();
    }
}

Class Member Variables

Member variables must be named by the following conventions:

  • Any variables declared in a class must be listed at the top of the class, prior to declaring any functions
  • You may not initialize any variables at declaration, initialization is to be done in the constructor
  • Class variables follow the same naming convention as all variables
  • The var construct is not permitted. Member variables always declare their visibility by usining one of the private,protected, or public constructs.

Example

/**
 * nameonetwo.class.php
 *
 * NameOneTwo class file.
 * @author [Author Name]
 * @version 1.0
 * @copyright Copyright 2010, MyName
 * @package VCC5
 */
class NameOneTwo {
    public $var1;        //comment your declarations
    public $var2;        //comment your declarations
    public $var3;        //comment your declarations
    
    /**
    * class constructor
    */
    function __construct() {
        $this->var1 = NULL;
        $this->var2 = NULL;
        $this->var3 = NULL;
    }
    
    //other functions and code
}

III.K. SQL Queries

The following formatting convention should be used for all SQL statements to allow for readability and easy copying and pasting into utilities such as phpMyAdmin or MySQL-Front for error-checking. Query should be write with double-quote and always use the tablename before selecting a field or using in the where clause.

SELECT Without LEFT JOIN

$queryString = "
    SELECT
        `table`.`field1`
    FROM
        `table`
    WHERE
        `table`.`field2` = ''
";

SELECT With LEFT JOIN

$queryString = "
    SELECT
        `table1`.`name`,
        `table2`.`name`
    FROM
        `table1`
    LEFT JOIN
            `table2`
        ON
            `table1`.`PK_ID` = `table2`.`FK_ID`
    WHERE
        `table1`.`key` = " . (int)$value;

INSERT

$queryString = "
    INSERT INTO
        `table`
    SET
        `field` = '" . (string)$value . "',
        `field2` = '" . (string)$anotherValue . "'
";

UPDATE

$queryString = "
    UPDATE
        `table`
    SET
        `field` = '" . (string)$value . "',
        `field2` = '" . (string)$anotherValue . "'
    WHERE
        `key` = " . (int)$keyValue;

III.L. SQL Stored Procedures & Views

All stored procedure names must begin with "proc_" and all views start with "view_".

Stored procedures call will use the following convention. After the "proc_" prefix, the procedure will be named [action][table name]. The procedure name uses the same conventions as function names (camel caps). The following are acceptable actions:

  • select — ** We don't use procedure for SELECT
  • insert — insert procedures. These include validations to check if primary key exists
  • update — update procudures. These include validations to check if primary key exists
  • delete — delete procedures. These include validations to check if primary key exists

Example:

        proc_insertClient(100);
        
If a procedure performs multiple functions ie: 1 or more insert, select, and update or delete. A generic name that describes the procedure should be used.

Example:

        proc_authentication();
        
In the case of procedures that are used by modules they should be prefixed with "module" after "proc_"

Example:

        proc_moduleInstallClient();
        

Stored procedures will use the following convention.

For main procedure we will use the SQLEXCEPTION HANDLER to Rollback the transaction.

All type for parameters or declared variable can be the MySQL type. The parameters can be optional.

        DELIMITER $$;
        
        DROP PROCEDURE IF EXISTS `virtual_call_center`.`proc_name`$$
        
        CREATE PROCEDURE `virtual_call_center`.`proc_name`(
            IN `IN_varName` VARCHAR(50)
        )
        BEGIN
            DECLARE `v_variableOne` INT(11);
            
            DECLARE EXIT handler FOR SQLEXCEPTION BEGIN SELECT 0 as result; ROLLBACK; END;
            
            #SQL STATEMENT
            #Procedure condition
            
            SELECT 1 as result;
            COMMIT;
        END$$
        
        DELIMITER ;$$     
        

III.M. Print Margin Column

Never write text after the Print Margin Column (Column 80).

When programming, you don't want to scroll horizontal to see what is your code and get lost.

Example:

        if(isset($id) && $id == 0 && is_array($userTable) && in_array($id, $userTable) && $_SESSION['userId'] !== NULL) { 
            [...]
        }
        
Make your line easy to read.
        if(isset($id) 
            && $id === 0 
            && is_array($userTable) 
            && in_array($id, $userTable) 
            && $_SESSION['userId'] !== NULL) { 
            
            //[...]
        }    
        

III.N. IF/ELSE block in function.

For several validation in a function, make sure that all those validation are seperate.

With that, that make the code easy to read.

Example:

        if(isset($id)) {
            if($id === 0) {
                if(is_array($userTable)) {
                    if(in_array($id, $userTable)) {
                        if($_SESSION['userId'] !== NULL) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
        
Those condition could be hard to read if you have many overlapping if.
        if(!isset($id)) {
            //[...]
            return false;
        }

        if($id !== 0) {
            //[...]
            return false;
        }

        if(!is_array($userTable)) {
            //[...]
            return false;
        }

        if(!in_array($id, $userTable)) {
            //[...]
            return false;
        }

        if($_SESSION['userId'] === NULL) {
            //[...]
            return false;
        }

        //[...]
        return false;
        

III.O. Log File.

Standard for log file is:
[date] [user ip session_code] [action] message. [LF]

date: Date formated D M d H:i:s Y
user: Username
ip: IP
session: Session id
action: Action
message: Message related

Example:

[Fri Oct 06 09:01:00 2007] [user 127.0.0.1 1234567890] [action] This is the action.
        

III.P. Patch File.

Standard for patch file is:
# Date in format :D M d H:i:s Y
# Owner Name
# Delete from the table all the rows and optimize this table.
--- Start Statement ---
      SQL Statement
--- End Statement ---
LF

Example:

# Fri Oct 06 09:00:00 2007
# John Doe
# Delete from the table all the rows and optimize this table.
--- Start Statement ---
    DELETE from `table`;
    OPTIMIZE TABLE `table`;
--- End Statement ---
        

IV. Documentation

IV.A. Comments Should Tell a Story

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.

IV.B. Document Decisions

Comments 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.

IV.C. Use Headers

Use a document extraction system like ccdoc . Other sections in this document describe how to use ccdoc to document a class and method.

These headers are structured in such a way as they can be parsed and extracted. They are not useless like normal headers. So take time to fill them out. If you do it right once no more documentation may be necessary.

IV.D. Make Gotchas Explicit

Explicitly 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

  • TODO topic
    Means there's more to do here, don't forget.
  • BUG [bugid] topic
    means there's a Known bug here, explain it and optionally give a bug ID.
  • UGLY
    When you've done something ugly say so and explain how you would do it differently next time if you had more time.
  • TRICKY
    Tells somebody that the following code is very tricky so don't go changing it without thinking.
  • WARNING
    Beware of something.
  • THINK
    Think about something to provide the best solution possible.

Gotcha Formatting

  • Make the gotcha keyword the first symbol in the comment.
  • Comments may consist of multiple lines, but the first line should be a self-containing, meaningful summary.
  • The writer's name and the date of the remark should be part of the comment. This information is in the source repository, but it can take a quite a while to find out when and by whom it was added. Often gotchas stick around longer than they should. Embedding date information allows other programmer to make this decision. Embedding who information lets us know who to ask.

Example

// TODO
// Add a new functionality here

IV.E. Interface and Implementation Documentation

There are two main audiences for documentation:

  • Class Users
  • Class Implementors

With a little forethought we can extract both types of documentation directly from source code.

Class Users

Class 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 Implementors

Class 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.

IV.F. Directory Documentation

Every directory should have a README file that covers:

  • The purpose of the directory and what it contains
  • A one line comment on each file. A comment can usually be extracted from the NAME attrubute of the file header.
  • Cover build and install directions.
  • Direct people to related resources:
    • Directories of source
    • Online Documentation
    • Paper Documentation
    • Design Documentation
  • Anything that might help someone

Consider a new person coming in 6 months after every original person on a project has gone. That lone scared explorer should be able to piece together a picture of the whole project by traversing a source directory tree and reading README files, Makefiles, and source file headers.

VI. Complexity Management

VI.A. Layering

Layering 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.

VI.B. Open/Closed Priciple

The Open/Closed principle states a class must be open and closed where:

  • open means a class has the ability to be extended.
  • closed means a class is closed for modifications other than extension. The idea is once a class has been approved for use having gone through code reviews, unit tests, and other qualifying procedures, you don't want to change the class very much, just extend it.

The Open/Closed principle is a pitch for stability. A system is extended by adding new code not by changing already working code. Programmers often don't feel comfortable changing old code because it works! This principle just gives you an academic sounding justification for your fears.

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.

VII. Server Configuration

VII.A. $HTTP_*_VARS / $_*

HTTP_*_VARS are either enabled or disabled, and will stay disabled on our servers. When enabled all variables must be accessed through $HTTP_*_VARS[key]. When disabled all variables can be accessed by the key name.

  • use $_* when accessing variables. (ex. - $_GET['var'], $_POST['var'], $_SERVER['var'])
  • use disabled HTTP_*_VARS in PHP configuration.

Justification

  • HTTP_*_VARS is not available in any configuration.
  • Register globals should be also disabled and stay disabled, and will be going away in future versions of PHP.
  • Users can't change variables by passing values.

VII.B. PHP File Extensions

There is lots of different extension variants on PHP files (.html, .php, .php3, .php4, .phtml, .inc, .class...).

  • Always use the extension .php.
  • Always use the extension .php for your class and function libraries.

Justification

  • The use of .php makes it possible to enable caching on other files than .php.
  • The use of .inc or .class can be a security problem. On most servers these extensions aren't set to be run by a parser. If these are accessed they will be displayed in clear text.

VIII. Miscellaneous

This section contains some miscellaneous do's and don'ts.

  • Don't use floating-point variables where discrete values are needed. Using a float for a loop counter is a great way to shoot yourself in the foot. Always test floating-point numbers as <= or >=, never use an exact comparison (== or !=).
  • Do not rely on automatic beautifiers. The main person who benefits from good program style is the programmer him/herself, and especially in the early design of handwritten algorithms or pseudo-code. Automatic beautifiers can only be applied to complete, syntactically correct programs and hence are not available when the need for attention to white space and indentation is greatest. Programmers can do a better job of making clear the complete visual layout of a function or file, with the normal attention to detail of a careful programmer (in other words, some of the visual layout is dictated by intent rather than syntax and beautifiers cannot read minds). Sloppy programmers should learn to be careful programmers instead of relying on a beautifier to make their code readable. Finally, since beautifiers are non-trivial programs that must parse the source, a sophisticated beautifier is not worth the benefits gained by such a program. Beautifiers are best for gross formatting of machine-generated code.
  • Accidental omission of the second ``='' of the logical compare is a problem. The following is confusing and prone to error.
if($a = $b) { ... }

Does the programmer really mean assignment here? Often yes, but usually no. The solution is to just not do it, an inverse Nike philosophy. Instead use explicit tests and avoid assignment with an implicit test. The recommended form is to do the assignment before doing the test:

$a = $b;
if($a) { ... }

VIII.A. 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) {
    startThermoNuclearWar(); 
}
else if(19 == $foo) {
    refundLotsoMoney(); 
}
else if(16 == $foo) {
    infiniteLoop();
}
else {
    cryCauseImLost(); 
}

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) {
    startThermoNuclearWar();
}
else if(WE_GOOFED == $foo) {
    refundLotsoMoney();
}
else if(THEY_DIDNT_PAY == $foo) {
    infiniteLoop();
}
else {
    happyDaysIKnowWhyImHere();
}

Now isn't that better?

VIII.B. 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.

VIII.C. 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().

VIII.D. 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()) { ...

VIII.E. 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.

VIII.F. Reusing Your Hard Work and the Hard Work of Others

Reuse across projects is almost impossible without a common framework in place. Objects conform to the services available to them. Different projects have different service environments making object reuse difficult.

Developing a common framework takes a lot of up front design effort. When this effort is not made, for whatever reasons, there are several techniques one can use to encourage reuse:

Don't be Afraid of Small Libraries

One common enemy of reuse is people not making libraries out of their code. A reusable class may be hiding in a program directory and will never have the thrill of being shared because the programmer won't factor the class or classes into a library.

One reason for this is because people don't like making small libraries. There's something about small libraries that doesn't feel right. Get over it. The computer doesn't care how many libraries you have.

If you have code that can be reused and can't be placed in an existing library then make a new library. Libraries don't stay small for long if people are really thinking about reuse.

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 Repository

Most 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.

VIII.G. 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(false) {
        //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?

VIII.H. Other Things

Avoid the following:

function test($var = NULL) {
    if($var == 'test') {
        ... lots of lines ...
    }
    else {
        return false;
    }
}

##############################

if($test === 'test) {
    return true;
}
else {
    return false;
}

##############################

function test($value) {
    $test = $value;
    $test = str_replace('/', '', $test);
    return $test;
}

Instead, do this:

function test($var = NULL) {
    if($var !=== 'test') {
        return false;
    }
    ... lots of lines ...
}

##############################

return $test === 'test';

##############################

function test($value) {
    /*
        first we remove $test from that function to have :
        
        $value = str_replace('/', '', $value);
        return $value;
        
        and after :
    */
    
    return str_replace('/', '', $value);
}