1 Indenting

Use tab indents at line beginnings. A tab is expected to represent four spaces. For inline spacing, use spaces, not tabs.

if (condition) {
    action; // indented with one tab
}
$foo      = "far";    //lined up with spaces
$foo_bar  = "foobar"; //lined up with spaces
Since inline content is lined up with spaces, a mono spaced font is always used when editing code.

2 Control Structures

These include if, for, while, switch, etc. Here is an example if statement, since it is the most complicated:

if ((condition1) || (condition2)) {
    action1;
} elseif ((condition3) && (condition4)) {
    action2;
} else {
    defaultaction;
}

Note the use of 'elseif', not 'else if'.

Multi-line if conditions are braced this way:

if ((condition1) || (condition2) || (condition3) ||
    (condition4)) {
    action1;
}

Control statements have one space between the control keyword and opening parenthesis, to distinguish them from function calls.

Do not omit the curly braces under any circumstance. In the case of a large number of short tests and actions, the following is acceptable:

if (condition)   { action; }
if (condition 2) { action 2; }
...

For switch statements, cases are indented one level. Case actions are indented two levels.

switch (condition) {
    case 1:
        action1;
        break;

    case 2:
        action2;
        break;

    default:
        defaultaction;
        break;
}

3 Function Calls

Functions are called with no spaces between the function name, the opening parenthesis, and the first parameter; spaces between commas and each parameter, and no space between the last parameter, the closing parenthesis, and the semicolon. Here's an example:

$var = foo($bar, $baz, $quux);

As displayed above, there is one space on either side of an equals sign used to assign the return value of a function to a variable. In the case of a block of related assignments, more space, using spaces not tabs, may be inserted to promote readability:

$short         = foo($bar);
$long_variable = foo($baz);

If assigning a reference to a variable, place the ampersand next to the referenced object, not the equal sign:

$reference = &$foo;
$reference = &foo();

4 Function and Class Definitions

Function declarations follow the "one true brace" convention:

function foo_function($arg1, $arg2 = '')
{
    if (condition) {
        statement;
    }
    return $val;
}

Arguments with default values go at the end of the argument list. Always attempt to return a meaningful value from a function if one is appropriate.

Functions used only on the current page begin with a _ character (e.g. _example_function), and placed at the bottom of the page. This helps distinguish functions defined on the current page from user-defined functions in included files.

Class declarations also follow the one true brace convenction:

class MyObject
{
    //....
}

5 Naming Libraries and Classes

Function libraries and user-defined class files are placed in the /includes/ directory of the application.

Function and configuration files are named with all lowercase letters and underscores, following the same rules as function names Example: /includes/store_functions.php.

Class files are named [ObjectName].class.php, following the same rules as class names. If the class is extended, the extending files are stored in a directory under /includes/ with the same name as the original object. Subclasses follow the exact same naming requirements.

Tester scripts are named [ObjectName].test.php

Common user defined functions are stored in /includes/base.functions.php

6 Comments

Inline documentation for classes follows the Javadoc convention.

6.1 Comments for Functions

Static functions are commented like this:

/**
 * Description of function.
 *
 * @param  datatype  $variablename   Description of variable.
 * @param  datatype  $variable2name  Description of variable2.
 * @return datatype  Description of return value.
 */

6.2 Comments for Classes

The class header block at the top of the class file are commented like this:

/**
 * ObjectName - description
 *
 * Copyright 1999-2005 iMarc LLC
 *
 * @version 0.0.2
 *
 * @author  Original Author [initials] <author@example.com>
 * @author  Your Name [initials] <you@example.com>
 * 
 * @todo    description
 *
 * @changes 0.0.2 Description [initials, YYYY-MM-DD]
 * @changes 0.0.1 Description [initials, YYYY-MM-DD]
 */

Any edit that noted in the changelog requires new authors to add their name and initials to the @authors list

Comment class variables like this:

/**
 * Variable description
 *
 * @var datatype
 */

