(→Database: Remvoed personal pronouns) |
mNo edit summary |
||
(52 intermediate revisions by 12 users not shown) | |||
Line 1: | Line 1: | ||
[[Simple Machines | {{Ambox| | ||
| text = This page describes the coding guidelines for the SMF "core" code, if you are writing a mod you may be looking for the [[Customization approval guidelines]]}} | |||
{{TOCright}} | |||
Simple Machines prides itself on code that is uniform and easy to read. To preserve these qualities, and to maintain performance and reduce the risk of introducing certain types of bugs, the [http://www.simplemachines.org/about/smf/team.php SMF Team] maintains coding guidelines to be used when writing customizations ([[mods]]/[[Theme|themes]]) as well as tools and core SMF code. Although it is not always possible to follow all guidelines exactly, breaking them should be a last resort. | |||
Once you understand these guidelines, you should better understand the SMF code. | |||
==Preface== | ==Preface== | ||
Line 16: | Line 19: | ||
Each group of actions should have their own controller file. For instance, admin actions use Admin.php. Supporting functions and/or functions that are used in multiple controllers should be placed in a model file. In the case of Admin.php we use Subs-Admin.php. The last piece of the puzzle is to create a view file – Admin.template.php. | Each group of actions should have their own controller file. For instance, admin actions use Admin.php. Supporting functions and/or functions that are used in multiple controllers should be placed in a model file. In the case of Admin.php we use Subs-Admin.php. The last piece of the puzzle is to create a view file – Admin.template.php. | ||
The view is divided into two parts – structure (PHP & HTML) and presentation (CSS). Although, not all templates have their own CSS file – that would be overkill. Structure is then split into two parts – template functions and a separate language file. If you're looking for the various files, you can find controller and model files in the 'Sources' directory, the view files in the 'Themes' directory, and the language files in the [[default theme]] directory in a sub-directory called 'languages.' The actual locations of these directories may vary across SMF installations, but they default to the forum's root directory. | The view is divided into two parts – structure (PHP & HTML) and presentation (CSS). Although, not all templates have their own CSS file – that would be overkill. Structure is then split into two parts – template functions and a separate language file. If you're looking for the various files, you can find controller and model files in the 'Sources' directory, the view files in the 'Themes' directory, and the language files in the [[SMF default theme]] directory in a sub-directory called 'languages.' The actual locations of these directories may vary across SMF installations, but they default to the forum's root directory. | ||
==Naming Conventions== | ==Naming Conventions== | ||
Line 45: | Line 48: | ||
====CSS Examples==== | ====CSS Examples==== | ||
{{code|1=<nowiki> | |||
/* WRONG! This class name tells where the element goes. */ | /* WRONG! This class name tells where the element goes. */ | ||
.rightmenu | .rightmenu | ||
Line 53: | Line 57: | ||
.profilemenu | .profilemenu | ||
{ | { | ||
} | }</nowiki>}} | ||
==Database== | ==Database== | ||
Line 71: | Line 75: | ||
* Use <code>LIMIT</code> whenever the <code>WHERE</code> clause contains a column that does not have a <code>UNIQUE</code> index – to include primary keys. | * Use <code>LIMIT</code> whenever the <code>WHERE</code> clause contains a column that does not have a <code>UNIQUE</code> index – to include primary keys. | ||
* <code>SELECT *</code> should not be used. Queries should only select columns that are needed. | * <code>SELECT *</code> should not be used. Queries should only select columns that are needed. | ||
* | * Do not use shorthand <code>INNER JOIN</code>, as it may not be supported in all database systems. | ||
* All commands (like SELECT, INSERT, REPLACE, UPDATE, DELETE, etc.) and keywords (WHERE, AND, OR, LIMIT, FROM, JOIN, AS, ON, etc.) should be capitalized. | |||
* When using more than one table in a query, use an alias for the table name. | * When using more than one table in a query, use an alias for the table name. | ||
* Always use <code>{db_prefix}</code> before the table name. | * Always use <code>{db_prefix}</code> before the table name. | ||
Line 83: | Line 87: | ||
Whitespace is important in making code easier to read and follow. Whitespace should be used in the following situations. | Whitespace is important in making code easier to read and follow. Whitespace should be used in the following situations. | ||
* Use one LF after <code>$smcFunc['db_query']('</code>. | * Use one LF after <code><nowiki>$smcFunc['db_query']('', '</nowiki></code>. | ||
* Indent once past the base of the query. | * Indent once past the base of the query. | ||
* Use one space after each keyword, column name, and table name. | * Use one space after each keyword, column name, and table name. | ||
Line 92: | Line 96: | ||
====Query Example==== | ====Query Example==== | ||
{{code|1=<nowiki> // Get their name and part of their address (excuse the wrapping) | |||
$result = $smcFunc['db_query'](' | $result = $smcFunc['db_query']('' ,' | ||
SELECT ppl.first_name, ppl.last_name, add.city, add.address | SELECT ppl.first_name, ppl.last_name, add.city, add.address | ||
FROM people as ppl | FROM {db_prefix}people as ppl | ||
LEFT JOIN {db_prefix}addresses as add ON (add.id_address = ppl.id_address) | |||
WHERE ppl.id_person = {int:person} | WHERE ppl.id_person = {int:person} | ||
AND (ppl.middle_name = 'foo' OR ppl.suffix != 'jr') | |||
AND {bool:condition}', | |||
array( | array( | ||
'person' => $id_person, | 'person' => $id_person, | ||
'condition' => $condition | 'condition' => $condition, | ||
) | ) | ||
); | );</nowiki>}} | ||
===Schema=== | ===Schema=== | ||
* All tables should use the <code> | * All tables should use the <code>{db_prefix}</code>. | ||
* Tables should work with HEAP or MyISAM. Do not expect a user to have InnoDB or any other engine enabled. | * Tables should work with HEAP or MyISAM. Do not expect a user to have InnoDB or any other engine enabled. | ||
* If a column has no reason to ever be <code>NULL</code>, specify <code>NOT NULL</code>. This saves space in the table data. | * If a column has no reason to ever be <code>NULL</code>, specify <code>NOT NULL</code>. This saves space in the table data. | ||
Line 117: | Line 121: | ||
====Schema Example==== | ====Schema Example==== | ||
{{code | |||
|1=<nowiki> CREATE TABLE {db_prefix}mountains | |||
( | ( | ||
id_mountain smallint(5) unsigned NOT NULL auto_increment, | id_mountain smallint(5) unsigned NOT NULL auto_increment, | ||
Line 125: | Line 130: | ||
KEY found(date_found) | KEY found(date_found) | ||
) TYPE = MyISAM; | ) TYPE = MyISAM; | ||
</nowiki>}} | |||
==PHP== | ==PHP== | ||
Line 133: | Line 139: | ||
* No whitespace at the end of lines. | * No whitespace at the end of lines. | ||
* Use tabs, not spaces, for proper indentation. | * Use tabs, not spaces, for proper indentation. | ||
* There is no need | * There is no need to use braces (curly brackets) for one line of code after a control structure. | ||
* Use braces (curly brackets) for switch ... case and every other time there are multiple statements. | * Use braces (curly brackets) for switch ... case and every other time there are multiple statements. | ||
* Do not use a space | * Do not use a space between function names and <code>();</code>. So, use <code>fooBar();</code> and not <code>fooBar ();</code>. | ||
* | * Spaces should be before and after an equal sign. For example <code>$var = 'foo';</code>. | ||
* When many common assignments are being done, it is common to use more space so that all of the assignments align with each other. This is acceptable. | * When many common assignments are being done, it is common to use more space so that all of the assignments align with each other. This is acceptable. | ||
* There should be no | * There should be no closing <code>?></code> at the end of the files. | ||
* Each statement should be on its own line. | * Each statement should be on its own line. | ||
* Use one space | * Use one space between control structures and parenthesis such as <code>if ()</code>, <code>elseif ()</code>, <code>for ()</code>, <code>while ()</code>, etc. | ||
* Use one space after language constructs such as echo, list, require, etc. | * Use one space after language constructs such as <code>echo</code>, <code>list</code>, <code>require</code>, etc. | ||
* Do not indent statements in the global scope (not inside of a class, function, statement, etc.). | * Do not indent statements in the global scope (not inside of a class, function, statement, etc.). | ||
* Every argument, except the last, in an argument list should have a space after the comma. | * Every argument, except the last, in an argument list should have a space after the comma. | ||
* | * Use one space before and after the <code>.</code> when concatenating. | ||
===Functions & Language Constructs=== | ===Functions & Language Constructs=== | ||
* Arguments with default values go at the end of the argument list in the function definition. For example, function foo ($bar, $var = true, $arg = 'foobar');. | * Arguments with default values go at the end of the argument list in the function definition. For example, <code>function foo ($bar, $var = true, $arg = 'foobar');</code>. | ||
* Wrapper functions (functions that do nothing more than call another function) should not be used. | * Wrapper functions (functions that do nothing more than call another function) should not be used. | ||
* Use isset() instead of | * Use <code>isset()</code> instead of <code>array_key_exists()</code>. | ||
* Use empty() instead of isset() && $var != '' && $var != 0 && $var != false. | * Use <code>empty()</code> instead of <code>isset() && $var != <nowiki>''</nowiki> && $var != 0 && $var != false</code>. | ||
* Use include_once() instead of include() and require_once() instead of require(). Code that | * Use <code>include_once()</code> instead of <code>include()</code> and <code>require_once()</code> instead of <code>require()</code>. Code that needs to run more than once should be inside of a function. | ||
===Variables=== | ===Variables=== | ||
* Initialize all variables. | * Initialize all variables. | ||
* Although there may be the rare case where a constant is desired, they should be used sparingly. | * Although there may be the rare case where a constant is desired, they should be used sparingly. Variables intended for the global scope should be declared with <code>global</code>. When constants are necessary, they should be uppercase. | ||
* There are some special variables | * There are some special variables such as <code>$modSettings</code>, <code>$context</code>, <code>$sourcedir</code>, etc. These variables should be used before creating new ones. | ||
* $_GET and $_POST are available via $_REQUEST. It | * <code>$_GET</code> and <code>$_POST</code> are available via <code>$_REQUEST</code>. It does not make a difference in terms of security to know where the data is coming from since all user data is dirty (see [[#Security|Security]]). | ||
** All $_REQUEST variables have been escaped. | ** All <code>$_REQUEST</code> variables have been escaped. They should be unescaped in order to be used in normal content. | ||
** All $_GET variables have been modified with htmlspecialchars__recursive(). | ** All <code>$_GET</code> variables have been modified with <code>htmlspecialchars__recursive()</code>. Undoing the special characters is necessary to use them in plain text. | ||
* It is usually best to use an associative array instead of numeric. | * It is usually best to use an associative array instead of numeric. | ||
* Avoid internal type changing/conflicts. PHP won't mind, but it will become confusing | * Avoid internal type changing/conflicts. PHP won't mind, but it will become confusing to keep track of that variable. For example, do not do <code>$var = 'foo'; $var = $var == 'foo' ? true : false;</code>. | ||
===Error Handling=== | ===Error Handling=== | ||
* All code should be E-STRICT compatible. | * All code should be E-STRICT compatible. | ||
* | * <code>@</code> should almost never be used to silence an error. If an error is expected, it should be checked for first. For instance, when including a file, check to see if it exists if it is possible it does not. Otherwise, use <code>require_once()</code>. | ||
* Simple Machines has its own error handling functions | * Simple Machines Forum has its own error handling functions. They should be used to return an error. They can be viewed in Errors.php. | ||
===Comments=== | ===Comments=== | ||
* Comment often! | * Comment often! | ||
* Use [http://en.wikipedia.org/wiki/PHPDoc PHPDoc] format. | |||
* Use comments to describe a block of code or as a placeholder for future code/mods. | * Use comments to describe a block of code or as a placeholder for future code/mods. | ||
* Comments should be unique per the file and should try to be unique per the entire application. | * Comments should be unique per the file and should try to be unique per the entire application. | ||
* Comments should be kept under 80 characters per line. | * Comments should be kept under 80 characters per line. | ||
* One line comments should | * One line comments should formatted with <code>//</code>, a space, the comment, and then a period. | ||
* Multiple line comments should use /* */ | * Multiple line comments should use <code>/* */</code> with each one on its own line. | ||
====Comment Examples==== | ====Comment Examples==== | ||
/ | {{code|1=<nowiki> /** | ||
* This comment will become part of the documentation of the do_something function | |||
*/ | |||
function do_something() | function do_something() | ||
{ | { | ||
// Here goes a one-liner... so a dog and a man walk in to a bar. | |||
foobar(); | foobar(); | ||
} | } | ||
/* This is my comment. | /* | ||
This is my comment. | |||
I want to comment some more. | I want to comment some more. | ||
Keep on commenting. | Keep on commenting. | ||
*/ | */ | ||
echo 'I love Rock & | echo 'I love Rock & Roll, put another dime in ©';</nowiki>}} | ||
===Miscellaneous Guidelines=== | ===Miscellaneous Guidelines=== | ||
* Always use <?php | * Always use <code><?php</code>. Never use short tags! | ||
* SMF uses procedural programming for most things. | * SMF uses procedural programming for most things. Object-oriented programming (OOP) should not be used unless creating an entire class. Future versions of this document will include guidelines for writing OOP PHP code. | ||
* Use single quotes. | * Use single quotes. Double quotes may be used to concatenate a variable, but should only be used in smaller strings. | ||
* Free as many resources as possible. | * Free as many resources as possible. | ||
* Do not use deprecated features | * Do not use deprecated features or features that do not exist in the minimum version. | ||
===Proper PHP Example=== | ===Proper PHP Example=== | ||
{{code|1=<nowiki><?php | |||
// | /** | ||
* This file contains some functions | |||
* This comment will become part of the documentation | |||
* | |||
* @copyright: 2007 Author <[email protected]> | |||
*/ | |||
foo_bar('x', 'woot'); | foo_bar('x', 'woot'); | ||
Line 208: | Line 223: | ||
} | } | ||
/ | /** | ||
* Declare a function. | |||
*/ | |||
function foo_bar ($arg, $arg1, $arg2 = false) | function foo_bar ($arg, $arg1, $arg2 = false) | ||
{ | { | ||
Line 232: | Line 249: | ||
$yawie = 12; | $yawie = 12; | ||
} | } | ||
?> | ?></nowiki>}} | ||
==Output== | ==Output== | ||
Simple Machines has a lot of goals for output, including [http://en.wikipedia.org/wiki/Semantic_HTML semantic markup], [http://en.wikipedia.org/wiki/Accessibility accessible] output, usable output, valid [http:// | Simple Machines Forum has a lot of goals for output, including [http://en.wikipedia.org/wiki/Semantic_HTML semantic markup], [http://en.wikipedia.org/wiki/Accessibility accessible] output, usable output, valid [http://en.wikipedia.org/wiki/HTML5 HTML 5], and valid [http://jigsaw.w3.org/css-validator CSS3]. This can be a bit confusing. To clarify, semantic markup means that the tags are used in a manner that make sense to humans and machines (like search engines). Use heading tags for headings, div tags to divide sections, p tags for paragraphs, etc. Markup must also validate in the document type. The CSS must also be valid. There are a lot of browsers out there that accept mistakes/kludges in markup. Simple Machines Forum does not. The accessible part is a little less simple. It means that markup should be all of the above, plus it should have the ability to change easily for people with physical impairments. Font size, screen size, etc. should be considered. | ||
'''All output should be contained inside a template!''' | |||
===echo=== | ===echo=== | ||
Simple Machines uses one thing and one thing only to output to the browser. | Simple Machines Forum uses one thing and one thing only to output to the browser – <code>echo</code>. <code>print()</code>, <code>heredoc</code>, and <code>nowdoc</code> should not be used. <code>echo</code> is not a function; it is a language construct. So, it does not require parenthesis around its input. Also, it accepts an unlimited number of arguments. | ||
* Send multiple arguments with the use of commas instead of concatenation (using periods). | * Send multiple arguments with the use of commas instead of concatenation (using periods). | ||
* Place a space after the comma, not before. | * Place a space after the comma, not before. | ||
* Put line breaks inside of the quotes. There is no need to use | * Put line breaks inside of the quotes. There is no need to use <code>"\n"</code>. | ||
* PHP whitespace and markup whitespace do not relate. When | * PHP whitespace and markup whitespace do not relate. When output should be indented, tabs should be added. <code>echo</code> statements should still line up with the function and other statements that they are in as per the PHP guidelines. | ||
====Proper Echo Example==== | ====Proper Echo Example==== | ||
{{code|1=<nowiki> // Using a long string with no variables. | |||
echo 'This is our very long string. We don\'t need any variables'; | echo 'This is our very long string. We don\'t need any variables'; | ||
Line 263: | Line 280: | ||
{ | { | ||
echo ' | echo ' | ||
<div> | |||
w00t!'; | w00t!'; | ||
if ($woot % 2 == 0) | if ($woot % 2 == 0) | ||
echo ' | echo ' | ||
<br /> | |||
woot w00t woot'; | woot w00t woot'; | ||
echo ' | echo ' | ||
</div>'; | |||
} | }</nowiki>}} | ||
====Improper Echo Example==== | ====Improper Echo Example==== | ||
{{code|1=<nowiki> // DO NOT DO IT LIKE THIS! | |||
echo "$var"; | echo "$var"; | ||
echo "output"; | echo "output"; | ||
Line 285: | Line 302: | ||
echo 'See what I mean by line breaks?' . "\n", | echo 'See what I mean by line breaks?' . "\n", | ||
'Look at how bad this looks' . "\n" . | 'Look at how bad this looks' . "\n" . | ||
'compared to just putting the line breaks in quotes.'; | 'compared to just putting the line breaks in quotes.';</nowiki>}} | ||
===Usable & Accessible=== | ===Usable & Accessible=== | ||
These are two distinctly different and important terms that are rarely given too much thought. | These are two distinctly different and important terms that are rarely given too much thought. SMF has many types of users, and Simple Machines is devoted to making it possible for all users to access a page. Thus, differences between users must be considered. Making a page accessible does not mean that it shouldn't look good. Keeping these thoughts in mind should help achieve a usable and accessible page. | ||
* A page should look and sound as good as possible. Remember, there are users out there that use screen readers. | * A page should look and sound as good as possible. Remember, there are users out there that use screen readers. | ||
* | * Some users may have a 21" monitor with a high resolution. Other users may be reading on a mobile device with a 2" screen and no graphics. | ||
* A lot more users than | * A lot more users than expected disable JavaScript. Think about how many people might do so to avoid popups or ads. | ||
===Graceful Degradation=== | ===Graceful Degradation=== | ||
* All browsers should be able to use a page regardless of their JavaScript settings, screen size, operating system, browser version, etc. | * All browsers should be able to use a page regardless of their JavaScript settings, screen size, operating system, browser version, etc. | ||
* What can | * What can not be made to look pretty with JavaScript or CSS due to the browser should still work and look as good as possible. | ||
===HTML=== | ===HTML=== | ||
* HTML is used to define the structure of a document, not the presentation (styling). | * HTML is used to define the structure of a document, not the presentation (styling). | ||
* Use HTML | * Use HTML 5. | ||
* Usually, a list should be used for menus. | * Usually, a list should be used for menus. | ||
* Use | * Use <nowiki><p></nowiki> for paragraphs. Use <nowiki><br /></nowiki> only for a single line break. Additional necessary space should be created through extra CSS margins. | ||
* Do not use | * Do not use <code><a name=”anchor”></code> for anchors. Use <nowiki><{tag} id=”anchor”></nowiki>, where {tag} is usually a header tag, since all ids are unique they act as anchors. | ||
* Although | * Although <nowiki><b></nowiki>, <nowiki><i></nowiki>, <nowiki><big></nowiki>, <nowiki><small></nowiki>, <nowiki><tt></nowiki> are not deprecated, they are strictly for presentation. They serve no purpose other than to make output look different. To show emphasis on text, consider using <nowiki><strong></nowiki> or <nowiki><em></nowiki>. To create a heading, consider using <nowiki><h#></nowiki>. | ||
* Here are some commonly used deprecated tags that should not be used: | * Here are some commonly used deprecated tags that should not be used: | ||
** | ** <nowiki><s></nowiki> is often used to signify deleted text. Instead, consider using <nowiki><del></nowiki> and <nowiki><ins></nowiki>. If the strike through is only necessary for presentation, considering using the CSS text decoration <code>"text-decoration: line-through;"</code>. | ||
** Do not use | ** Do not use <nowiki><pre></nowiki> for code snippets. There are the <nowiki><code></nowiki> and <nowiki><sample></nowiki> tags for that. | ||
** Perhaps the most commonly used deprecated tag is | ** Perhaps the most commonly used deprecated tag is <nowiki><font></nowiki>. This tag has no semantic value. Use <nowiki><span></nowiki> instead. | ||
** Internet Explorer does not support | ** Internet Explorer does not support <nowiki><q></nowiki> or <nowiki><button></nowiki>. Do not use them. | ||
* Tables are for tabular data! Think of a table as a spreadsheet. Would | * Tables are for tabular data! Think of a table as a spreadsheet. Would the data work well in a spreadsheet? Do not use a table simply to make layout easier. | ||
* Use | * Use <nowiki><th></nowiki> and <nowiki><caption></nowiki> to define the data in the table. | ||
* Forms are a block level tag. They do not require a wrapping div. | * Forms are a block level tag. They do not require a wrapping div. | ||
* Related fields should be grouped with a | * Related fields should be grouped with a <nowiki><fieldset></nowiki> tag. All fieldsets should have a legend | ||
* All form fields should have a label using the | * All form fields should have a label using the <nowiki><label></nowiki> tag. | ||
* Related options in a select box should be grouped using the | * Related options in a select box should be grouped using the <nowiki><optgroup></nowiki> tag | ||
* When creating a list of form controls, it is usually best to use a definition list | * When creating a list of form controls, it is usually best to use a definition list <nowiki><dl></nowiki>. Define the label with <nowiki><dt></nowiki> and the control with <nowiki><dd></nowiki>. | ||
* Never use a reset button. One mistaken click will cause the entire form to disappear! | * Never use a reset button. One mistaken click will cause the entire form to disappear! | ||
===CSS=== | ===CSS=== | ||
CSS is used to define the presentation of a document, not the structure. | CSS is used to define the presentation of a document, not the structure. Do not be afraid of CSS. It can make changing a lot of elements of a page a lot faster. It can also decrease the size of your output. It can also be used to change styles based on the user's interface. | ||
* Use the BSD/Allman indentation style. | * Use the BSD/Allman indentation style. | ||
* Try to avoid inline styles and useless class names, such as | * Try to avoid inline styles and useless class names, such as <code>bigred</code>. Another user may decide that the text needs to be normal sized but bold and blue. | ||
* Use CSS shorthand as often as possible. Here is an [http://www.456bereastreet.com/archive/200502/efficient_css_with_shorthand_properties excellent article] on this subject from http://456BereaStreet.com. | * Use CSS shorthand as often as possible. Here is an [http://www.456bereastreet.com/archive/200502/efficient_css_with_shorthand_properties excellent article] on this subject from http://456BereaStreet.com. | ||
* Do not do browser sniffing. The proper alternative is to use feature sniffing. | * Do not do browser sniffing. The proper alternative is to use feature sniffing. | ||
* If a value is 0, do not use a value type (px, em, etc). | * If a value is 0, do not use a value type (px, em, etc). | ||
* IE conditional comments may be necessary, but use them sparingly. | |||
===JavaScript=== | ===JavaScript=== | ||
* Simple Machines JavaScript guidelines use the same indentation and whitespace guidelines as PHP. | * Simple Machines Forum JavaScript guidelines use the same indentation and whitespace guidelines as PHP. | ||
* Never alter the prototype of Object. | * Never alter the prototype of <code>Object</code>. | ||
* Avoid global objects. Use the var keyword in all functions. Put stuff in the smf namespace/object. Large mods can have their own namespace if they need to. | * Avoid global objects. Use the <code>var</code> keyword in all functions. Put stuff in the <code>smf</code> namespace/object. Large mods can have their own namespace if they need to. | ||
* Avoid eval(), including hidden ones. eval() is allowed in very specific situations, such as parsing JSON, but it shouldn't be used anywhere else. Hidden eval()s are something like onclick = "function();". Instead, just use onclick = function; | * Avoid <code>eval()</code>, including hidden ones. <code>eval()</code> is allowed in very specific situations, such as parsing JSON, but it shouldn't be used anywhere else. Hidden <code>eval()</code>s are something like <code>onclick = "function();"</code>. Instead, just use <code>onclick = function;</code>. | ||
* | * Do not browser detect. Instead, use feature detection. if possible, do feature detection outside of a function. | ||
* | * Do not do calculations in the 2nd part of a <code>for</code> loop. | ||
* Avoid the this keyword, because what it refers to can be unpredictable. Use it only in functions that | * Avoid the <code>this</code> keyword, because what it refers to can be unpredictable. Use it only in functions that will be run only by an event, when the alternative is messing around with the <code>Event</code> object. | ||
* Use regular expressions – they are faster than most JavaScript string testing functions. | |||
* Use | |||
* Use object literal notations. It's quick and simple. | * Use object literal notations. It's quick and simple. | ||
==Security== | ==Security== | ||
Simple Machines prides itself on its security history | Simple Machines prides itself on its security history and would like to keep it that way. Here are several guidelines to help keep security tight. | ||
===Are you my SMF?=== | ===Are you my SMF?=== | ||
The following is a simple, effective solution to make sure that a file is being used only on the intended system and only by SMF. It should appear at the very beginning of a file just after the copyright/license block. The output of die() may be changed, but it isn't recommended. | |||
====SMF Check Example==== | ====SMF Check Example==== | ||
if (!defined('SMF')) | if (!defined('SMF')) | ||
die('Hacking attempt...'); | |||
===Wash those variables!=== | ===Wash those variables!=== | ||
There's no telling where they've been! Everything that comes from a user can be a potential security risk. After all, there is no way to tell if that user is a sweet old Granny or some crazed hacker bent on destroying the web site. All integers should be defined as integers before they are used. That includes array keys and values. Does that mean that every <code>$_REQUEST</code> variable has to be cast to an integer? No. That means that <code>$_REQUEST</code> variables that should be an integer must be cast to an integer. | |||
====Casting Example==== | ====Casting Example==== | ||
{{code|1=<nowiki> $_GET['foo'] = (int) $_GET['foo']; | |||
$bar = array ( | $bar = array ( | ||
'my_fav' => (int) $_POST['bar]['my_fav'], | 'my_fav' => (int) $_POST['bar]['my_fav'], | ||
Line 365: | Line 377: | ||
// (int) $var and settype($var, 'int') do the same thing. | // (int) $var and settype($var, 'int') do the same thing. | ||
$id_groundup = (int) $id_groundup; | $id_groundup = (int) $id_groundup; | ||
$id_yo_momma = settype($id_yo_momma, 'int'); | $id_yo_momma = settype($id_yo_momma, 'int');</nowiki>}} | ||
All variables being passed to the database must be escaped! This is a favorite exploit among hackers — SQL injection. Does that mean that | All variables being passed to the database must be escaped! This is a favorite exploit among hackers — SQL injection. Does that mean that every <code>$_REQUEST</code> variable should be escaped? Yes, but there is a solution for that. In QueryString.php, there is a function called <code>cleanRequest()</code>. It escapes all <code>$_REQUEST</code> variables at once. That means that <code>$_REQUEST</code> variables that are used in output need to be un-escaped with <code>stripslashes()</code>. | ||
===The Permission System=== | ===The Permission System=== | ||
Membergroups should not be hardcoded in order to give them special access. Use <code>allowedTo()</code> and <code>isAllowedTo()</code>, where <code>allowedTo()</code> is a basic check and <code>isAllowedTo()</code> is a check/error combination. Use <code>boardsAllowedTo()</code> to get a list of boards a user has a permission to. | |||
===Session checks=== | ===Session checks=== | ||
Before | Before using input passed through a form from a user, their session should always be checked. Use <code>validateSession()</code> before actions in the administration center to ensure the user is who they say they are. Note that <code>adminIndex()</code> will do this. | ||
[[Category:Developing SMF]] | {{Developing SMF}} | ||
<noinclude>[[Category:Customizing SMF]] | |||
[[Category:Developing SMF]]</noinclude> |
Latest revision as of 08:24, 4 September 2015
This page describes the coding guidelines for the SMF "core" code, if you are writing a mod you may be looking for the Customization approval guidelines |
Simple Machines prides itself on code that is uniform and easy to read. To preserve these qualities, and to maintain performance and reduce the risk of introducing certain types of bugs, the SMF Team maintains coding guidelines to be used when writing customizations (mods/themes) as well as tools and core SMF code. Although it is not always possible to follow all guidelines exactly, breaking them should be a last resort.
Once you understand these guidelines, you should better understand the SMF code.
Preface
This document is meant for semi-experienced users. As such, it does not define many basic terms such as CSS, HTML, PHP, and MySQL. It is better to familiarizing oneself with those topics before continuing with these guidelines. The term ‘foobar’ is used throughout this document as a placeholder for real variable and function names.
Please see the SMF Coding Discussion board in the SMF Community Forums for answers to further questions. The function database contains additional information about the files, functions, and variables described in this document.
This document is free for distribution. Please translate this to as many languages as possible.
Model View Controller (MVC)
SMF is based on a Model-View-Controller (MVC) architecture which separates content and presentation. The software uses the action present in the URL and the file associated with that action to function as the controller. Various loading and processing functions serve as the models, and the many theme and template functions make up the views.
Each group of actions should have their own controller file. For instance, admin actions use Admin.php. Supporting functions and/or functions that are used in multiple controllers should be placed in a model file. In the case of Admin.php we use Subs-Admin.php. The last piece of the puzzle is to create a view file – Admin.template.php.
The view is divided into two parts – structure (PHP & HTML) and presentation (CSS). Although, not all templates have their own CSS file – that would be overkill. Structure is then split into two parts – template functions and a separate language file. If you're looking for the various files, you can find controller and model files in the 'Sources' directory, the view files in the 'Themes' directory, and the language files in the SMF default theme directory in a sub-directory called 'languages.' The actual locations of these directories may vary across SMF installations, but they default to the forum's root directory.
Naming Conventions
Code for any Simple Machines product should use the following naming conventions.
Descriptive Naming
- The name of the variable or function should describe what it is or does. For instance, use $message_settings instead of $mset.
- The name of the variable or function should signify what the object is being used for, not where it is to be used.
- English should be used as the default language when choosing a name for variables or functions.
Camel Case and Underscores
- Underscores ($foo_bar) or camelCase ($fooBar) should be used.
- Do not use camel case when naming tables, columns, or indexes in databases. They do not work in all database systems.
Database
- When a column is an ID, it should prefixed with ‘id_’ as in ‘id_member’.
- Tables with the purpose of logging should be prefixed with ‘log_’ as in ‘log_errors’.
Files
- When displaying an index of information to a user such as BoardIndex.php and MessageIndex.php, the file name should end with 'Index'.
- A file that is used for the management of a feature like members should be prefixed with ‘Manage’. For instance, SMF's file is named ManageMembers.php.
- Files that are not the controller file for an action, but instead contain many functions that can be used in several files, should be prefixed with 'Subs-'. For instance, SMF's posting functions are placed in a file called Subs-Post.php.
- Classes should have their own file, prefixed with 'Class-'.
Cascading Style Sheets (CSS)
- ID and class names should be lower case.
- ID and class names should always describe what they do, not where they are to be used.
CSS Examples
/* WRONG! This class name tells where the element goes. */ .rightmenu { } /* CORRECT! This class name tells what the element is or does. */ .profilemenu { }
Database
While SMF 1.1.x only supports MySQL, SMF 2.0 supports multiple databases. Following these guidelines is extremely important to make sure that remains so.
Functions
- Do not use database-specific functions to send a query to the database (i.e. -
mysql_query()
for MySQL). Use$smcFunc['db_query']()
. - Do not use database-specific functions to handle query results (i.e. -
mysql_fetch_array()
ormysql_fetch_assoc()
in MySQL). Most of the time, the associative array returned by$smcFunc['db_fetch_assoc']()
should be used. In rare cases like when usinglist()
,$smcFunc['db_fetch_row']()
may be used. - Always use
$smcFunc['db_free_result']()
to free the resource when they are no longer needed.
Queries
Queries have the ability to make a forum run fast or to cripple it. It all depends on how the query is designed. Queries should be optimized as much as possible.
- Queries should never be in the template files.
- Most of the time it is best to use joins instead of using multiple queries or subselects. Sometimes this is not the case. Simple benchmarking will reveal the differences.
- Do not use
UNION
,SET PASSWORD
,BENCHMARK
, subselects, or comments in a query. - Use
LIMIT
whenever theWHERE
clause contains a column that does not have aUNIQUE
index – to include primary keys. SELECT *
should not be used. Queries should only select columns that are needed.- Do not use shorthand
INNER JOIN
, as it may not be supported in all database systems. - All commands (like SELECT, INSERT, REPLACE, UPDATE, DELETE, etc.) and keywords (WHERE, AND, OR, LIMIT, FROM, JOIN, AS, ON, etc.) should be capitalized.
- When using more than one table in a query, use an alias for the table name.
- Always use
{db_prefix}
before the table name. - Never use reserved words.
- Do not add the database name to tables.
- All comments should be before the call to
$smcFunc['db_query']()
. There should be no comments inside of queries. - Do not rely on the default value. When performing an
INSERT
, columns for which there is no value should be excluded.
Whitespace
Whitespace is important in making code easier to read and follow. Whitespace should be used in the following situations.
- Use one LF after
$smcFunc['db_query']('', '
. - Indent once past the base of the query.
- Use one space after each keyword, column name, and table name.
- Do not use spaces when using a function such as
COUNT(column)
. - Each of the following keywords should be on their own line:
SELECT
,DELETE
,UPDATE
,FROM
,JOIN
,LEFT JOIN
,WHERE
,AND
,OR
,GROUP BY
,ORDER BY
,LIMIT
, etc.
Exception: when AND
& OR
are used in a sub-condition, they do not need to be on their own line.
Query Example
// Get their name and part of their address (excuse the wrapping) $result = $smcFunc['db_query']('' ,' SELECT ppl.first_name, ppl.last_name, add.city, add.address FROM {db_prefix}people as ppl LEFT JOIN {db_prefix}addresses as add ON (add.id_address = ppl.id_address) WHERE ppl.id_person = {int:person} AND (ppl.middle_name = 'foo' OR ppl.suffix != 'jr') AND {bool:condition}', array( 'person' => $id_person, 'condition' => $condition, ) );
Schema
- All tables should use the
{db_prefix}
. - Tables should work with HEAP or MyISAM. Do not expect a user to have InnoDB or any other engine enabled.
- If a column has no reason to ever be
NULL
, specifyNOT NULL
. This saves space in the table data. - Use default values whenever possible. Always assume that those values may be used at some point.
- Use the smallest data type possible, but remember that it needs to scale. Come up with reasonable maximums for IDs, and that will be the maximum size of your column. Use a corresponding column type.
- Remember that using an
UNSIGNED
number will nearly double the amount of rows allowed. UseUNSIGNED
when are you defining an ID. - Primary keys are strongly recommended.
Schema Example
CREATE TABLE {db_prefix}mountains ( id_mountain smallint(5) unsigned NOT NULL auto_increment, name varchar(25) NOT NULL default '', date_found int(10) unsigned NOT NULL default '0', PRIMARY KEY (id_mountain), KEY found(date_found) ) TYPE = MyISAM;
PHP
Whitespace
- Use the BSD/Allman indentation style.
- Use Unix line feeds (LF) instead of carriage returns like the old Mac® style (CR) and not a combo like Windows® (CR+LF). That does not mean use Unix indentation style.
- No whitespace at the end of lines.
- Use tabs, not spaces, for proper indentation.
- There is no need to use braces (curly brackets) for one line of code after a control structure.
- Use braces (curly brackets) for switch ... case and every other time there are multiple statements.
- Do not use a space between function names and
();
. So, usefooBar();
and notfooBar ();
. - Spaces should be before and after an equal sign. For example
$var = 'foo';
. - When many common assignments are being done, it is common to use more space so that all of the assignments align with each other. This is acceptable.
- There should be no closing
?>
at the end of the files. - Each statement should be on its own line.
- Use one space between control structures and parenthesis such as
if ()
,elseif ()
,for ()
,while ()
, etc. - Use one space after language constructs such as
echo
,list
,require
, etc. - Do not indent statements in the global scope (not inside of a class, function, statement, etc.).
- Every argument, except the last, in an argument list should have a space after the comma.
- Use one space before and after the
.
when concatenating.
Functions & Language Constructs
- Arguments with default values go at the end of the argument list in the function definition. For example,
function foo ($bar, $var = true, $arg = 'foobar');
. - Wrapper functions (functions that do nothing more than call another function) should not be used.
- Use
isset()
instead ofarray_key_exists()
. - Use
empty()
instead ofisset() && $var != '' && $var != 0 && $var != false
. - Use
include_once()
instead ofinclude()
andrequire_once()
instead ofrequire()
. Code that needs to run more than once should be inside of a function.
Variables
- Initialize all variables.
- Although there may be the rare case where a constant is desired, they should be used sparingly. Variables intended for the global scope should be declared with
global
. When constants are necessary, they should be uppercase. - There are some special variables such as
$modSettings
,$context
,$sourcedir
, etc. These variables should be used before creating new ones. $_GET
and$_POST
are available via$_REQUEST
. It does not make a difference in terms of security to know where the data is coming from since all user data is dirty (see Security).- All
$_REQUEST
variables have been escaped. They should be unescaped in order to be used in normal content. - All
$_GET
variables have been modified withhtmlspecialchars__recursive()
. Undoing the special characters is necessary to use them in plain text.
- All
- It is usually best to use an associative array instead of numeric.
- Avoid internal type changing/conflicts. PHP won't mind, but it will become confusing to keep track of that variable. For example, do not do
$var = 'foo'; $var = $var == 'foo' ? true : false;
.
Error Handling
- All code should be E-STRICT compatible.
@
should almost never be used to silence an error. If an error is expected, it should be checked for first. For instance, when including a file, check to see if it exists if it is possible it does not. Otherwise, userequire_once()
.- Simple Machines Forum has its own error handling functions. They should be used to return an error. They can be viewed in Errors.php.
Comments
- Comment often!
- Use PHPDoc format.
- Use comments to describe a block of code or as a placeholder for future code/mods.
- Comments should be unique per the file and should try to be unique per the entire application.
- Comments should be kept under 80 characters per line.
- One line comments should formatted with
//
, a space, the comment, and then a period. - Multiple line comments should use
/* */
with each one on its own line.
Comment Examples
/** * This comment will become part of the documentation of the do_something function */ function do_something() { // Here goes a one-liner... so a dog and a man walk in to a bar. foobar(); } /* This is my comment. I want to comment some more. Keep on commenting. */ echo 'I love Rock & Roll, put another dime in ©';
Miscellaneous Guidelines
- Always use
<?php
. Never use short tags! - SMF uses procedural programming for most things. Object-oriented programming (OOP) should not be used unless creating an entire class. Future versions of this document will include guidelines for writing OOP PHP code.
- Use single quotes. Double quotes may be used to concatenate a variable, but should only be used in smaller strings.
- Free as many resources as possible.
- Do not use deprecated features or features that do not exist in the minimum version.
Proper PHP Example
<?php /** * This file contains some functions * This comment will become part of the documentation * * @copyright: 2007 Author <[email protected]> */ foo_bar('x', 'woot'); if (empty($_REQUEST['bla'])) { require_once('some_file.php'); have_fun(); } /** * Declare a function. */ function foo_bar ($arg, $arg1, $arg2 = false) { global $modSettings, $context; static $counter; if ($arg2) ex_of_calling(); elseif ($arg) $foo = array(); foreach ($arg as $key => $value) $arg2 = $key; while ($arg1) { do_something(); fooBar(); } $abc = 123; $woot = 098; $yawie = 12; } ?>
Output
Simple Machines Forum has a lot of goals for output, including semantic markup, accessible output, usable output, valid HTML 5, and valid CSS3. This can be a bit confusing. To clarify, semantic markup means that the tags are used in a manner that make sense to humans and machines (like search engines). Use heading tags for headings, div tags to divide sections, p tags for paragraphs, etc. Markup must also validate in the document type. The CSS must also be valid. There are a lot of browsers out there that accept mistakes/kludges in markup. Simple Machines Forum does not. The accessible part is a little less simple. It means that markup should be all of the above, plus it should have the ability to change easily for people with physical impairments. Font size, screen size, etc. should be considered.
All output should be contained inside a template!
echo
Simple Machines Forum uses one thing and one thing only to output to the browser – echo
. print()
, heredoc
, and nowdoc
should not be used. echo
is not a function; it is a language construct. So, it does not require parenthesis around its input. Also, it accepts an unlimited number of arguments.
- Send multiple arguments with the use of commas instead of concatenation (using periods).
- Place a space after the comma, not before.
- Put line breaks inside of the quotes. There is no need to use
"\n"
. - PHP whitespace and markup whitespace do not relate. When output should be indented, tabs should be added.
echo
statements should still line up with the function and other statements that they are in as per the PHP guidelines.
Proper Echo Example
// Using a long string with no variables. echo 'This is our very long string. We don\'t need any variables'; if ($code) echo $foobar; else echo 'This is to show you what concatenation looks like ', $txt['see'], 'what I mean by putting line breaks inside of quotes'; // Short string with variable. echo "this $is a $short $string"; // Proper indentation and spacing. if ($woot) { echo ' <div> w00t!'; if ($woot % 2 == 0) echo ' <br /> woot w00t woot'; echo ' </div>'; }
Improper Echo Example
// DO NOT DO IT LIKE THIS! echo "$var"; echo "output"; echo <<<EOD heredoc $stupid heredoc EOD; print "bla \'bla\' bla"; echo 'See what I mean by line breaks?' . "\n", 'Look at how bad this looks' . "\n" . 'compared to just putting the line breaks in quotes.';
Usable & Accessible
These are two distinctly different and important terms that are rarely given too much thought. SMF has many types of users, and Simple Machines is devoted to making it possible for all users to access a page. Thus, differences between users must be considered. Making a page accessible does not mean that it shouldn't look good. Keeping these thoughts in mind should help achieve a usable and accessible page.
- A page should look and sound as good as possible. Remember, there are users out there that use screen readers.
- Some users may have a 21" monitor with a high resolution. Other users may be reading on a mobile device with a 2" screen and no graphics.
- A lot more users than expected disable JavaScript. Think about how many people might do so to avoid popups or ads.
Graceful Degradation
- All browsers should be able to use a page regardless of their JavaScript settings, screen size, operating system, browser version, etc.
- What can not be made to look pretty with JavaScript or CSS due to the browser should still work and look as good as possible.
HTML
- HTML is used to define the structure of a document, not the presentation (styling).
- Use HTML 5.
- Usually, a list should be used for menus.
- Use <p> for paragraphs. Use <br /> only for a single line break. Additional necessary space should be created through extra CSS margins.
- Do not use
<a name=”anchor”>
for anchors. Use <{tag} id=”anchor”>, where {tag} is usually a header tag, since all ids are unique they act as anchors. - Although <b>, <i>, <big>, <small>, <tt> are not deprecated, they are strictly for presentation. They serve no purpose other than to make output look different. To show emphasis on text, consider using <strong> or <em>. To create a heading, consider using <h#>.
- Here are some commonly used deprecated tags that should not be used:
- <s> is often used to signify deleted text. Instead, consider using <del> and <ins>. If the strike through is only necessary for presentation, considering using the CSS text decoration
"text-decoration: line-through;"
. - Do not use <pre> for code snippets. There are the <code> and <sample> tags for that.
- Perhaps the most commonly used deprecated tag is <font>. This tag has no semantic value. Use <span> instead.
- Internet Explorer does not support <q> or <button>. Do not use them.
- <s> is often used to signify deleted text. Instead, consider using <del> and <ins>. If the strike through is only necessary for presentation, considering using the CSS text decoration
- Tables are for tabular data! Think of a table as a spreadsheet. Would the data work well in a spreadsheet? Do not use a table simply to make layout easier.
- Use <th> and <caption> to define the data in the table.
- Forms are a block level tag. They do not require a wrapping div.
- Related fields should be grouped with a <fieldset> tag. All fieldsets should have a legend
- All form fields should have a label using the <label> tag.
- Related options in a select box should be grouped using the <optgroup> tag
- When creating a list of form controls, it is usually best to use a definition list <dl>. Define the label with <dt> and the control with <dd>.
- Never use a reset button. One mistaken click will cause the entire form to disappear!
CSS
CSS is used to define the presentation of a document, not the structure. Do not be afraid of CSS. It can make changing a lot of elements of a page a lot faster. It can also decrease the size of your output. It can also be used to change styles based on the user's interface.
- Use the BSD/Allman indentation style.
- Try to avoid inline styles and useless class names, such as
bigred
. Another user may decide that the text needs to be normal sized but bold and blue. - Use CSS shorthand as often as possible. Here is an excellent article on this subject from http://456BereaStreet.com.
- Do not do browser sniffing. The proper alternative is to use feature sniffing.
- If a value is 0, do not use a value type (px, em, etc).
- IE conditional comments may be necessary, but use them sparingly.
JavaScript
- Simple Machines Forum JavaScript guidelines use the same indentation and whitespace guidelines as PHP.
- Never alter the prototype of
Object
. - Avoid global objects. Use the
var
keyword in all functions. Put stuff in thesmf
namespace/object. Large mods can have their own namespace if they need to. - Avoid
eval()
, including hidden ones.eval()
is allowed in very specific situations, such as parsing JSON, but it shouldn't be used anywhere else. Hiddeneval()
s are something likeonclick = "function();"
. Instead, just useonclick = function;
. - Do not browser detect. Instead, use feature detection. if possible, do feature detection outside of a function.
- Do not do calculations in the 2nd part of a
for
loop. - Avoid the
this
keyword, because what it refers to can be unpredictable. Use it only in functions that will be run only by an event, when the alternative is messing around with theEvent
object. - Use regular expressions – they are faster than most JavaScript string testing functions.
- Use object literal notations. It's quick and simple.
Security
Simple Machines prides itself on its security history and would like to keep it that way. Here are several guidelines to help keep security tight.
Are you my SMF?
The following is a simple, effective solution to make sure that a file is being used only on the intended system and only by SMF. It should appear at the very beginning of a file just after the copyright/license block. The output of die() may be changed, but it isn't recommended.
SMF Check Example
if (!defined('SMF')) die('Hacking attempt...');
Wash those variables!
There's no telling where they've been! Everything that comes from a user can be a potential security risk. After all, there is no way to tell if that user is a sweet old Granny or some crazed hacker bent on destroying the web site. All integers should be defined as integers before they are used. That includes array keys and values. Does that mean that every $_REQUEST
variable has to be cast to an integer? No. That means that $_REQUEST
variables that should be an integer must be cast to an integer.
Casting Example
$_GET['foo'] = (int) $_GET['foo']; $bar = array ( 'my_fav' => (int) $_POST['bar]['my_fav'], 'ghost' => (int) $_GET['boo'], ); // (int) $var and settype($var, 'int') do the same thing. $id_groundup = (int) $id_groundup; $id_yo_momma = settype($id_yo_momma, 'int');
All variables being passed to the database must be escaped! This is a favorite exploit among hackers — SQL injection. Does that mean that every $_REQUEST
variable should be escaped? Yes, but there is a solution for that. In QueryString.php, there is a function called cleanRequest()
. It escapes all $_REQUEST
variables at once. That means that $_REQUEST
variables that are used in output need to be un-escaped with stripslashes()
.
The Permission System
Membergroups should not be hardcoded in order to give them special access. Use allowedTo()
and isAllowedTo()
, where allowedTo()
is a basic check and isAllowedTo()
is a check/error combination. Use boardsAllowedTo()
to get a list of boards a user has a permission to.
Session checks
Before using input passed through a form from a user, their session should always be checked. Use validateSession()
before actions in the administration center to ensure the user is who they say they are. Note that adminIndex()
will do this.