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.

1.1 Indenting Code

iMarc follows the one true brace indent convention. Function, method, and class definitions place the curly brace, unindented on a new line by itself. Content after the curly brace is indented. Control blocks like if or while keep the curly brace on the same line.

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

Arrays — Complex or nested arrays use the following indention format, noted via the closing parenthesis characters:

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

1.2 Indenting Code & Markup

When breaking between PHP and HTML, follow the same indenting standards. With the exception of PHP tags, after one item opens, the next item is indented.

<div>
	<?
	try {
		$users->tossIfEmpty();
		?>
		<table class="item_list">
			<tr>
				<th>Name</th>
			</tr>
			<? foreach ($users as $user) { ?>
				<tr>
					<td>
						<?= $user->getName() ?>
					</td>
				</tr>
			<? } ?>
		</table>
		<?
	} catch (fEmptySetException $e) {
		$e->printMessage();
	}
	?>
</div>

1.3 Indenting SQL

Non-trivial SQL statements should be broken onto multiple lines for easier reading, e.g.:

SELECT
		user_id,
		first_name,
		last_name
	FROM
		users
	WHERE
		user_id = 1
	ORDER BY
		first_name ASC,
		last_name ASC

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 equal sign, not the referenced object:

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

4 Class, Method, and Function Definitions

Class declarations follow the one true brace convention described above in indenting.

class MyObject
{
	//....
}

Function and method declarations also follow this same indenting style:

static public function fooMethod($bar, $baz, $foobar='')
{
	//....
}

private function barMethod($arg_1, $arg_2='')
{
	//....
}

For consistency, method visibility should be explicitly defined. Even though public is implied and not required.

While static public function foo() and public static function foo() are treated identically, we prefer starting with static so the static keyword stands out.

Arguments with default values go at the end of the function’s 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). These functions are placed at the bottom of the page. This helps distinguish functions defined on the current page from user-defined functions in included files. These functions can generally be avoided.

5 Storing and Naming Files

This section describes our naming convention and standard directory structure.

5.1 Typical Directory Structure

Each developer and team has slight variations on directory structure and folder organization. One key objective is to group all application files — classes, libraries, and files that the user never requests. Another practice is to group all support files — files that the browser requests, but aren’t actual pages or public facing URLs.

A typical directory structure for a website groups all PHP, application files in the /inc/.

Supporting markup, images, and supporting files are stored in subfolders under /sup/.

Files that can be written or uploaded by the webserver are stored in /writable/.

.htaccess

index.php - typically the only visible file not in a directory

/inc - PHP, application code
	init.php - PHP bootstrap
	/classes
	/flourish
	/templates
		/site
			header.php
			footer.php
		/admin
			header.php
			footer.php

/sup - supporting files
	/ajax
	/css
	/flash
	/html
		robots.txt
		404.php
	/img
	/js

/writable - organized in sub-directories

5.2 Public Directories and Web Files

All web pages pages are stored in directories that correspond with the public name in the navigation menu. These directories are lower case with underscores instead of spaces. For example, all pages in the “About Us” section should be found in the directory /about_us/

Web files follow the same rule. The “Contact Us” page would be named contact_us.php. The “Board of Directors” page would be name board_of_directors.php.

Do not shorten or abbreviate web directory or file names.

5.3 Pages and Included Views

Within a public directory there may be controller pages and views. Typically, the controller page performs logic, sets variables and includes an appropriate view. The controller page is the public web file and follows naming rules for public directories and web files.

Both the controller and the view are named as .php files. Even if a view contains no PHP, it is still named file.php, not file.inc or file.html.

The following is an acceptable directory structure for a section of a website with multiple pages and views.

/about_us
    index.php
    contact.php
    board_of_directors.php
    /views
        /contact
            form.php
            confirmation.php
        /board_of_directors
            list.php
            detail.php

With simpler sections, it is acceptable to remove the sub-directory in the views folder.

/search
    index.php
    /views
        form.php
        results.php

In the following example we see sidebar.php, a common file included in both views. That file is in a sub-directory, named partials. Even if the partials are static chunks of markup, filesnames still end in .php.

/search
    index.php
        /views
            form.php
            results.php
            /partials
                sidebar.php

5.4 Naming Libraries and Classes

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

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

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

6 Comments

Inline documentation for classes follows the PHPDoc convention.

Wrap comments at 80 characters when possible to increase readability.

/**
 * Sets the user's password. If the password is empty, the existing value will be left. 
 * If the password changed, an email is triggered to the user.
 * 
 * @param  string $password User's password
 * @return void
 */

6.1 Comments for Functions and Class Methods

Functions and methods are commented using the template below. If there are no parameters, do not include @param void.