Class methods are commented similar to functions, with some additional information.

/**
 * The description of the method goes here.
 *
 * @since  version number
 * 
 * @param  datatype  $variablename   Description of variable.
 * @param  datatype  $variable2name  Description of variable2.
 * @return datatype  Description of return value.
 */

6.3 Versioning

All class files are versioned with three revision numbers.

[major].[minor].[bugfix]

7 Including Code

With the exception of HTML and template files, included files always use require_once. This will ensure that no matter how many factory methods we use or how much dynamic inclusion we do, the library will only be included once.

If you are dynamically including a filename, or want the code to only be used conditionally (an optional template), use include.

8 PHP Code Tags

In class files, always use <?php ?> to delimit PHP code, not the <? ?> shorthand. This is required for PEAR compliance and is also the most portable way to include PHP code on differing operating systems and setups.

On general pages it's OK to use shorthand tags (<?= $var ?>), but keep in mind that using shorthand tags makes your code less portable with short_open_tag turned off.

Break out of PHP if the majority of line is outputting HTML or text.

<?php 
    // do stuff in PHP
    ?>
    Are you sure that you want to delete <?= $thing ?>
    <?php 
    // more php
?>

If an opening tag is printed outside of PHP, it's closing counterpart is output the same way.

<table>
    <tr>
        <?php 
            // do stuff in PHP
        ?>
    <tr>
<table>

9 Header Comment Blocks

All other source code files contain the following comment block as the header:

/* --------------------------------------------------------------------- */
/* (optional) Page description
/* 
/* @author  Original Author [initials] <user@example.com>
/* @author  New Author [initials] <user@example.com>
/* --------------------------------------------------------------------- */

Generally, page skeletons should follow this structure:

  1. header comment block
  2. requires, object initiation
  3. template, print header
  4. body comment block
  5. body
  6. footer comment block
  7. print footer
<?php
/* --------------------------------------------------------------------- */
/* @author  Dave Tufts [dt] <dave@imarc.net>
/* --------------------------------------------------------------------- */
require_once($_SERVER['DOCUMENT_ROOT'] . "/includes/init.php");

$db       = new Database();
$session  = new Session();
$template = new Template("template", "Title");
$template->printHeader();

/* --------------------------------------------------------------------- */
/* Body
/* --------------------------------------------------------------------- */
?>

content...

<?php
	$foo   = (isset($_REQUEST['foo'])) ? $_REQUEST['foo'] : '';
	$error = '';
?>


<?php
/* --------------------------------------------------------------------- */
/* Footer
/* --------------------------------------------------------------------- */
$template->printFooter();
?>

10 Example URLs and IPs

Use example.com for all example URLs, per RFC 2606.

Use the IP range 192.0.2.0/24 for all example IP addresses, per RFC 3330. If you just need a single IP address use 192.0.2.1

11 php.ini settings

All code must work with register_globals disabled. This means using $_COOKIE, $_SESSION, $_SERVER and $_ENV to access all cookie, session, server and environment data, respectively.

To retrieve submitted data, you can use $_GET, $_POST or $_REQUEST

All class files must work with error_reporting = E_ALL and E_STRICT. Failure to do so would result in ugly output, error logs getting filled with lots of warning messages, or even downright broken scripts.

No code assumes that '.' is in the include path. The preferred way to include a file is to specify it's full server path using DOCUMENT_ROOT:

require_once($_SERVER['DOCUMENT_ROOT'] . "/path/to/file.php");
In certain cases a relative path is needed. Always specify './' in front of a filename when you are including a file in the same directory.

12 XHTML 1.0 Compliance

All tag names and parameters must be lower case including javascript event handlers:

<div style="color:#000;">...</div>
<a href="http://example.com" onmouseover="status=''" 
  onmouseout="status=''">...</a>

All tag parameters must be of a valid parameter="value" form (numeric values must also be surrounded by quotes). For parameters that had no value in HTML, the parameter name is the value. For example:

