Coding Guidelines From Online Manual

Revision as of 15:23, 24 May 2010 by Admin (talk | contribs)
Jump to: navigation, search

Simple Machines prides itself on its clean and pure code. This document provides as guidelines for all coding done in relation to Simple Machines including Simple Machines Forum. This includes all development done by the SMF Team and users including customizations (mods/themes), core development, and tools.

There may be times when certain guidelines in this document can not be adhered to. This is understandable and these are only meant to be guidelines. They serve to help you and our users to better understand the code put in to SMF.

This document is meant for a semi-experienced user. As such, it doesn't define a lot of things which most programmers will know such as CSS, HTML, PHP, and MySQL.

The term ‘foobar’ (or ‘foo’ & ‘bar’) is used throughout this document as an example. It is not an actual function, variable, or example. It is simply a placeholder. After the section on naming conventions, the examples may not adhere to the naming conventions. They are only examples.

If you have any questions about this document, please see the SMF Coding Discussion board in the SMF Community Forums. You may visit the function database for information about files, functions, and variables described in this document. If you have any comments or suggestions about this document, please see the SMF Documentation site.

This document is free for distribution. Please translate this to as many languages as possible.

Model View Controller (MVC)

Simple Machines Forum is built using a “model-view-controller” (MVC) architecture. The controller is the action and file. The model is functions which can be used in a separate action/controller. The view can be considered the output and theme related functions. Each group of actions should have their own file. For instance admin actions use Admin.php. All functions which are to be used by functions in that file or other files should use a file for the model. In the case of Admin.php, we use Subs-Admin.php. The last piece of the puzzle is to create a view – admin.template.php. The view is broken up in to two parts – structure and presentation (CSS). Not all templates have their own CSS file – that would be overkill. Structure is then broken up in to several parts - template functions (*_above() & *_below()), front-end output such as HTML, CSS and JavaScript, and a language file. These files are broken up in to directories. The following directories are based on the root SMF directory. The view files are in the Themes directory. The model and controller files are located in Sources.

Naming Conventions

This section describes the naming conventions for files, functions, and variables. The conventions below are ambiguous. Use Descriptive Names

The name should describe what you are using this for. For instance $message_settings instead of $mset. The name should signify what the object is being used for, not where it is to be used. Use English as the default language when choosing a name. Camel Case and Underscores

Use underscores ‘_’ or 1camel case (foo_bar() or fooBar()). Exception: 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 you are displaying an index of information to be displayed to a user such as BoardIndex.php and MessageIndex.php, the file should end with ‘Index’. A file that is used for the management of a feature, such as members, should be prefixed with ‘Manage’. For instance ManageMembers.php. Files that are not the base file for an action, but instead contain many functions to interact with possibly several files, should be prefixed with ‘Subs-’. For instance Subs-Post.php. Classes should have their own file, prefixed with ‘Class-’. Cascading Style Sheets (CSS)

ID and class names must be lower case. ID and class names should always describe what they do, not where they are to be used. Naming Conventions Examples: IDs & class names

/* WRONG WAY TO DO IT! */ .rightmenu { } /* RIGHT WAY TO DO IT! */ .profilemenu { }

Database

Functions

Do not use mysql_query() to send a query to MySQL. In all versions up to SMF 1.1.x, db_query(). In future versions that is done with $smfFunc['db_query'](). Do not use mysql_fetch_array(). Most of the time you should be using an associative array by using mysql_fetch_assoc(). In rare cases, like when using list(), you may use mysql_fetch_row(). Use mysql_free_result() to free the resource. Queries

Queries have the ability to make your forum run fast or to cripple it. It all depends on how your query is designed and what it is designed for. You, as a developer of queries, should do your best to ensure that a query will be optimized. 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. You should do simple benchmarking to test the difference. You may not use UNION, SET PASSWORD, BENCHMARK, subselects, or comments in a query. As usual, there is an exception that you should almost never do as it allows for security vulnerabilities: $modSettings['disableQueryCheck'] = 1; Use LIMIT whenever possible when the WHERE clause contains a column that does not have a UNIQUE index – to include primary keys. When selecting columns, only select those which you are going to use. Do not use SELECT *. When doing the shorthand INNER JOIN, always enclose in parenthesis FROM (table1 as t1, table2 as t2). All keywords 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. You do not need to add the database name to tables. All comments should be before the call to db_query(), there should be no comments inside of queries. Do not rely on the default value. If you are doing an insert and the table has a column that you have no value for, don't include that column. Whitespace

