PHP Programming Guidelines
Author: Mikael Simonsson,

1. Introduction

1.1 ) Notes
These guidelines are based on my programming style and you are surely allowed to ignore them if something doesn't suit you or if it enhances readability.

I welcome all of your comments and suggestions.
2. Naming Conventions

2.1 ) Variable names should be in mixed case starting with lower case prefix.
$sName, $iSizeOfBorder // string, integer
This makes it very easy to look at a variable and directly see what it contains.

See appendix A for a list of common prefixes.
2.2 ) Boolean variables should use the prefix b followed by is, has, can or should.
$bIsActive, $bHasOwner
This also applies to functions that return boolean, but without the b prefix, for example:
$user->hasEmail();
2.3 ) Negated boolean variable names must be avoided.
var $bIsActive;
// Not: $bIsNotActive

var $bHasId;
// Not: $bHasNoId
It's not directly obvious what that following code does:

// Not:
if ( !$bIsNotActive )
{
    ...
}
2.4 ) Object variables should be all lowercase or use the prefix o or obj.
$session, $page // Preferred
$oSession
$objPage
All lowercase is the preferred here but the important thing is to be consistent.
2.5 ) Constants must be all uppercase using underscore to separate words.
$SESSION_TIMEOUT, $BACKGROUND_COLOR, $PATH
However, in general the use of such constants should be minimized and if needed replace them with functions.
2.6 ) Function names should start with a verb and be written in mixed case starting with lower case.
validateUser(), fetchArray()
2.7 ) Abbreviations must not be uppercase when used in a name.
$sHtmlOutput
// Not: $sHTMLOutput
getPhpInfo()
// Not: getPHPInfo()

Using all uppercase for the base name will give conflicts with the naming conventions given above.
2.8 ) SQL keywords should be all uppercase.
SELECT TOP 10 sUsername, sName FROM users
This makes it much easier to understand and get an overview of a SQL query.
2.9 ) Private class variables should have underscore suffix.
class MyClass
{
    var $sName_;
}
This is a way of separating public and private variables. However this is not as important as in C++ since in PHP you use the $this-> pointer to access class variables.
2.10 ) All names should be written in English.
$iThreadId // Not: $iTradId (Swedish)
English is the preferred language for international development. This also applies to comments.
2.11 ) Variables with a large scope should have long names, variables with a small scope can have short names.
Temporary variables are best kept short. Someone reading such variables should be able to assume that its value is not used outside a few lines of code.

Common temporary variables are $i, $j, $k, $m and $n. Since these variables should have small scope prefixes are a bit of overkill.
2.12 ) The name of the object is implicit, and should be avoided in a method name.
$session->getId() // Not: $session->getSessionId()
The latter might seem natural when writing the class declaration, but is implicit in use, as shown in the example above.
2.13 ) The terms get/set must be used where an attribute is accessed directly.
$user->getName()
$user->setName($sName)
This is already common practice in languages like C++ and Java.
2.14 ) Abbreviations should be avoided.
$session->initialize();
// Not: $session->init();

$thread->computePosts();
// Not: $thread->compPosts();
There is an exception to this rule and that is names that are better known for their shorter form, like Html, Cpu etc.
3. Syntax

3.1 ) Function and class declarations.
function doSomething()
{
    ...
}

// Not:
function doSomething() {
    ...
}

The same applies to class declaration.
3.2 ) Statements and curly brackets.
if ( $bIsActive )
{
    ...
}

// Not:
if ( $bIsActive ) {
    ...
}

The first bracket should always be on the line after the statement, not on the same line. The code is much easier to follow this way. This also applies to for, switch, while etc.
3.3 ) Statements and spaces.
if ( $sName == 'John' )
{
    ...
}

// Not:
if($sName=='John')
{
    ...
}
4. Recommended practices

4.1 ) Double versus Single quotes.
// Double quotes:
$sSql = "SELECT iUserId FROM users WHERE sName = 'John Doe'";

// Single quotes:
echo '<td width="100"></td>';
As a general rule use double quotes with sql-commands otherwise single quotes.
4.2 ) Do not rely on register_globals
register_globals makes input from GET, POST, COOKIE directly accessible as variables in PHP, however you should never assume that this is set. Use $_GET, $_POST and $_COOKIE instead.
4.3 ) Do not rely on short_open_tag
If short_open_tag is set it allows the short form of PHP's open tag to be used, that is <? ?> instead of <?php ?>. Like register_globals you should never assume that this is set.
4.4 ) String concatenation.
$sOutput = 'Hello ' . $sName;
// Not: $sOutput = "Hello $sName";
Another readability issue.
4.5 ) Comment your code.
Always try to comment your code, regardless of how simple it may seem to you and remember to use English.
4.6 ) Complex code should always be avoided.
If you find that you have trouble understanding code you've written then try to picture other people understanding it. Comments help but doesn't always do it here so rewrite!
5. Security

5.1 ) Use htmlspecialchars when sending non-HTML tags to the browser.
$sTest = htmlspecialchars('"&<>');
// $Test would now contain: &quot;&amp;&lt;&gt;
This minimizes the risks of cross site scripting or that the formatting gets distorted. Applies to all data we get from GET, POST, COOKIE or a database.
5.2 ) Always initialize variables
// Not:
if ( $bIsAlwaysFalse )
{
    $sFilename = 'somefile.php';
}

if ( $sFilename != '' )
{
    // display $sFilename's source
    ...
}
If the first statement above evaluates to false then we're in trouble. A malicious user could inject his/her own value for $sFilename and view any file readable under the current security context.

The correct way to do it here would be to add $sFilename = ''; before the first statement.

Note: This is only true if register_globals is set but you should never assume otherwise. You never know if your code one day ends up on a server with register_globals set.
5.3 ) SQL-injection and input validation
// Not:
$iThreadId = $_POST['iThreadId'];

$sSql = "SELECT sTitle FROM threads WHERE iThreadId = " . $iThreadId;
To see what's wrong with to code above, lets take a look at the following HTML code:
<form method="post" action="insecure.php">

    <input type="text" name="iThreadId" value="4; DROP TABLE users">

    <input type="submit" value="Don't click here">

</form>
If we submit the above form to our insecure page, the string sent to the database server would look like the following, which is not very pleasant:
SELECT sTitle FROM threads WHERE iThreadId = 4; DROP TABLE users
There are several ways you can append SQL commands like this, some dependent of the database server.

For another example, look at the following code:
$sSql = "SELECT iUserId FROM users" .
        " WHERE sUsername = '" . $sUsername . "' AND sPassword = '" . $sPassword . "'";
We can easily skip the password section here by entering "theusername'--" as the username or "' OR '' = '" as the password (without the double-quotes), resulting in:
// Note: -- is a line comment in SQL so everything after it will be skipped
SELECT iUserId FROM users WHERE sUsername = 'theusername'--' AND sPassword = ''
// Or:
SELECT iUserId FROM users WHERE sUsername = 'theusername' AND sPassword = '' OR '' = ''
Here is where validation comes into play, in the first example above we must check that $iThreadId really is a number before we append it to the SQL-query.
if ( !is_numeric($iThreadId) )
{
    // Not a number, output error message and exit.
    ...
}
The second example is a bit trickier since PHP has built in functionality to prevent this, if it is set! This directive is called magic_quotes_gpc, which like register_globals never should have been built into PHP, and I will explain why.

To have characters like ' in a string we have to escape them, this is done differently depending on the database server:
// MySQL:
SELECT iUserId FROM users WHERE sUsername = 'theusername\'--' AND sPassword = ''
// MS SQL Server:
SELECT iUserId FROM users WHERE sUsername = 'theusername''--' AND sPassword = ''
Now what magic_quotes_gpc does, if set, is to escape all input from GET, POST and COOKIE (gpc). This is done as in the first example above, that is with a backslash. So if you enter "theusername'--" into a form and submit it, $_POST['sUsername'] will contain "theusername\'--", which is perfectly safe to insert into the SQL-query, as long as the database server supports it (MS SQL Server doesn't)! This is the first problem the second is that you need to unescape it if you're not using it to build a SQL-query.

A general rule here is to create your code so that it works regardless if magic_quotes_gpc is set or not. The following code will show you a solution to the second example:
// Strip backslashes from GET, POST and COOKIE if magic_quotes_gpc is on
if ( get_magic_quotes_gpc() )
{
    // GET
    if ( is_array($_GET) )
    {
        // Loop through GET array
        foreach( $_GET as $key => $value )
        {
            $_GET[$key] = stripslashes($value);
        }
    }

    // POST
    if ( is_array($_POST) )
    {
        // Loop through POST array
        foreach( $_POST as $key => $value )
        {
            $_POST[$key] = stripslashes($value);
        }
    }

    // COOKIE
    if ( is_array($_COOKIE) )
    {
        // Loop through COOKIE array
        foreach( $_COOKIE as $key => $value )
        {
            $_COOKIE[$key] = stripslashes($value);
        }
    }

}

function sqlEncode($sText)
{
    if ( $bIsMySql )
        return addslashes($sText);
    else // Is MS SQL Server
        return str_replace("'", "''", $sText);
}

$sUsername = $_POST['sUsername'];
$sPassword = $_POST['sPassword'];

$sSql = "SELECT iUserId FROM users" .
        " WHERE sUsername = '" . sqlEncode($sUsername) . "'" .
        " AND sPassword = '" . sqlEncode($sPassword) . "'";
Preferrably you can put the if statement and the sqlEncode function in an include.

Now as you probably can imagine a malicious user can do a lot more than what I've shown you here, that is if you leave your scripts vulnerable to injection. Check the reference section for more information on this topic.
References

Coming soon
Appendix A

A.1 ) Notation Specification
a Array
b bool
d double
f float
i int
l long
s string
g_ global (followed by normal prefix)
Valid XHTML 1.0!