<input type="checkbox" checked="checked" />
<select name="example">
    <option selected="selected" value="1">Example</option>
</select>
<td nowrap="nowrap">Example</td>

All tags must be properly closed. Tags where closing is forbidden must end with a space and a slash:

<br />
<hr />
<img src="example.gif" alt="Example" />
<input type="submit" value="Example" />

All form definitions must be on their own line and either fully defined within a <td></td> pair or be outside table tags. Forms must also always have an action parameter:

<form action="http://example.com/example.cgi" method="post">
<table>
    <tr><td>example</td></tr>
</table>
</form>

<table>
    <tr>
        <td>
            <form action="/foo.php" method="get">
            </form>
        </td>
    </tr>
</table>

All JavaScript tags must have a valid type parameter:

<script type="text/javascript">
<!--
...
// -->
</script>

Nothing may appear after </html>, therefore include any common footers after all other output.

All images must have an alt attribute:

<img src="/images/foo.gif" alt="Foo" />

Input fields of type "image" also require an alt attribute, but do not allow the border attribute.

External cascading style sheets are save in the /css/ directory for that site

External javascript files are save in the /js/ directory for that site

Images are saved in the /images/ directory for that site. If desired, images can be broken up into sub-directories, all under the main /images/ directory

13 Database Naming Conventions

Name your database after the primary domain it serves. Since database names can't contain periods, replace the domain's period(s) with underscores.

Domain: example.com
Database Name: example_com

Domain: wiki.example.com
Database Name: wiki_example_com

All database tables need to make sure that their table and field names work in all databases. Many databases reserve words like 'uid', 'user', etc. for internal use, and forbid words that are SQL keywords (select, where, etc.).

All names (database, table, and column names) are lowercase, with underscores ('_') to separate words, to avoid case sensitivity issues.

Table names are plural (users).

Column names are singular (user_name).

The primary key column is named the singular of table name followed by _id:

Table Name:  users
Primary Key: user_id

14 Regular Expression Use

Always use the preg_ functions if possible instead of ereg_ (and preg_split() instead of split()); they are included in PHP by default and much more efficient and much faster than ereg_.

NEVER use a regular expression to match or replace a static string. explode() (in place of split()), str_replace(), strpos(), or strtr() do the job much more efficiently.

15 Parameter Passing

Objects are passed by reference. Everything else, including arrays, is passed by value wherever semantically possible.

[Zend Engine 2: objects are also be passed by value]

This practice takes full advantage of reference counting.

16 Long Lines

Wrap comments and SQL statements at 80 characters. Try to keep lines of code and HTML as clear and readable as possible.

17 Line Breaks

Only use UNIX style of line-break (\n), not Windows/DOS/Mac style (\r\n).

Using vi, to convert from DOS style type:

:g/^M/s///g

Using Dreamweaver:

Under the "Preferences" menu, 
    select the category, "Code Format".
Select "LF (Unix)" as the line break type.

18 Private Variables