@throws tags should be included for every class of exception that is thrown under normal circumstances and that should be handled by calling code. For instance, if an exception is thrown when the network is down, you don’t need to document that.

/**
 * Description of function.
 *
 * @throws ExceptionClass  Under what conditions the exception is thrown
 * @throws OtherException  Under what conditions the exception is thrown
 *
 * @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 using the template below. Non-framework code should not include @version or @changes tags.

/**
 * Description of class
 *
 * @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.

Class variables can be commented like either of these. Just stay consistent within the same document:

/**
 * Variable description
 *
 * @var datatype
 */
private $database;

Class methods are commented the same as functions.

6.3 Versioning

All framework class files are versioned with three revision numbers.

[major].[minor].[bugfix]

6.4 Inline Comments

Inline comments should explain the reason behind a line of code or block of code. Comments should not explain what the code is actually doing, as that can be surmised from the code itself.

Inline comments should always follow the C++ comment style with two forward slashes

// Admin searches go back to the SiteManager since the normal results view can't 
// handle ordering by anything but association or date created
if ($admin_search) {
    $destination = '/sitemanager/assets/assets.php' . $destination;  
}

// Set domain-wide cookie for 3rd party video site to read (video.example.com)
fCookie::set('video_access', 'on', '0', '/', '.example.com');

User::foo(); // brief explanation 

C-style comments, /* comment */ are reserved for header blocks, classes, and function commenting as described elsewhere in this document. Using C-style comments inline can cause problems if a maintainer wants to comment out a large block of code.

// The inline comment below, makes commenting out the entire block impossible
/*
if (User::isAdministrator()) {
    User::foo(); /* brief explanation */
}
*/

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

In views and HTML pages, minimize PHP and use short tags when mixing in PHP.

<div class="success">
	<p>
		<?= $name > successfully added.
	</p>
</div>

<table>
<? foreach($users as $user) { ?>
	<tr>
		<td><?= $user->prepareName(); ?></td>
		<td><?= $user->preparePhone(); ?></td>
		<td><?= $user->prepareWaterPoloScore(); ?></td>
	</tr>               
<? } ?>
</table>

We realize that using shorthand tags makes code less portable with short_open_tag turned off.

Break out of PHP for any non-trivial HTML output.

<?php 
$template->set('page_name', 'Welcome');
$template->set('section', 'Homepage');
$template->place('header');
?>

<p>
	Welcome to my web page.
</p>

<?php 
$template->place('footer');
?>

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>

8 Header Comment Blocks (optional)

If you think that a non-class page requires more explanation, you can optionally add a comment to start the pages. If you choose to add a header comment, it can be formatted like this:

/* --------------------------------------------------------------------- */
/* Overall description of entire page.
/* 
/* @author  Original Author [initials] <user@example.com>
/* --------------------------------------------------------------------- */

9 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

10 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 or $_POST.

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 includes should include ../. If you need to traverse up a directory, use an absolute path containing $_SERVER['DOCUMENT_ROOT'] instead.

require_once $_SERVER['DOCUMENT_ROOT'] . "/path/to/file.php";

When including a relative path, always specify ./ at the beginning.

11 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 the table name followed by _id:

Table Name:  users
Primary Key: user_id

12 Regular Expression Use

Always use preg_ functions 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.

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

14 Private Variables

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

class Foo {
	private $bar;

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

15 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
}

16 Existence checking

Often you’ll need to check whether or not a variable, property or array key exists. There are several similar methods to do this. In general, use the simplest.

(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.
}

(c) Don’t use array_key_exists() unless it is possible that an array key has a value of null. array_key_exists() runs many times slower than isset() because a full array scan must be performed:

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

17 Quotes

Use your judgement when quoting strings. The following variations of single and double quotes are all considered good practice at iMarc. Just be consistent within a single application.

$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') { ... }

Database::Select('*', 'table', 'this', 'that');

18 define()

Constants should use ALL CAPITAL LETTERS.

define("MY_VARIABLE", "foo");
define("MY_OTHER", "bar"); 

20 Setting Variables

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

$foo   = (isset($_GET['foo'])) ? $_GET['foo'] : '';
$bar   = request_value('bar'); // on older RecordClass-driven sites
$bax   = fRequest::get('baz'); // on newer Flourish-driven sites
$error = '';

21 Naming Conventions

File naming conventions are descibed above in Storing and Naming Files. Database naming conventions are described in Database Naming Conventions.

See CSS Class and ID Names for information about naming markup selectors.

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

21.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;

21.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 { ... }

21.3 Class Names

Classes should be given descriptive names. Avoid using abbreviations.

Classes are named in upper camel case. Each word in the class name should start with a capital letter, without underscore delimiters.

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

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

21.4 Method Names

Method names follow the lower camel case convention. 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 { ... }

22 Security

