Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Coding style: Difference between revisions

From MoodleDocs
(Adding @access after discussion in dev chat)
(noting that description for @return is optional for simple functions)
Line 497: Line 497:
The @return tag is mandatory if the function has a return statement, but can be left out if it does not have one.
The @return tag is mandatory if the function has a return statement, but can be left out if it does not have one.


If function does 'return;' we use @return void. If a function uses 'return null;' thats a @return null as null is the specific data type.  
If function does 'return;' we use @return void. If a function uses 'return null;' thats a @return null as null is the specific data type.
 
The description portion is optional, it can be left out if the function is simple and already describes what is returned.


If there are multiple possible return types, list them pipe-delimited without spaces:
If there are multiple possible return types, list them pipe-delimited without spaces:

Revision as of 07:35, 14 February 2012

Overview

Scope

This document describes style guidelines for developers working on or with Moodle code. It talks purely about the mechanics of code layout and the choices we have made for Moodle.

For details about using the Moodle API to get things done, see the coding guidelines.

Goals

Consistent coding style is important in any development project, and particularly when many developers are involved. A standard style helps to ensure that the code is easier to read and understand, which helps overall quality.

Abstract goals we strive for:

  • simplicity
  • readability
  • tool friendliness, such as use of method signatures, constants, and patterns that support IDE tools and auto-completion of method, class, and constant names.

When considering the goals above, each situation requires an examination of the circumstances and balancing of various trade-offs.

Note that much of the existing Moodle code may not follow all of these guidelines - we continue to upgrade this code when we see it.

File Formatting

PHP tags

Always use "long" php tags. However, to avoid whitespace problems, DO NOT include the closing tag at the very end of the file.

<?php

require('config.php');

Indentation

Use an indent of 4 spaces with no tab characters. Editors should be configured to treat tabs as spaces in order to prevent injection of new tab characters into the source code.

Don't indent the main script level:

GOOD: <?php require('config.php'); $a = required_param('a', PARAM_INT); if ($a > 10) {

   call_some_error($a);

} else {

   do_something_with($a);

}

BAD: <?php

   require('config.php');
   $a = required_param('a', PARAM_INT);
   if ($a > 10) {
       call_some_error($a);
   } else {
       do_something_with($a);
   }

Maximum Line Length

The key issue is readability.

Aim for 132 characters if it is convenient, it is not recommended to use more than 180 characters.

Wrapping lines

When wrapping a line, indent the follow-on line by 8 spaces rather than 4. For example:

if (a_long_condition() &&

       a_nother_long_condition()) {
   do_something();

}

Using eight spaces makes it easier to spot the difference between wrapped lines and indented blocks.

The example above is only an illustration; it is best practice to avoid wrapping lines in control structures. See below.

Wrapping Arrays

Associative arrays are a exception to the rule about 8-space indent for follow-on lines. The correct layout is: $plugininfo['preferences'][$plugin] = array(

   'id'     => $plugin, 
   'link'   => $pref_url, 
   'string' => $modulenamestr

); Lining up the =>s is optional. Small arrays can be done on one line.

Wrapping function declarations

