Skip to content

e107 Coding Standard

Cameron edited this page May 25, 2020 · 26 revisions

IMPORTANT: If you're unsure of something, ask in gitter.

Section 1 - Pear Standard

(Where no comment here, the Pear standard applies unmodified) The Pear coding standards (http://pear.php.net/manual/en/standards.php) form the basis of our own standard. This standard makes some comments on those standards, and in some cases defines a different way of doing things.

Indenting

Use tabs to indent - not spaces - then you can adjust your editor to suit.

Control Structures

Use BSD/Allman/Pascal style - opening '{' on a new line. The only exception is for short single statements within {...} such as ' return ; '- then it may be on a single line. This is optional.

Good:

if($t ==2)
{
(tab)echo "hello";
}


if($t == 2)
{
    return;
}


if($t == 2) { return; } // (Optional -for short lines only)

Bad:

if($t ==2) echo "hello"; // Missing braces { }


if($t ==2){ echo "<div>My long line of code and ".LAN_XXX." etc</div>"; }

Section 2 - PHP/e107

PSR-1 is used with the exception of class/method naming (see below).

Variables

Use camelCase naming.

$myVariable = "whatever"; 

Constants

Use UPPER_CASE_WITH_UNDERSCORES for constants.

define("CONSTANT_NAME", "some value");

Where numeric values represent a particular status, define a constant for that value to make the meaning more obvious.

Classes, Methods and Functions.

Class names must use lowercase and may use an underscore (_) to separate words. The corresponding file should use the same name and casing. (see below)

Method/Function names should use camelCase unless they are shortcodes (eg. sc_news_image()) or methods within e_admin_form_ui in which case they should use lowercase to match the database field name.

Example: myplugin_shortcodes.php

class myplugin_shortcodes extends e_shortcode
{
    public function sc_myplugin_field($parm=null)
    {
        $value = $this->myCustomFunction();
        return $value; 
    }

    private function myCustomFunction()
    {
        // .... 
    }
}

Admin-ui example:

class news_form_ui extends e_admin_form_ui
{

	function news_template($curVal,$mode)
	{
		if($mode === 'read')
		{
			return $curVal;
		}

            // ...

Routines are to be documented at both file and function level using phpDoc syntax. Where appropriate create supplementary pages of general documentation, and document individual variables. When passing more than three or so parameters to a function, consider passing some of them in an array (especially convenient if many are optional) or if within a class, consider using vars.

Better:

function myFunction($array)
{

}

Avoid:

function myFunction($var1,$var2,$var3,$var4,$var5,$var6);
{

}

Avoid replicating or bypassing e107 classes/handlers which are used for specific purposes.
One example: use the e107 file-class instead of PHP file functions to read directories.

$fl = e107::getFile();  
$filesArray = $fl->get_files($path, $fmask);

Paths

File names must be all lowercase.

Use the e_XXX path constants to refer to directories: Absolute constants (e_XXX_ABS) for paths within HTML pages Relative paths (e_XXX) to access files from within the code

e_BASE; // the root directory of e107's installation
e_PLUGIN; // equivalent to: e107_plugins/
THEME; // equivalent to current theme directory

eg. 
require_once(e_PLUGIN."myplugin/myfile.php");
$img = e_PLUGIN_ABS."myplugin/images/myimage.png"; 

Forms

Always use e107's form handler for form elements in the front-end.

$frm  = e107::getForm();
$frm->checkbox($name,$value,$checked);
$frm->radio($name,$value,$checked);		
$frm->select($name,$array,$currentValue); // full SELECT tag with OPTIONs.  
$frm->admin_button($name, $value, $action, $label); // $label needed only when different to $value
$frm->admin_button('field_name', LAN_UPDATE, 'update');

Globals

Avoid the use of global variables, especially as an alternative to parameter passing. (We'll always need some globals - but the less there are, the smaller the chance of a clash). In v2.0 use the e107::getxxx syntax to get pointers to 'global' functions and objects like $sql, $tp

$pref   = e107::getPref();  // grab preferences array
$sql    = e107::getDb();     // sql class
$frm    = e107::getForm();   //  form class
$tp     = e107::getParser(); // text/template parsing class
$ns     = e107::getRender(); // rendering class. 
$fl     = e107::getFile();  // file handling class. 
$mes    = e107::getMessage(); // message handling 

If/Else and Switch

Consider switch statements rather than multiple if...then...elseif.... (generally for three or more, or better yet, use an array to change the result where possible. Simple 'If/Else' example:

if($j == 3)
{
    $text = "Hello";
}
else
{
    $text = "Goodbye";
}

Switch Example:

 switch($option)
 {
    case 'opt1':
     $val = "value1";
    break;
 
    case 'opt2':
     $val = "value2";
    break;

    case 'opt3':
     $val = "value3";
    break;
 
    case 'opt4':
     $val = "value4";
    break;
 }

Preferred version of above:

$array = array(
    'opt1'=>'value1',
    'opt2'=>'value2',
    'opt3'=>'value3',
    'opt4'=>'value4'
);
$val = $array[$option];

Extract

Avoid using extract() on arrays (usually DB rows) - it causes confusion, and sometimes unneeded variables. And especially don't use it on $_POST and $_GET variables! Use the new retrieve function to select and fetch data as an array.

Good:

$sql = e107::getDb();
$array = $sql->retrieve('user', 'user_email, user_name', 'user_id=1'); // returns row from table e107_user

OR
    
$array = e107::getDb()->retrieve('user', 'user_email, user_name', 'user_id=1');

Avoid:

while($row = $sql->db_Fetch(MYSQL_ASSOC)
{
    extract($row);
    $text .= $field_name;
}

Explode() and List

Consider using list() instead of explode() when the values will be given their own strings and the size of the array is known.

Okay:

$tmp = explode("|",$array);
$id = $tmp[0];
$email = $tmp[1];

Better:

list($id,$email) = explode("|",$array);

As $_POST variables are checked/sanitized, create new variables to hold the checked values - makes it obvious what's safe and what's dodgy. (Note validator_class.php, especially where the same DB data can be input from several different places). Make sure $_POST variables are only processed if the user has the relevant permissions, and if the relevant options are enabled (its not enough to hide the button or field which initiates the post - security levels MUST be checked at the point of execution).

Debugging/Errors

Code must not produce any PHP warnings or notices during normal use. This primarily implies that all variables must be defined before being used. It also implies that a corrupted installation may produce errors, although as far as practicable the code should accommodate this. Many PHP notices can be avoided by using the functions vartrue() and varset() - see class2.php.

We recommend this collection of Firefox Addons. Most important being the e107 Debugger.

Upgrades

For simple changes, the code should silently handle the situation For more complex changes, an upgrade routine is required. See [plugin]_setup.php

Javascript, CSS and Meta

Use the following functions to include js or css files

e107::js();
e107::css();
e107::meta();

SQL Details

Don't enclose integer values in quotes in WHERE clauses - slows up the query. (But make sure there's no way the value can be something other than an integer).

$sql->select('user','*',"user_id = '".$id."' LIMIT 1");  	
$id = 1;

//BAD - Slower and risk of failure.  
$sql->gen("SELECT * FROM `#user` WHERE user_id = '".$id."' LIMIT 1");  // previously db_Select_gen 

//GOOD - Faster, 0 if no value  
$sql->gen("SELECT * FROM `#user` WHERE user_id = ".intval($id)." LIMIT 1"); // previously db_Select_gen

Where only one record is expected, use "LIMIT 1" in the query for better performance.

insert() and update()

Associate field names and values (rather than using an ordered list of just values) - avoids problems with DB changes. Make sure values are specified for any field without a default - else fails in STRICT mode.

Insert Example:

$insert = array(
    'user_id' => 1,
    'user_email' => '[email protected]'
);

$sql->insert($insert);

Update Example (many fields):

$update = array(
    'user_email' => '[email protected]',
    // ... Long list of fields/values
    'WHERE' > 'user_id = 1'
);

$sql->update($update);

Update Example (few fields):

$sql->update("user","user_email = '[email protected]' WHERE user_id = 1 LIMIT 1");

Note the _FIELD_DEFS and _NOTNULL arrays for assisting with producing valid data. These are auto-generated for any insert() or update() that passes array data which does not include them. If the automatic generation produces the wrong conversions, there is a mechanism to override the default.

Use backticks around table names and field names. (eg.#user)

$sql->gen("SELECT user_email FROM `#user` WHERE user_id = 1 LIMIT 1");

Only read the fields that are actually needed, especially on tables with a large record size.

The following functions are available through gen():

SQL_CALC_FOUND_ROWS - returns total number of rows which would match the search criteria in the absence of a LIMIT phrase.
If used, value is automatically retrieved and placed into $sql->total_results
REPLACE - effectively if row exists, delete it, then insert (so unspecified fields become the default)
INSERT ... ON DUPLICATE KEY UPDATE - on existing records, will only update specified fields

To avoid problems when 'STRICT' mode is set for MySQL, make sure any field which doesn't have a default value is defined (usually 'text' fields). (This is handled automatically if passing array data to insert() or update(). (db_Insert() or db_Update())

If reading XML files (typically plugin.xml) use the filter capability to limit the amount of data retained in memory to that which is actually needed.

Plugins

Many of the plugins in the v2 branch have not yet been upgraded to the v2 way of doing things. If you are developing a new plugin for v2+ of e107, be sure to use the following plugins as your reference of how things SHOULD be done.

  • faqs - admin-area only
  • gallery - front-end and admin-area.

To check whether a particular plugin is installed, use function e107::isInstalled($plugname) - returns TRUE if plugin installed.

Language Files and LANS

Plugin Language-File Naming

Use the following path: e107_plugins/YOUR_PLUGIN/languages/LANGUAGE/

  • English_front.php - used in front-end script.
  • English_admin.php - used in admin script.
  • English_global.php - used site-wide.
  • English_log.php - used for logging.

Loading Language-Files

To load a language file from the current theme's directory:

  e107::lan('theme'); // loads e107_themes/CURRENT_THEME/languages/English.php (when English is selected)

To load a language file from a plugin folder:

e107::lan('faqs');
e107::lan('faqs',true);
e107::lan('faqs',false, true);
e107::lan('faqs',true, true);

Will include the following paths:

e107_plugins/faqs/languages/English_front.php
e107_plugins/faqs/languages/English_admin.php
e107_plugins/faqs/languages/English/English_front.php
e107_plugins/faqs/languages/English/English_admin.php 

Defining Language Terms aka 'LANS'

Avoid duplicating terms, particularly in the admin area. If coding for admin, always search lan_admin.php and the lan_xxxx.php of the specific page you are working on for existing LANs which may match what you need translated. Avoid using HTML or URLs inside LAN definitions. Also, use double quotes within the defines and use str_replace where needed. See the examples below:

Good:

define("LAN_XXX", "Thank you [b]Firstname[/b]");
define("LAN_XXX", "Go to [x] to see the results."); // Good - replace [ and ] with <a href='...'> and </a> using str_replace()
define("LAN_XXX", "I want to [quote] here"); // Good - replace [ and ] with " " using str_replace()

Bad:

define("LAN_XXX", "Thank you <b>Firstname</b>"); //Bad contains HTML
define("LAN_XXX", "Thank you <a href='www.somewhere.com'>Firstname</a>"); //Bad contains HTML
define("LAN_XXX", "Thank you [link=www.somewhere.com]Firstname[/link]"); //Bad - allows translator to modify link.

Avoid short language strings for words such as 'and', 'to' and so on. There aren't always equivalents in other languages. If embedding values into a phrase, use substitution. Avoid using substitution terms which are real words or known bbcodes.

define("LAN_EXAMPLE_01", "Update results: [x] records changed, [y] errors, [z] not changed");
$srch = array('[x]','[y]','[z]');
$repl = array($changed,$errors,$unchanged);
$text = str_replace($srch,$repl,LAN_EXAMPLE_01);

Template Files

Use the gallery plugin as your reference. Avoid code, especially functions, inside template files. You may load a template file in the following way:

$template 	= e107::getTemplate('gallery');	// loads e107_plugins/gallery/templates/gallery_template.php

HTML

e107 v2+ uses the HTML5 standard. Be sure to validate the HTML output of your code. Many of us use the Firefox addon: http://users.skynet.be/mgueury/mozilla/

Shortcodes

Note and use the class-based structure introduced in v2.0 - with this it should be possible to totally avoid globals. See the gallery or faqs plugins for an example.

Newer Plugins:

$sc = e107::getScBatch('gallery',TRUE); // loads e107_plugins/gallery/shortcodes/batch/gallery_shortcodes.php

Version 1.x plugin upgraded/adapted:

$sc = e107::getScBatch('faqs',TRUE); // loads e107_plugins/faqs/faqs_shortcodes.php

Parameter standards (see eHelper::scParams() and eHelper::scDualParams() )

Recommended Code Editors

We recommend using PHPStorm by Jetbrains

Clone this wiki locally