One LF after db_query(" Indent once past the base of the query. One space after each keyword, column name, and table name. No space 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. See the example at the end of this section for more information on that. 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, specify NOT 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. Use UNSIGNED when are you defining an ID. Primary keys are strongly recommended.

Example of a query:

// Get their name and part of their address (excuse the wrapping) $result = db_query(" SELECT ppl.first_name, ppl.last_name, add.city, add.address FROM people as ppl LEFT JOIN addresses as add ON (add.id_address = ppl.id_address) WHERE ppl.id_person = $id_person AND (ppl.middle_name = 'foo' OR ppl.suffix != 'jr') AND $condition", __FILE__, __LINE__);


Example of a schema:

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

Simple Machines uses 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 tabs1. No need for brackets with one line of code after a control structure Use brackets for switch ... case and every other time (other than one liners) there are multiple statements. No space after function name and before (); So no space fooBar(); Space before and after the equal sign. $var = ; 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 extra line after the closing ?> Each statement should be on its own line One space after control structures such as if () elseif () for () while () etc 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) (see the example in Appendix B) Every argument, except the last, in an argument list should have a space after the comma. When declaring a function, the function name should have a space after it, before the ‘(’. ie function foo () One space before and after the ‘.’ when concatenating. (See ‘echo’ for echo concatenation) Functions (and language constructs)

Arguments with default values go at the end of the argument list in the function definition. IE function foo ($bar, $var = true, $arg = 'foobar'); Wrapper function, or functions that do nothing more than call another function, should not be used Use isset() instead of in_array() Use empty() instead of isset() && $var != && $var != 0 && $var != false Use include_once() instead of include() and require_once() instead of require(). Code that you want 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. If you want to put a variable in the global scope, use global. When you do need to use a constant, they should be uppercase. There are some special variables that we use such as $modSettings, $context, $sourcedir, etc. You can find out more about those in the documentation. Use those variables before creating your own. $_GET and $_POST are available via $_REQUEST. It doesn't 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. So, if you wish to use them you are going to have to unescape them. Also, $_GET has had htmlspecialchars__recursive() done to it. So you will need to undo those special chars if you want to use the plain text. 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 as you start to work with that variable. For example, don't do $var = 'foo'; $var = $var == 'foo' ? true : false; Error Handling

All code should be E-STRICT compatible. You should almost never use @ to silence an error. If you expect there to be an error, check to see if you will receive that error first. For instance, if you are going to include a file, check to see if it exists if you expect it to fail. Otherwise, use require_once(). We have our own error handling functions. You will surely see them while debugging your code. They should be used when you want to return an error. To see those functions, look in Errors.php. Comments

Comment often! 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 use ‘// ’ - two forward slashes (//) and a space then the text. It should end with a period. Multiple line comments should use /* */ and each are on their own line. PHP Example of comments:

// Here goes a one-liner... so a dog and a man walk in to a bar function do_something() { 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 and ?>. Never use short tags! SMF uses procedural programming for most things. If you are not creating an entire class, please do not use object oriented programming (OOP). Future versions of this document will include guidelines for writing OOP PHP code. Use single quotes. You may use double quotes when you want to concatenate a variable, but should only be used in smaller strings. Free as many resources as possible. Free db_query() calls as often as possible, unless it is towards the end of the operation of the ENTIRE script. Do not use deprecated features. Nor should you use features that don't exist in the minimum version.

Example of proper PHP:

<?php // Copyright © 2007: Joshua Dickerson

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

We have a lot of goals for output. Semantic markup, accessible output, usable output, XHTML 1 or HTML 4.01 valid, and CSS 2 valid. That can be a bit confusing. To break it down – semantic markup means that the tags are used in a manner that make sense to a human and machines (ie search engines). Like headings used for headings, div used to divide sections, p used for paragraphs, etc. It also must validate in the document type. Whether that be XHTML or HTML. The CSS files also must be valid. There are a lot of browsers out there that accept mistakes/kludges in markup, we don't. The accessible part is a little less simple. It means that your markup should be all of the above including the ability for it to change easily for people with impairments. Font size, screen size, etc are things that need to be thought about.

All output should be contained inside a template! echo

Simple Machines uses one thing and one thing only to output information. We do not use print() or heredoc2. We only use echo. It is not a function, it is a language construct. So, you don't need parenthesis around it's output. As well, you can pass an unlimited number of arguments to it. 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 you want your output to be indented, you need to add tabs. Your PHP/echo statements should still line up with the function and other statements that they are in as per the PHP guidelines. Example of proper usage of echo:

// 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 '

w00t!';

if ($woot % 2 == 0) echo '
woot w00t woot';

echo '

';

} Example of improper usage of echo:

// 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 AND Accessible!

These are two distinctly different and important terms but are rarely thought about. Making a page accessible does not mean that it shouldn't look good. The guidelines below will help you to achieve an accessible page. We have many types of users. We are devoted to making it possible for all users to access a page. Thus, you have to consider differences between users. A page should look and sound good as best you can. Remember, there are users out there that use screen readers. You might have a 21” monitor with a high resolution. Other users may be reading your page on a mobile device with a 2” screen and no graphics. A lot more users than you may think don't have JavaScript enabled. Think about how many people might disable JavaScript so they don't get popups or ads. Graceful Degradation