If you have many parameters, indent them in line with the first parameter: public function graded_users_iterator($course, $grade_items = null, $groupid = 0,

                                     $sortfield1 = 'lastname', $sortorder1 = 'ASC',
                                     $sortfield2 = 'firstname', $sortorder2 = 'ASC') {

Wrapping Control Structures

If you have too many conditions in one control structure, try setting some variables before the start of the structure to improve readability.

GOOD: $coursecategory = $element['object']->is_course_item() or $element['object']->is_category_item(); $scalevalue = in_array($element['object']->gradetype, array(GRADE_TYPE_SCALE, GRADE_TYPE_VALUE));

if ($coursecategory and $scalevalue) {

BAD: if (($element['object']->is_course_item() or $element['object']->is_category_item())

   and ($element['object']->gradetype == GRADE_TYPE_SCALE
   or $element['object']->gradetype == GRADE_TYPE_VALUE)) {

Line Termination

Use standard Unix text format. Lines must end only with a linefeed (LF). Linefeeds are represented as ordinal 10, or hexadecimal 0x0A.

Do not use carriage returns (CR) like Macintosh computers (0x0D).

Do not use the carriage return/linefeed combination (CRLF) as Windows computers (0x0D, 0x0A).

Lines should not contain trailing spaces. In order to facilitate this convention, most editors can be configured to strip trailing spaces, such as upon a save operation. However, if you are editing an existing Moodle file and are planning to submit your changes for integration, please switch off that feature so that whitespace changes do not pollute the patch (other developers will have trouble viewing what you've done if every other line has been edited for whitespace).

Naming Conventions

Filenames

Filenames should :

  • be whole english words
  • be as short as possible
  • use lowercase letters only
  • end in .php, .html, .js, .css or .xml

Classes

Class names should always be lower-case English words, separated by underscores:

class some_custom_class {

   function class_method() {
       echo 'foo';
   }

}

Always use () when creating new instances even if constructor does not need any parameters.

$instance = new some_custom_class();

When you want an plain object of no particular class, for example when you are preparing some data to insert into the database with $DB->insert_record, you should use the PHP standard class stdClass. For example:

$row = new stdClass(); $row->id = $id; $row->field = 'something'; $DB->insert_record('table', $row);

Before Moodle 2.0, we used to define a class object extending stdClass, and use new object(); This has now been deprecated. Please use stdClass instead.

Functions and Methods

Function names should be simple English lowercase words, and start with the name of the module to avoid conflicts between modules. Words should be separated by underscores.

Verbosity is encouraged: function names should be as illustrative as is practical to enhance understanding.

Note there is no space between the function name and the following (brackets).

function forum_set_display_mode($mode = 0) {

   global $USER, $CFG;
        
   if ($mode) {
       $USER->mode = $mode;
   } else if (empty($USER->mode)) {
       $USER->mode = $CFG->forum_displaymode;
   }

}

Function Parameters

Parameters are always simple lowercase English words (sometimes more than one, like $initialvalue), and should always have sensible defaults if possible.

Use "null" as the default value instead of "false" for situations like this where a default value isn't needed. public function foo($required, $optional = null)

However, if an optional parameter is boolean, and its logical default value should be true, or false, then using true or false is acceptable.

Variables

Variable names should always be easy-to-read, meaningful lower-case English words. If you really need more than one word then run them together, but keep them short as possible. Use plural names for arrays of objects.

GOOD: $quiz
GOOD: $errorstring
GOOD: $assignments (for an array of objects)
GOOD: $i (but only in little loops)
BAD: $Quiz
BAD: $camelCase
BAD: $aReallyLongVariableNameWithoutAGoodReason
BAD: $error_string

Core global variables in Moodle are identified using uppercase variables (ie $CFG, $SESSION, $USER, $COURSE, $SITE, $PAGE, $DB and $THEME). Don't create any more!

Constants

Constants should always be in upper case, and always start with the name of the module. They should have words separated by underscores. define('FORUM_MODE_FLATOLDEST', 1);

Booleans and the null value

Use lower case for true, false and null.

Strings

Since string performance is not an issue in current versions of PHP, the main criteria for strings is readability.

Single quotes

Always use single quotes when a string is literal, or contains a lot of double quotes (like HTML):

$a = 'Example string'; echo ''; $html = '<a href="http://something" title="something">Link</a>';

Double quotes

These are a lot less useful in Moodle.

Use double quotes when you need to include plain variables or a lot of single quotes. In Moodle 2.0 or later you won't need to be doing a lot of single quotes for SQL so that's not an issue.

echo "$string"; $statement = "You aren't serious!";

Variable substitution

Variable substitution can use either of these forms:

$greeting = "Hello $name, welcome back!";

$greeting = "Hello {$name}, welcome back!";

String concatenation

Strings must be concatenated using the "." operator.

$longstring = $several.$short.'strings';

If the lines are long, break the statement into multiple lines to improve readability. In these cases, put the "dot" at the end of each line.

$string = 'This is a very long and stupid string because '.$editorname.

         " couldn't think of a better example at the time.";

Language strings

Language strings should "Always look like this" and "Never Like This Example".

Capitals should only be used when:

  1. starting a sentence, or
  2. starting a proper name, like Moodle.

Arrays

Numerically indexed arrays

Negative numbers are not permitted as indices.

An indexed array may start with any non-negative number, however all base indices besides 0 are discouraged.

When declaring indexed arrays with the array function, a trailing space must be added after each comma delimiter to improve readability:

$myarray = array(1, 2, 3, 'Stuff', 'Here');

Multi-line indexed arrays are fine, but pad each successive lines as above with an 8-space indent:

$myarray = array(

       1, 2, 3, 'Stuff', 'Here',
       $a, $b, $c, 56.44, $d, 500);

Associative arrays

Use multiple lines if this helps readability. For example:

$myarray = array(

   'firstkey' => 'firstvalue',
   'secondkey' => 'secondvalue'

);

Classes

Class declarations

  • Classes must be named according to Moodle's naming conventions.
  • The brace should always be written on the line beside the class name.
  • Every class must have a documentation block that conforms to the PHPDocumentor standard.
  • All code in a class must be indented with 4 spaces.
  • Placing additional code in class files is permitted but discouraged. In such files, two blank lines must separate the class from any additional PHP code in the class file.
An example:

/**

* Documentation Block Here
*/

class sample_class {

   // all contents of class
   // must be indented 4 spaces

}

Class member variables

Member variables must be named according to Moodle's variable naming conventions.

Any variables declared in a class must be listed at the top of the class, above the declaration of any methods.

The var construct is not permitted. Member variables always declare their visibility by using one of the private, protected, or public modifiers. Giving access to member variables directly by declaring them as public is permitted but discouraged in favor of accessor methods (set/get).

Functions and methods

Function and method declaration

Functions must be named according to the Moodle function naming conventions.

Methods inside classes must always declare their visibility by using one of the private, protected, or public modifiers.

As with classes, the brace should always be written on same line as the function name.

Don't leave spaces between the function name and the opening parenthesis for the arguments.

The return value must not be enclosed in parentheses. This can hinder readability, in additional to breaking code if a method is later changed to return by reference.

Return should only be one data type. It is discouraged to have multiple return types

/**

* Documentation Block Here
*/

class sample_class {

   /**
    * Documentation Block Here
    */
   public function sample_function() {
       // all contents of function
       // must be indented four spaces
       return true;
   }

}

Function and method usage

Function arguments should be separated by a single trailing space after the comma delimiter.

three_arguments(1, 2, 3);

Control statements

In general, use white space liberally between lines and so on, to add clarity.


If / else

Put a space before and after the control statement in brackets, and separate the operators by spaces within the brackets. Use inner brackets to improve logical grouping if it helps.

Indent with four spaces.

Don't use elseif!

Always use braces (even if the block is one line and PHP doesn't require it).

if ($x == $y) {

   $a = $b;

} else if ($x == $z) {

   $a = $c;

} else {

   $a = $d;

}

Switch

Put a space before and after the control statement in brackets, and separate the operators by spaces within the brackets. Use inner brackets to improve logical grouping if it helps.

Always indent with four spaces. Content under each case statement should be indented a further four spaces.

switch ($something) {

   case 1:
       break;
   case 2:
       break;
   default:
       break;

}


Foreach

As above, uses spaces like this:

foreach ($objects as $key => $thing) {

   process($thing);

}

Require / include

Each file that is accessed via browser should start by including the main config.php file.

require(dirname(dirname(__FILE__)) . '/config.php');

Any other include/require should use an absolute path starting with $CFG->dirroot or $CFG->libdir. Relative includes can sometimes behave strangely under PHP, so it is safer to avoid them. Our CLI script must not use relative config.php paths.

Includes should generally only be done at the top of files or inside functions/methods that need them. Using include/require in the middle of a file in global scope very hard to audit the security of the code.

All other scripts with the exception of imported 3rd party libraries should use following code at the very top to prevent direct execution which might reveal error messages on misconfigured production servers.

defined('MOODLE_INTERNAL') || die();

Documentation and comments

Code documentation explains the code flow and the purpose of functions and variables. Use it whenever practical.

PHPDoc

Moodle stays as close as possible to "standard" PHPDoc format to document our files, classes and functions. This helps IDEs (like Netbeans or Eclipse) work properly for Moodle developers, and also allows us to generate web documentation automatically.

PHPDoc has a number of tags that can be used in different places (files, classes and functions). We have some particular rules for using them in Moodle that you must follow:

@copyright

These include the year and copyright holder (creator) of the original file. Do not change these in existing files!

 @copyright 2008 Kim Bloggs

@license

These must be GPL v3+ and use this format:

 @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later"

@param

We use short type names (bool instead of boolean, int instead of integer).

Don't put hyphens or anything fancy after the variable name, just a space.

 @param int $maximum The maximum allowed age for this nightclub

@return

The @return tag is mandatory if the function has a return statement, but can be left out if it does not have one.

If function does 'return;' we use @return void. If a function uses 'return null;' thats a @return null as null is the specific data type.

The description portion is optional, it can be left out if the function is simple and already describes what is returned.

If there are multiple possible return types, list them pipe-delimited without spaces:

 @return string|bool If $output was true then return a string, else it will echo the string and return true.

@uses

If a function uses die or exit, please add this tag to the docblock to help developers know this function could terminate the page:

 @uses exit

@access

The access can be used to specify access control for an element

  1. Should only be used when the method definition does not already specify access control.
  2. In the case of functions, specifying public access is redundant and so should be avoided.
 @access private

@package

The package tag should always be used to label php files with the correct Frankenstyle component name. Full rules are explained on that page, but in summary:

  1. If the file is part of any component plugin, then use the plugin component name (eg mod_quiz or gradereport_xls)
  2. If the file is part of a core subsystem then it will be core_xxxx where xxxx is the name defined in get_core_subsystems(). (eg core_enrol or core_group)
  3. If the file is one of the select few files in core that are not part of a subsystem (such as lib/moodlelib.php) then it just as a package of core.
  4. Each file can only be part of ONE package.

(We do not have standards for @subpackage at all. You can use within your @package how you like.)

 @package gradereport_xls

@category

We use @category only to highlight the public classes, functions or files that are part of one of our Core APIs, or that provide good example implementations of a Core API. The value must be one of the ones on the Core APIs page.

 @category preferences 

@since

When adding a new classes or function to the Moodle core libraries (or adding a new method to an existing class), use a @since tag to document which version of Moodle it was added in. For example:

 @since Moodle 2.1

@see

If you want to refer the user to another related element (include, page, class, function, define, method, variable) then you can use @see.

 @see some_other_function()

@link

If you want to refer the user to an external URL, use @link.

 @link https://docs.moodle.org/dev/Core_APIs

inline @link

Occasionally you might want to refer to something else inline within your text, say in a function description. For these cases you can use an inline @link (with an element name OR a URL) and it looks like this:

  /**
   * This function uses {@link get_string()} to obtain the currency names...
   * .....

@deprecated

When deprecating an old API, use a @deprecated tag to document which version of Moodle it was deprecated in, and add @todo and @see if possible. Make sure to mention relevant MDL issues. For example:

/**

* ...
* @deprecated since Moodle 2.0 MDL-12345 - please do not use this function any more.   
* @todo MDL-22334 This will be deleted in Moodle 2.2.
* @see class_name::new_function()
*/

If it is important that developers update their code, consider also adding a debugging('...', DEBUG_DEVELOPER); call to repeat the deprecated message. If the old function can no longer be supported at all, you may have to throw a coding_exception. There are examples of the various options in lib/deprecatedlib.php.

@throws

This tag is valid and can be used optionally to indicate the method or function will throw and exception. This is to help developers know they may have to handle the exceptions from such functions.

Files

All files that contain PHP code should contain, without any blank line after the php open tag, a full GPL copyright statement at the top, plus a SEPARATE docblock right under it containing a:

  1. short one-line description of the file
  2. longer description of the file
  3. @package tag (required)
  4. @category tag (only when everything in the file is related to one of the Core APIs)
  5. @copyright (required)
  6. @license (required)

<?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>.


/**

* This is a one-line short description of the file
*
* You can have a rather longer description of the file as well,
* if you like, and it can span multiple lines.
*
* @package    mod_mymodule
* @category   backup
* @copyright  2008 Kim Bloggs
* @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

Classes

If the class is the only thing in a file then it is covered by the file docblock and adding an explicit class docblock is optional.

However, in all other cases, classes must have a docblock like this:

/**

* Short description for class 
*
* Long description for class (if any)...
*
* @package    mod_mymodule
* @copyright  2008 Kim Bloggs
* @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/


(NOTE that classes that are fundamental to core APIs will also need a @category tag)

Properties

All properties should have a docblock with the following minimum information:

class example {

   /** @var string This variable does something */
   protected $something;

}

Even if there are several properties all sharing something in common, do not use DocBlock templates. Instead, document every property explicitly as in the following example:

class zebra {

   /** @var int The number of white stripes */
   protected $whitestripes = 0;
   /** @var int The number of black stripes */
   protected $blackstripes = 0;
   /** @var int The number of red stripes */
   protected $redstripes = 0;

}

Constants

Class constants should be documented in the following way:

class sam {

  /**
   * This is used when Sam is in a good mood.
   */
  const MOOD_GOOD = 0;

}


Functions

All functions and methods should have a docblock like this:

/**

* The description should be first, with asterisks laid out exactly
* like this example. If you want to refer to a another function,
* use @see as below.   If it's useful to link to Moodle
* documentation on the web, you can use a @link below or also 
* inline like this {@link https://docs.moodle.org/dev/something}
* Then, add descriptions for each parameter and the return value as follows.
*
* @see clean_param()
* @param int   $postid The PHP type is followed by the variable name
* @param array $scale The PHP type is followed by the variable name
* @param array $ratings The PHP type is followed by the variable name
* @return bool A status indicating success or failure
*/

Defines

All defines should be documented in the following way:

/**

* PARAM_INT - integers only, use when expecting only numbers.
*/

define('PARAM_INT', 'int');

/**

* PARAM_ALPHANUM - expected numbers and letters only.
*/

define('PARAM_ALPHANUM', 'alphanum');


Inline comments

Inline comments should use the // style, laid out neatly so that it fits among the code and lines up with it.

function forum_get_ratings_mean($postid, $scale, $ratings = null) {

   if (!$ratings) {
       $ratings = array();     // Initialize the empty array
       $rates = $DB->get_records('forum_ratings', array('post' => $postid));
       // Process each rating in turn
       foreach ($rates as $rate) {
           ...
       }
       // Do something else here 
       something_else();
    ... etc.


If your comment is due to some MDL issue, please feel free to include the correct MDL-12345 in your comment. This makes it easier to track down decisions and discussions about things.

Using TODO

This is especially important if you know an issue still exists in that code that should be dealt with later. Use a TODO along with a MDL code to mark this. For example:

// TODO MDL-12345 This works but is a bit of a hack and should be revised in future.

If you have a big task that is nearly done, apart a few TODOs, and you really want to mark the big task as finished, then you should file new tracker tasks for each TODO and change the TODOs comments to point at the new issue numbers.

(The script .../lib/simpletest/todochecker.php in Moodle 2.0 will check this. Requires an admin login. This is a new requirement, see MDL-19772)

CVS keywords

We have stopped using CVS keywords such as $Id$ in Moodle 2.0 completely.

Exceptions

Use exceptions to report errors, especially in library code.

Throwing an exception has almost exactly the same effect as calling print_error, but it is more flexible. For example, the caller can choose to catch the exception and handle it in some way. It also makes it easier to write unit tests.

Any exception that is not caught will trigger an appropriate call to print_error, to report the problem to the user.

Do not abuse exceptions for normal code flow. Exceptions should only be used in erroneous situations.

Exception classes

We have a set of custom exception classes. The base class is moodle_exception. You will see that the arguments you pass to new moodle_exception(...) are very similar to the ones you would pass to print_error. There are more specific subclasses for particular types of error.

To get the full list of exception types, search for the regular expression 'class +\w+_exception +extends' or ask your IDE to list all the subclasses of moodle_exception.

Where appropriate, you should create new subclasses of moodle_exception for use in your code.

A few notable exception types:

moodle_exception
base class for exceptions in Moodle. Use this when a more specific type is not appropriate.
coding_exception
thrown when the problem seems to be caused by a developer's mistake. Often thrown by core code that interacts with plugins. If you throw an exception of this type, try to make the error message helpful to the plugin author, so they know how to fix their code.
dml_exception (and subclasses)
thrown when a database query fails.
file_exception
thrown by the File API.

Dangerous functions and constructs

PHP includes multiple questionable features that are highly discouraged because they are very often source of serious security problems.

  1. do not use eval() function - language packs are exception (to be solved in future)
  2. do not use preg_replace() with /e modifier - use callbacks in order to prevent unintended PHP execution
  3. do not use backticks for shell command execution

Credits

This document was drawn from the following sources:

  1. The original Coding guidelines page
  2. The Zend guidelines and
  3. Feedback from all core Moodle developers

See also