Coding conventions (draft)
To a great extend complies with the phpBB Coding Guidelines.
PHP code tags
Always use <?php ?>. Others may not work depending on the server config.
<?php echo 'foo'; ?>
Variable Names, class names, function names
Use PascalCasing for classes and functions, camelCasing for variables. Private member variables of classes should use Pascal Casing, prefixed with “m_”.
Variable names should be descriptive, so that you can know or at least guess what the variable contains and is used for.
class Template; class AdminNotifications; class Url; $taskId; $userName; GetTaskDetails();
Length for all should be less than 25 chars.
Loops
For loops (and only for loops) you should use single letter variable names. When the depth increases, the variable name increases as well (starting with $i).
for ($i = 0; $i < 10; $i++) { for ($j = 0; $j < $array_count; $j++) { doFoo(); } }
Variable variables
Whenever possible, please avoid them. They can have a huge negative impact on the readability.
// Negative example, taken from Flyspray 0.9.8 (try to understand what this code does ^^) function cryptic_function($lang, $module) { $before = get_defined_vars(); require_once("lang/en/$module.php"); $after_en = get_defined_vars(); $new_var = array_keys(array_diff($after_en, $before)); $new_var_name = @$new_var[1]; $new_var['en'] = @$$new_var_name; if (file_exists("lang/$lang/$module.php")) { require_once("lang/$lang/$module.php"); } $new_var[$lang] = @$$new_var_name; $$new_var_name = @array_merge($new_var['en'], $new_var[$lang]); }
Function aliases
Do not use function aliases. So use implode instead of join and count instead of sizeof (for example).
Documentation
Whenever you create a new class or function, please provide PHPDoc comments like this one:
/** * Generates a message depending on the $type and using $data * @param integer $type * @param array $data usually contains task_id => $id * @return array which is array($subject, $body) * @access public */ function generate_message($type, $data = array())
Constant names
Constant names should only have uppercase letters, while multiple words are seperated by an underscore.
define('FS_VERSION', '0.9');
Assignments
If you do multiple, similar assignments, you should align them. The ”&” token should be adjacent to the ”=”, not the variable name.
$user_id =& $id; $user_name = null; $user_type = ADMIN;
Curly braces / Indentation
Never omit curly braces. This makes it easier to understand the code. Within each block indent using four (4) spaces. Tabs are only allowed for CSS or Javascript files 1).
//bad if ($do) DoStuff(); if ($do) DoStuff(); while ($do) DoStuff(); // good if ($do) { DoStuff(); } while ($do) { DoStuff(); }
Position of braces
You can either put braces in their own line, or put the opening brace in the same line as the statement. For switch and classes always put braces in their own line. Single line statements should not be used.
// bad if ($do) { DoStuff();} if ($do) {DoStuff();} function DoStuff() { die(); } // good if ($do) { DoStuff(); } if ($do) { DoStuff(); } function DoStuff() { die(); }
Tokens / Keywords
Always leave one space between tokens (if while . ? > , < = ; & || …), similar to the written language. Only use lowercase letters for keywords like if, true, null etc.
$i=0; // bad $i = 0; // good if($i<7) ... // bad if ($i < 7) ... // good if ( ($i < 7)&&($j > 8) ) ... // bad if ($i < 7 && $j > 8) ... // good DoStuff( $i, 'foo', $b ); // bad DoStuff($i, 'foo', $b); // good for($i=0; $i<$size; $i++) ... // bad for ($i = 0; $i < $size; $i++) ... // good $i=($j < $size)?0:1; // bad $i = ( ($j < $size) ? 0 : 1 ); // good
Comments
Always use /* */ for multi line and // for single line comments. You can also use comment “artwork” to divide a script visually.
/* This is a multi line comment */ /**********************\ * Next example * \**********************/ // Single line
Function calls
If you call a function, do not put a space between the function name and the opening parenthesis.
doFoo();
If you call a method, do not use ”→” but only ”::” if the method is a static method (or can be used as such).
// In Flyspray for example: Flyspray::Redirect(...); $user->can_DoStuff();
Statements
If a statement can be used both with and without parentheses (like require), do not use parentheses.
require 'myfile.php'; include 'another_file'; if ($do) { doFoo(); } echo 'bar';
Operators
Do you know the exact precedence of all the operators in PHP? Neither do I. Don't guess. Always make it obvious by using parentheses to force the precedence of an equation so you know what it does. Remember to not over-use this, as it may harden the readability. Basically, do not enclose single expressions. Examples:
// what's the result? who knows. $bool = ($i < 7 && $j > 8 || $k == 4); // now you can be certain what I'm doing here. $bool = (($i < 7) && (($j < 8) || ($k == 4))); // But this one is even better, because it is easier on the eye but the intention is preserved $bool = ($i < 7 && ($j < 8 || $k == 4));
Also use || instead of OR and && instead of AND. Use != instead of <> .
Increment/decrement operators
To increase the readability, increment/decrement operators should always be used on their own lne.
// bad $array[++$i] = $j; $array[$i++] = $k; // good $i++; $array[$i] = $j; $array[$i] = $k; $i++;
echo or print
Use echo.
echo 'foo';
In case you want:
echo 'foo' . $a . '..' . $b . '/' ; //.. many more
use printf().
printf('foo%s..%s/',$a, $b); //...many more
or vprintf() in more complicated cases:
vprintf('foo%s..%s/', array_map('htmlspecialchars',array($a,$b)));
instead of:
echo 'foo' . htmlspecialchars($a) . '..' . htmlspecialchars($b) . '/';
what is faster does not matter, code readability is more important.
Quoting
If you specify a string in double quotes, variables are parsed within it which is slightly more work for the parser. Additional to that there are more escape sequences. So use single quotes as long as it does not contain any variables.
// bad $foo = "bar"; // good DoStuff($myvar. 'foobar'); DoStuff("{$myvar}foobar"); // for many variables, prefer the second example DoStuff("{$a}{$b}f{$c}oobar"); // or consider sprintf() $var = sprintf('%s:foo %s - %s', $a, $b, $c);
Unquoted strings
In PHP you can specify strings without quotes if they don't contain chars which are not allowed in variable names. However, since this is bad style and generates a PHP notice (in fact an unquoted variable name should be a constant) we won't do it. Never.
// bad $foo = $assoc_array[blah]; $foo = bar; // good $foo = $assoc_array['blah']; $foo = 'bar';
Ternary operator
Only use it for simple expressions, preferably for assignments. Also add parentheses around the whole expression.
// bad ($i < $size && $j > $size) ? DoStuff($foo) : DoStuff($bar); // good $val = ( ($foo) ? $foo : $bar );
Cecking if a variable is empty
Do not use empty(), since this can only be applied to variables. You can not use it for expressions:
// doesn't work if (empty(Get::val('foo'))) ...
It is recommended to make use of type juggling here:
if ($foo) { DoStuff(); } if (Get::val('foo')) { DoStuff(); }
Keep in mind that “0” is considered an empty string in this case. If this is not appropriate in your situation use != '' to check if a string is empty.
Magic numbers
Do not use magic numbers (numbers that have a certain meaning). Instead we should use constants which makes the code more readable and maintainable. All constants should be saved in a single file though (preferably constants.inc.php).
//bad if ($type == 1) { DoStuff(); } elseif ($type == 2) { doFoo(); } // good define('COMMENT_ADDED', 1); define('COMMENT_EDITED', 2); if ($type == COMMENT_ADDED) { DoStuff(); } elseif ($type == COMMENT_EDITED) { DoFoo(); }
Error level
Our code should not generate any errors/warnings/notices if E_ALL is set for error reporting. So don't use uninitialized variables, non-extistent array keys or unquoted strings.
Nesting
Try not to use an unnecessary high level of nesting by making use of continue, break (loops) and return (functions).
// bad foreach ($arr as $key => $val) { if ($key == 1) { if ($val == 'foo') { echo 'foo'; } else { echo 'val'; } // and much more code } } // good foreach ($arr as $key => $val) { if ($key != 1) { continue; } if ($val == 'foo') { echo 'foo'; } else { echo 'val'; } // and much more code }
Switch formatting
The curly braces are on their own line. case is indented, everything after case is indented again. Successive case keywords are each on their own line (so when acting like OR). If there is no break at the end of a piece of code after case, add a comment so that it is obvious. Add an empty line after each case block. Example:
switch ($field) { case 'item_summary': case 'attached_to_project': case 'task_type': if ($field == 'task_priority') { $old_value = $priority_list[$old_value]; } break; case 'foo': echo 'bar'; // FALL THROUGH case TASK_OPENED: $return .= $details_text['taskopened']; break; }
File names
Basically files consist of an extension (only “php”) and a descriptive name.
Files which contain a class should use the following sheme: class.classname.php. classname is the (if necessary) abbreviated name of the class. Other include files should use name.inc.php. Examples:
class.tpl.php, fix.inc.php, modify.php
Template files have the extension “tpl” and can contain multiple words seperated by a dot to classify them. Examples:
details.tabs.comment.tpl, common.list.tpl, index.tpl
SQL queries Layout
Use uppercase letters for keywords in SQL statements (like SELECT). Align the query by keywords and only use single line queries if they are very simple.
Always use prepared statements if the query contains PHP variables, it does not matter where they come nor if you “think” it 's safe to use the variable on the query directly, contributions not following this rule will be rejected.
$history = $db->Query('SELECT * FROM {history}'); // Check for cached version $cached = $db->Query("SELECT content, last_updated FROM {cache} WHERE topic = ? AND type = ?", array($task_details['task_id'], 'task'));
for a larger/real world example, see script/reports.php in our svn repository.
Table Layout
All columns and table names are written lowercase everywhere. Column names should consist of two words, seperated by an underscore. Do not use SQL keywords for column names (so that backticks are not needed).
Various DONT-DOs
Please try to keep your code reasonable. Thus don't do stuff like the code below. BTW, I did not make this code up. I've seen it myself.
// Define the class [oh really? please spare those comments] $mail = new PHPMailer(); $date = time(); // so why not use $time? time !== date $time = date('U'); // well... see above function foo() { unset($incoming); // unset non existent variables?? $incoming = ... }
Dangerous stuff
Do not introduce new code using the following PHP functions/statements:
- eval(), we are attempting to remove existing code that use it, new code that uses eval() will not be accepted.
- Do not use $_SERVER['PHP_SELF'] when you really mean $_SERVER['SCRIPT_NAME'];
- the preg_replace ”/e” modifier should not be used, use preg_replace_callback instead.
- avoid system(), exec(), shell_exec(), popen(), and passthru() calls. If your really need this, ask for a second opinion on the mailing lists. Your code will need a security review before its inclusion.
- Obiously, code depending on either magic_quotes, register_globals, allow_url_fopen or allow_url_include PHP directives to be enabled, will never be accepted.