All browsers should be able to use a page regardless if JavaScript is enabled, screen size, operating system, browser version, etc. What can't be made to look pretty with JavaScript or CSS due to the browser should still work and should look as good as possible. HTML

HTML is used to define the structure of a document, not the presentation (make this bold, this small, this big). Use HTML 4.01 or XHTML 1.0 transitional/strict doctype. Aim for strict. Usually a list should be used for menus.

Use

for paragraphs. Use
only for a single line break. Two or more define a paragraph so use that. If you need more space in between paragraphs use extra margin through CSS. Don't use

when you need 3 or more line breaks, but don't use
when you need 2 line breaks. You should not use 3 line breaks in anything except user submitted text. Do not use <a name=”anchor”> for anchors. Use <{tag} id=”anchor”> since all ids are unique they act as anchors. Where {tag} is usually a header tag. Although , , , , are not deprecated, you should understand that they are strictly for presentation. They serve no purpose other than to make your output look different. If you want to show emphasis on text, consider using or . If you want to create a heading, consider using <h#>. Here are some commonly used deprecated tags that should not be used: You may wish to show text with a strike through it. You may be using to signify that it is for deleted text. Instead, consider using and . If you are only striking it for presentation, considering using the CSS text decoration “text-decoration: line-through;”. Do not use

 when you really want to show a code snippet. There are the  and <sample> tags for that.
Perhaps the most commonly used deprecated tag is . This tag has no semantic value. Next time you are thinking of using , use .
Internet Explorer does not support  or <button>. Do not use them.
Tables are for tabular data! Think of a table as a spreadsheet. Would your data work well in a spreadsheet? Are you only using a table because it makes it easier to layout data?
Use  and  to define the data in the table.
Forms are a block level tag. They do not require to be wrapped by a 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 
. Define the label with
and the control with
. 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. Don't 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 your styles based on the user's interface. Use the BSD/Allman indentation style. Try to avoid in-line styles, and useless class names such as ‘bigred’, another user may decide that the text needs to be normal sized, but bold and blue. Only use pseudo classes for links. Internet Explorer 6 and below don't support them. Use CSS shorthand as often as possible. An excellent article on this can be found on 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). Box model: This is where a correct doctype comes in. IE < 6 uses IE box model. IE >=6 uses W3C when a correct doctype is set. You may use IE condocsditional comments. Use them sparingly. JavaScript Our 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 the smf 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; Don't browser detect, instead use feature detection. And if possible, do feature detection outside of a function. Don't do calculations in the 2nd part of a for loop. for (var i = 0; i < theArray.length; i++) -> for (var i = 0, l = theArray.length; i < l; i++) Avoid the this keyword, because what it refers to can be unpredictable. Use it only in functions that you know will be run only by an event, when the alternative is messing around with the Event object. More info on the this keyword. Avoid string concatenation. If you need to join many strings, put them in an array and use the join method. Use use regular expressions – they're faster than most JavaScript string testing functions. Use object literal notations. It's quick and simple.

Security

Security Simple Machines prides itself on its security history. We would like to keep it that way. We have added several guidelines to keep security tight. Are you my SMF? We have come up with a simple, but effective solution to make sure that your file is being used only on your system and only by SMF. The example below defines exactly how this check should appear. It should appear at the very beginning of your file, just after the copyright/license block. You may change what is output on die() but it isn't recommended. Example of SMF check: if (!defined('SMF')) die('Hacking attempt...'); Wash those variables, you don't know where they've been! Everything that comes from a user can be a potential security risk. After all, you don't know if that user is your sweet old Granny, or some crazed hacker hell bent on destroying your website. All integers should be defined as integers before you start working with them. That includes array keys and values. Does that mean that you have to cast every $_REQUEST variable to an integer? No. That means that you must cast all $_REQUEST variables that should be an integer to an integer. Example of casting to int: $_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 you should escape every $_REQUEST variable sent to you? Yes, but we have a solution for that. In QueryString.php, there is a function called cleanRequest(). It escapes all $_REQUEST variables for you. Although, that means when you are working with $_REQUEST variables, you need to un-escape them with stripslashes(). The permission system Do not hard code the groups that you want to give access to. Use allowedTo() and isAllowedTo() where allowedTo() should be used for a basic check and isAllowedTo() should be used as a check/error combination. When you want to get a list of boards a user has a permission to, use boardsAllowedTo(). Session checks Before you start to work with input passed through a form from a user, you should always check their session. Whenever you have an action in the administration center, use validateSession() to ensure the user is who they say they are. Note that adminIndex() will do this for you.


Advertisement: