Copyright 2005, 2010 iMarc LLC
Last updated January 8, 2010
@version: 2.0.1 [View Changelog]
@author: David Tufts [dt] <dave@imarc.net>
@author: William Bond [wb] <will@imarc.net>
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.
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' ) );
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>
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
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;
}
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();
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.
This section describes our naming convention and standard 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
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.
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
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.
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 */
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. */
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.
All framework class files are versioned with three revision numbers.
[major].[minor].[bugfix]
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 */
}
*/
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>
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> /* --------------------------------------------------------------------- */
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
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.
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
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.
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.
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;
}
}
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
}
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)) { }
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');
Constants should use ALL CAPITAL LETTERS.
define("MY_VARIABLE", "foo");
define("MY_OTHER", "bar");
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 = '';
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.
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;
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 { ... }
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 { ... }
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 { ... }
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'];
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();
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.
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
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:
All HTML should comply with XHTML 1.0 Strict. For more information about this standard, see http://www.w3.org/TR/xhtml1/.
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>
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 { ... }
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>
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 { ... }
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>
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>