In PHP 5, make all class variables private or protected (unless there's a really good reason not to). Create setXxx() methods to set private class variables, and getXxx() methods to retrieve their data

class Foo {
    private $bar;

    public setBar($in) {
        $this->bar = $in;
    }
    public getBar() {
        return $this->bar;
    }
}

19 Array Definitions

When defining arrays, or nested arrays, use the following format, where indentation is noted via the closing parenthesis characters:

$arrayname['index'] = array(
    'name1' => 'value1',
    'name2' => array(
        'subname1' => 'subvalue1',
        'subname2' => 'subvalue2'
    )
);

The only exception is for empty or short arrays that fit on one line, which may be written as:

$arrayname['index'] = array();

20 Error checking

Where possible, use try/catch blocks.

try {
    if (empty($foo)) {
        throw new Exception ("Error message");
    }
    ...
} catch (Exception $e) {
   echo $e->getMessage();
}

For simple checks, use the variable $error to flag errors. On any page that checks for $error set $error at the top of the page:

$error = '';
...
if ($error) {
    action
}

21 Existence checking

Often you'll need to check whether or not a variable or property exists. There are several cases here:

a. If you need to know if a variable exists at all and is not null, use isset():

// Check to see if $param is defined.
if (isset($param)) {
    // $param may be false, but it's there.
}

b. If you need to know if a variable exists AND has a non-empty value (not null, 0, false, empty string or undefined), use empty():

// Make sure that $answer exists, is not an empty string, and is
// not 0:
if (!empty($answer)) {
    // $answer has some non-false content.
} else {
    // (bool)$answer would be false.
}

As pointed out in the comment of the else clause, empty() essentially does the same check as isset() -- is this variable defined in the current scope? -- and then, if it is, returns what the variable would evaluate to as a boolean. This means that 0, while potentially valid input, is "empty" - so if 0 is valid data for your case, don't use empty().

c. If you know you are working with a mixed variable then using just isset() and empty() could cause unexpected results, for example if testing for a key and the variable is actually a string:

$foo = 'bar';
if (isset($foo['somekey'])) {
    // This will evaluate to TRUE!
}

If you know that there is a possibility of a mixed type variable the solution in this case would be to add an is_array() check in the if() statement.

d. Use array_key_exists() when you want to check if an array key is defined even if it has a value of null:

// Make sure we have a charset parameter. Value could also be null.
if (!array_key_exists('charset', $params)) { }

Please note that array_key_exists() is a performance hit (25%-100%) and should only be used when necessary. Instead try to use empty() or isset() instead.

22 Quotes

Use your judgement when quoting strings. The following are all considered good practice at iMarc.

$foo = "value";
$foo = "Isn't it neat?";
$foo = 'Bob said, "I like that"';
$foo = "Name\tEmail\tZip\n";

echo "Hello World";
echo 'Hello World';

$foo['bar'] = "someval";

if ($var == 'something') { action; }

$db->simpleSelect('*', 'table', 'this', 'that');

PHP does treat single quotes and double quotes differently:

Single Quotes:
  • Variables in the string are not parsed or expanded.
  • New line symbols can be included as literal line ends (not recommended).
  • To include a single quote character, escape it with a \ (backslash) character, as in: echo 'Here\'s an example';
  • Backslash (\) characters do not need to be escaped when single quoted: echo 'c:\\temp';
Double Quotes:
  • Parses and expands variables in the string.
  • Uses advanced (sprintf-style) escape sequences like \n, \$, \t, etc.
  • Should be used in the gettext shortcut _("") format.
  • Use with care, as many correct looking strings are really invalid.
  • To specify a \ (backslash) character in a double quoted string, escape it with another backslash:: echo "c:\\\\temp";

23 define()

Constants that are set in an included file (/includes/config/local.config.php, for example), use ALL CAPITAL LETTERS. Constants defined on an individual page, use all capital letters preceded by an underscore. The preceding underscore tells future developers that it's defined on the current page

define("MY_VARIABLE", "foo"); // defined in included file
define("_MY_VARIABLE", "foo"); // defined on current page

24 Optimizations

The following optimizations are used, if possible:

24.1 Concatenate strings

Building a string with concatenation (the "." operator) using single-quoted strings and variables is faster than using an interpolated string (a string inside double quotes with variables inside the string itself).

That said, iMarc implements concatenation using double-quoted strings. Concatenation is easier to read than embedding variables when auditing code for logic and security problems.

24.2 Loops

Make sure that you do not continue to define the same variable within a loop. Instead, declare the variable a single time before the loop is run.

$entries = array(...);

// slower
for ($i = 0; $i < count($entries); ++$i) {
    echo $entries['foobar'];
}

// faster
$length = count($entries);
$foobar = $entries['foobar'];
for ($i = 0; $i < $length; ++$i) {
    echo $foobar;
}

With large arrays, array_key_exists() seems very slow.

// slower (with large arrays)
if (array_key_exists($key, $array)) { ... }

// faster
if (is_array($array) && isset($array[$key])) { ... }

25 Setting Variables

For security and readability, set all non-superglobal page variables at the top of each page.

$foo   = (isset($_REQUEST['foo'])) ? $_REQUEST['foo'] : '';
$bar   = request_value('bar'); // *see note below
$error = '';
*request_vaule() is only available in iMarc's base.functions.php framework file.

26 Naming Conventions

File naming conventions are descibed above in Naming Libraries, for programming files, and below in File Naming for HTML, CSS, and other file non-programming files. Database naming conventions are described in Database Naming Conventions

The following describes how PHP variables, functions, classes, and methods should be named

26.1 Variable Naming

Variable names must be meaningful. One letter variable names must be avoided, except for places where the variable has no real meaning or a trivial meaning (e.g. for ($i=0; $i<100; ++$i)).

Variable names should be in lowercase and use underscores to separate words.

$page_function = "foo";
$database_user = "mysql";
$i             = 0;

26.2 Function Names

User defined functions should be in lowercase, with words underscore to separate words. Take care to minimize the letter count, but do not use abbreviations, because they greatly decrease the readability of the function name itself.

// GOOD Examples
function mcrypt_self_test { ... }
function mysql_list_fields { .... }

// BAD Examples
function hw_GetObjectByQueryCollObj { ... }
function jf_n_s_i { ... }

26.3 Class Names

Classes should be given descriptive names. Avoid using abbreviations where possible. Each word in the class name should start with a capital letter, without underscore delimiters (CamelCaps starting with a capital letter).

// GOOD Examples
class Curl { ... }
class FooBar { ... }

// BAD Examples
class foobar { ... }
class foo_bar { ... }

26.4 Method Names

Method names follow the 'studlyCaps' (also referred to as 'bumpy case' or 'camel caps') naming convention, with care taken to minimize the letter count. The initial letter of the name is lowercase, and each letter that starts a new 'word' is capitalized.

// GOOD Examples
function public connect() { ... }
function public getData() { ... }

// BAD Examples
function public get_Data() { ... }
function public buildsomewidget { ... }

27 File Naming Conventions

Also see Naming Libraries, for programming file naming standards.

27.1 CSS Files

CSS files are typically be saved in the website's /css/ directory or in a subdirecory of /includes/ or /lib/.

It's encouraged to split style selectors into multiple files.

Note: Sites commonly only use two templates – public and sitemanager – with no common syles shared between the two. In such cases, the site does not need a base.css.

28 Markup Standards

28.1 CSS Class and ID Names

CSS class and id names should describe the content they reference not how the content is laid out. Naming references to visual design such as 'leftcolumn' or 'redtext' should be avoided.

// GOOD CSS Class Names
.pullquote { ... }
.minor_navigation { ... }
.secondary { ... }

// BAD CSS Class Names
.blue_quote { ... }
.left_nav { ... }
.column { ... }

28.2 Inline CSS Styles

Avoid inline CSS styles wherever possible. Never reference a CSS selector and add to or override elements with inline style. The following code is pure evil.

// Pure EVIL
<div class="blue_quote" style="color: #f00;">
  Foobar
</div>

28.3 Mimimal Markup

Minimize the use of HTML markup and CSS classes and ids.

// Bad example (HTML and CSS)
<div class="headertitle">...</div>
<div class="headerdescription">...</div>
<div class="headerlinks">...</div>

.headertitle { ... }
.headerdescription { ... }
.headerlinks { ... }

// Better example (HTML and CSS)
<div id="header">
    <h3>...</h3>
    <p>...</p>
    <ul>...<ul>
</div>

#header h3 { ... }
#header p  { ... }
#header ul { ... }

28.4 iMarc HTML Head

iMarc pages should also include a meta credits tag, stating "Design, programming, and hosting by iMarc. More info at http://imarc.net"

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta name="credits" content="Design, programming, and hosting 
        by iMarc. More info at http://imarc.net" />
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    
    <title>Title</title>
</head>