Preventing Cross-Site Scripting (XSS)

Whenever working with data input by a user, whether from the query string, form inputs or cookies, be sure to escape the data before echoing it to prevent XSS. This prevents malicious users from injecting javascript functionality or unapproved content into pages.

// GOOD Examples
echo htmlentities($_GET['name'], ENT_COMPAT, 'UTF-8');

// BAD Examples
echo $_POST['email'];

Use Appropriate HTTP Methods

Any sort of script that saves data in a database, changes the state of something, sends an email, or any other sort of creative or destructive action should always be done via the HTTP POST method. This helps prevent Cross-Site Request Forgeries (CSRF) and prevents search engines from affecting the content of a site.

// DO this
if ($_SERVER['HTTP_METHOD'] == 'POST') {
	delete_from_database();
}

// NOT this
delete_from_database();

Use Tokens to Prevent Cross-Site Request Forgeries (CSRF)

CSRF attacks leverage the fact that users are often logged into multiple sites in a single browser at a time. They make fake requests to sites via image tag src URLs and javascript.

To prevent CSRF attacks, use the HTTP POST method for creative or destructive actions and always include a token. The token should be a random string that is provided to the user in a hidden form input and is then verified against a list of tokens saved in their session.

23 URLs

Whenever possible, use mod_rewrite or a PHP front controller to create URLs that don't have the .php suffix. Similarly, try to avoid having URLs that reference index.php.

// GOOD urls
/about/contact_us
/members/benefits
/

// BAD urls
/about/contact_us.php
/index.php

When creating URLs for dynamic or database-driven content, put database primary keys and actions in the path instead of the query string. If possible, also include the name of the resources to make for human-readable URLs.

// GOOD urls
/news/12-nerds-fight-about-code-standards 
/events/201-annual_membership_class

// BAD urls
/news/index.php?news_id=12
/events/?event_id=201&action=view

24 UTF-8

UTF-8 should be used as the character encoding for all aspects of a site/app. UTF-8 is one of the most robust and widely-supported character encodings, which means that all parts of a site can share content without converting between encodings.

The following list of includes some of, but not necessarily all, the aspects of a site that need to be build with UTF-8 in mind:

HTML
Set your editor to use UTF-8 by default and be sure to output correct content-type headers and meta tags
PHP
Use correct content-type headers and meta tags to ensure that browsers are sending UTF-8 for form contents. Also use UTF-8 safe string functions.
Database
Most databases require that the encoding be set when the database is created. Ensure your database connection is set to use UTF-8.
Email
When sending emails, be sure to use code that supports UTF-8 in recipient names, the subject and the body
Other Data
Be sure to convert (if necessary) and clean data coming from old databases, external systems, XML files, CSV files and other such data sources

25 XHTML 1.0 Compliance

All HTML should comply with XHTML 1.0 Strict. For more information about this standard, see http://www.w3.org/TR/xhtml1/.

26 Markup Standards

All pages should work in every browser, even if they don’t look the same across all browsers.

Use XHTML Strict 1.0 DOCTYPE declaration. A strict DOCTYPE is always preferred over transitional.

The <html> tag should contain the appropriate lang attribute(s) and ISO code(s).

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

The encoding type should be specified in the meta tag as in the following:

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

All CSS and XHTML output must be validated.

Data container tables should contain a summary attribute.

<table summary="Table containing main toolbar items (icons and links).">

All style rules should be in CSS rather than included in a style attribute for a given element.

All form elements need label tags.

Font size, specified in CSS, should be in relative measures whenever possible.

New windows should not be opened via the target attribute in an a tag. Instead it should be controlled through JavaScript.

All JavaScript functions and CSS should be in external files rather than in the head element, unless it’s a single page script or style.

Simple markup on a single line contains no extra whitespace between the tag and its contents.

<h1>My Title</h1>

Multi-line markup starts a new indented line after the opening tag, and closes the tag on a new outdented line.

<p>
    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 
    Vestibulum condimentum wisi eget diam. Phasellus felis. 
    Nullam tincidunt. Lorem ipsum dolor sit amet.
</p>

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

CSS class and ID names should be lowercase and use underscores to separate words.

// GOOD CSS Class Names
.pull_quote { ... }
.minor_navigation { ... }
#secondary_content { ... }

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

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

26.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 { ... }

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

Addendum 1: Tips

Don’t use a semicolon with the short tag echo shortcut, <?= $foo ?>

Don’t return booleans in simple conditional statements. Instead, return the conditional check.

// No
if ($foo == 'bar') {
	return true;
}
retrun bar;

// No 
return ($foo == 'bar') ? true : false;

// Yes
return ($foo == 'bar');

Add Google Analytics and other site tracking JavaScrips at the close of the page, right before </body>