Project Coding Standards
These are general notes applied to Projects that I work on, the main intent is to try and make the code maintainable over a long period of time.
HTML (and Flexy Templates)
PHP Code
- Follow PEAR
Standards Which are (with slight additions):
- Indenting
- 4 spaces (Not tabs)
- Do not over indent
- Try and keep within 80 chars wide
Bad:
$ret = $this->callSomething(array('xxx' => 'yyyy', 'zzz' => 'aaaa' ....Good
$ret = $this->callSomething(array( 'xxx' => 'yyyy', 'zzz' => 'aaaa' .... )); - Control Structures
- If / else
- No short ifs (eg. always use brackets)
- do not use else if you have returned or used break; within
a block.
if ((condition) || (condition2)) { statement; statement; } else { statement; } if ((condition) || (condition2)) { statement; statement; } elseif ((condition) || (condition2)) { statement; } // for long if statements if ((condition) || (condition2) //8 space indent || (condition2) || (condition2) ) { // closer and opener 4 space indented statement; statement; } - break out or return rather than nesting if statements
When inside methods or loops, use return or break, rather than have heavily nested if blocks
for ($i = 0; $i < 100; $i++) { if (!$x) { break; } if ($x < 10) { statement; statement; break; } ...... } function get($arg) { $do = DB_DataObject:;factory('xxxx'); if (!(int)$arg || !$do->get($arg)) { return HTML_FlexyFramework::factory('Error/404', array('errors'=> .....); } ..... - BAD:
function get($arg) { $do = DB_DataObject:;factory('xxxx'); if ($arg != "") { if ($do->get("id",$arg)) { ... do stuff ... } } } - Switch Case
- indent once for case, and once for statements
- extra line after break; except on last one.
switch ($x) { case 1: statement; break; case 2: statement; break; default: statement; break; }
- Method Calls
- No space between method name and ( and first paramenter
- space after each,
- avoid more than 2 arguments to a method (use named arguments)
- dont over indent arguments
- commas after argument, not before
- extra space can be added before return assignment to
enhance readability
$ret = $this->callSomething($a, $b); $ret = $this->callSomething(array( 'xxx' => 'yyyy', 'zzz' => 'aaaa', )); $abcdefg = $this->find(); $xxx = $this->fetch(); - Method Definitions
- first { is on line after definition
- return from method sooner rather than later
function callSomething($a, $b) { if ($a) { return false; } statement; } - return should not use () brackets around return values
- Class Definitions
- first { is on line after definition
class myclass extends anotherclass { function .......() { } }
- Comments
- Do not commit commented out code
- Method / Class header Comments use /* */ style comments
- Class Properties can use docbook comments or // comment after
the defintion
class something { var $fred; // this is fred's var }Longer comments should use /* */ style docbook comments
- Methods (unless FlexyFramework get()/post()/getAuth()) require standard phpdoc comment definitons (detailing arguments) preferably with simple examples of usage.
- Comments inside of Methods should use // style comments to
enable commenting out blocks of code for debugging using /* */ style
comments
- # bash/perl style comments should not be used.
- Include / Require
- include / require_once etc. should not use ( ) around arguments
- do not attempt to cope with require_once failing (by hiding
it or testing include path)
include 'MyClass.php
- Always use full relative path rather than short path
require_once 'MyProject/MyClass.php'; //rather than require_once 'MyClass.php'
- PHP Code tags
- Do not use short tags (<?)
- Do not add the closing tag to the end of files (?>) as it often can
break sessions
- Naming Conventions
- Classes
- use Caps_First with _ between names. (exceptions may occur when part of the name relates to an extenal object - eg. a database table)
- Good: Some_MyProject, Some_Project_Driver
- Bad: SomeProjectThatWorks, SomeProject_ProjectDriver
- Underscore heirachy should relate to extends pattern where feasible
- Fred_Driver extends Fred
- Fred_Driver_Postgres extends Fred_Driver
- Libraries should always use a type prefix (eg. HTML_/Net_/DB_ etc.)
- Projects should always have a non-generic name, and all classes should extend that (see later)
- Functions
- DO NOT USE!
- Methods / Variables
- Always use studlyCaps with first letter lower, and latter ones upper case.
- Good: someMethod(), $this->someVar;
- Bad: some_method(), SomeMethod(), $this->some_var
- Prefix private methods with underscore (these may use _ in names as well.
- _my_private_method()
- $this->_my_private_var;
- Avoid pass-by-reference, unless absolutely necessary.
- Constants
- Built in constants should use lower case
- eg. true, false, null
- User Defined constants should be associated with a class
and all uppercase
- eg. MYPROJECT_TESTFLAG
- Global Variables
- Should always be associated with a class
- Should not be accessed from outside the owning class.
- Should be initialized when the class is loaded (in global
space)
- File Formats
- Should use UNIX line endings (\n) not (\r\n)
- Class inheritance
- Avoid to many levels of extends, in general code (above 3
should be extremely rare, and well justified.)
- Require / Lazy loading
- Utilize Lazy loading, avoid requiring files at the start of each class/page file.
- Avoid long lists of require_once's at the top of each file.
- Avoid assuming that a class is loaded, help the end user out by
explicitly saying where some class comes from.
- Mixing PHP + HTML/Data
- Avoid embedding large quantities of data in PHP code. Use JSON/INI or simple text files if possible
- Avoid where possible echo'ing HTML or text from within classes. Always prefer adding to the Templates any HTML
- Exceptions may occur when dealing with HTML/XML Trees.
- Only use "echo", not "print" (as this makes checking for xss vectors simpler)
- Avoid placing validation messages in the PHP code, (This
excludes system failure type messages), as this makes doing translated
templates with user error messages easier.
use
$this->warning['messagename']= true; and in the HTML: <p flexy:if="warning[messagename]">Display the warning / error</p>
- Method Arguments
- Maximum of 3, over that, use named arguments array.
- Prefered Max is 2 arguments.
- Avoid pass by reference, unless absolutely necessary (it makes
code more difficult to follow) - return arrays or associative arrays.
- XML / HTML
- use the DOM / SimpleXML extensions
rather than XML_Tree/XML_Parser.
- DataObjects:
- reusable chunks of database specific code should go in the DataObjects.
- Favour moving joinAdd constructs into the DataObject for
reuse later or optimization, by manually modifying the
$this->_joinAdd variable.
- DataObjects should be initialized using DB_DataObject::factory(....)
- DataObjects should not make direct use of input variables (eg. $_REQUEST/$_POST/$_GET etc.)
- Do not use cachedGet()
- For frequently called gets/fetches, consider defining a cached() method.
- return value for $do->get() can be considered to be boolean, and tests should look like this:
if(!$do->get($id)) { $this->error = .... return HTML_FlexyFramework::run('Error/404'); } - $do->get(), can work out the primary key, so do not use it with $do->get('id',$arg);
- use $do->find(true) when fetch a single result, (rather than using $do->find() / $do->fetch();
- user $do->escape($data) on all data going to whereAdd() or similar methods, where the data can not be trusted (eg. user input/reading from a file.)
- FlexyFramework
- Implement and use Page base classes
- A project one (non-authenticated) eg. MyProject extends
HTML_FlexyFramework2_Page
- A authenticated one, eg. MyProject_Authenticated extends MyProject
- Page Classes should avoid deep directory trees:
- MyProject_Products_Search_Edit (MyProject/Products/Search/Edit.php) == BAD
- MyProject_ProductsEdit (MyProject/ProductEdit.php) == GOOD.
- Page Constructors
- __construct or class constructors should not be used (it makes the code to difficult to follow)
- Unused shared base classes (eg. MyProject_SomePage extends
MyProject_SomePageBase)
- Avoid these, merge the code into an existing class (either the project one or a relivant page)
- Avoid calling parent::get() or parent::post(), make explicite methods for actions, and call them specificily in child classes. (This enables top level get/post to return 404 errors on pages not found)
- Do not use the modules toolkit - it was a bad idea, and is only ther for Backwards compatibility
(you should write your own project based loadModules routine, that loads any modules you intend to use) - Do not make error handling a module Errors should be handled
with flags (or values with context data, not messages) and be part of
the page template
- Emailing
- Should use standard Email template code, and not hard code Email into PHP. (see Flexy tricks)
- Be careful of data injection in to Email templates
From: { t.someemail } <test@xyz> Subject: A test .....In the above example ensure that { t.someemail } does not have line breaks comming from user input!
- Errors
- Do not use @ to suppress errors unless absolutly necessary.
- in PHP4
- use PEAR::raiseError(), lazy loaded.
- consider outputtting debuging information via
trigger_error(E_USER_WARNING) (when debugging is turned on.)
- in PHP5,
- trigger_errror() is for programming errors / debug warnings.
- Exceptions for everything else (Probably PEAR_Exceptions)
- Try and catch all exceptions where they are thrown. - try and avoid global catchalls.
- use function arguments to determine if Exceptions should be
converted to return values, or run
HTML_FlexyFramework::run('error',array('errors'=> array('problem_with_xxx'=>true)));
- Constants
- Should not be used as configuration variables
- Normal usage should be to assist in the readability of code,
normally in cases where a set of numerical states can be represented by
a textural word (eg. SMTP_STATE_READY_TO_SEND = 1)
- Naming should follow PEAR standards (eg. with class name
prefixed - or in PHP5 just use class constants.)
- Configuration
- Most configuration is automatically detected by the Framework, however other configuration settings may originate from
- bootstrap (index.php) hard coded into the Framework constructor
- bootstrap (index.php) may choose to load a .ini fire to help building the constructor
- FlexyFramework's ConfigDir is depreciated.
- If you do use configuration files, load them in the
bootloader, and pass them as an argument to the
HTML_FlexyFramework::factory() startup, eg.
$siteconfig = parse_ini_file('/etc/flexyframework/myproject_config.ini'); HTML_FlexyFramework::factory(array( ...... MyProject => $siteconfig, )); - Read Access
- Global configuration is available by:
$opts = HTML_FlexyFramework::get()->classname;
- DO NOT write or alter the global configuration.
- Sessions
- All sessions should be stored associated with the class they
are used in, and try not to access them directly from outside the
class.. eg,
class MyProject_TestPage extends MyProject { function get() { $_SESSION[__CLASS__]['settings'] = .......
- Debugging
- at present the recommendation is to use a debug flag on a
class, and trigger_error with E_USER_WARNING. this can be caught by the
error handler if neccessary and reformated.
Javascript
- all js files go in [templatedirectory]/images/js/ (unless using Roo Pman system)
- This enables the Flexy url-rewriter to fix src locations.
- Pman Project specific
- Files go in module directory, filename matches class name eg. Pman.Tab.XXX goes in Pman.Tab.XXX.js
- At present Filename/Classnames start with Pman.Tab.* or Pman.Dialog.* depending on what the class deals with
- Library components still go in images/js/ - usually are extensions of a Roo element.
- functions should only be used on the page that they are called.
- Therefore, no functions should appear in .js files
- Included js files should be classes.
- Classes are of three types
- Static classes Eg. for layouts or Dialogs (less so now)
- Implementations of Roo.Observable, created as follows
Pman.Dialog.XXX = new Roo.Observable({ xxx : function () { .... }, yyy : 1 }); - Classes which can be extended - these should use Roo.extend() syntax and have a constructor.
- Code should, in general, follow pear standards (re indentation
etc.)
- Global variables
- should be avoided where possible - Tie a global resorce to a static class, or class instance
- Strings
- Use single Quotes for non-translatable strings
- Use double Quotes for strings that require translation
- Class names should use the Project(eg. Pman), Type(eg. Dialog), Module name (eg. Builder), then the specific name
- eg. Pman.Dialog.BuilderDatabaseModelView
- names like Q3, ERM are bad, BuilderQuizType3 is better
- Avoid generic Class names, eg. 'Util',
- Should assume availablity of baseURL and rootURL will be provided
by
<flexy:toJavascript baseURL="baseURL" rootURL="rootURL"/>
- Javascript included .js files should avoid running code directly, they should be initiated manually by the page.
- BAD = .js file that modifies the whole layout when it loads
- GOOD = registering modules with Pman, or implementing the Roo.Document.on('load', ... event)
- For AJAX transactions with PHP use the Roo.Ajax.request(), or Pman.request()
- (PHP/Pman project) Use the the method $this->jdata(), $this->jok(), $this->jerr() to send back data
- avoid using eval(), unless absolutely no good alternative exits (see. Roo.decode())
General Coding (PHP / js)
- Avoid mapping names (especially databases columns to other names)
- BAD : this.tbx0 = document.getElementById('username');
- GOOD : this.username = document.getElementById('username');
- EVEN BETTER this.elements.username = document.getElementById('username');
- This can then be automated by for[each] loops...
HTML (and Flexy Templates)
- Indent Code with 2 spaces
- Try and keep to less than 80 characers per line
- Layout Tags 1 per line etc.
- Example:
<div> <table> <tr> <td>XXXX</td> </tr> </table> </div> - Prefer CSS over Tables (or keep table usage to a minium) - use class="xxxx" rather that id="xxx" to enable reuse of styles
- except when really representing tabular information.
- Generally Avoid usage of absolute positioning of CSS.
- (this can get messy when reflowing with javascript)
- Use { baseURL } and { rootURL } in links / post locations
- Use the images/ folder for images, so the Template engine can rewrite the url.
- Break complex pages into two templates and duplicate HTML rather
than conditionally displaying large chunks,
eg. BAD:
{ if:step1 } ......... { end: } { if:step2 } ..... { end: } ........
XUL
- do not use commandset/command, use oncommand with javascript call.
CSS
- Should not in general be embeded into HTML file.
- Author / Copyright should be at top of file once, not in multiple places
- Do not use non-english characters in comments.
- should be located in [templatedir]/images/css/ and referenced by href="images/css/....." so the url can be rewritten by the template engine.
- should be indented as follows
.stylename { display: block; font-color: red; }Note the large indentation on the definitions. - Should be split into seperate files depending on their purpose (eg. leftnav.css / topnav.css etc.)
- Should be carefull when redefining standard HTML elements, it is
prefered to use a base style, and refer to subelements: eg.
.mybody h2 - Should generally avoid using #id - prefer classes
SQL
Variable names (Stored procedures)
Input arguements : i_***
Local Variables : v_***
Cursor Variables : c_****
Records : r_****
Cursor Names : cursor_****
Indent
- 4 spaces
- Indent the body, eg. everything inside of 'BEGIN'
- Statements that span more than one line, every subsequent line should be indented
Alignment
- a sequence of A = B, A= B... the right hand side should be space apart so that it aligns
eg.
invbuy_transdate = new_inv_buy.transdate,
invbuy_qty = new_inv_buy.qty,
invbuy_totalcost = new_inv_buy.totalcost,
variable type alignment- types should be seperated from variable name.
v_trans_qty numeric(18, 6);
v_new_invhist_id integer;
Case
- Use uppercase for SQL keywords.
Line length
- try and keep it under 80 characters
WHERE conditions
one compare per line.
AND / OR keywords on seperate lines
() brackets - on there own line
Wrap groups of AND/OR with () brackets do not rely on operator precidence
INSERT VALUES
max two columns per lines
add extra line break after each pair of lines
values should match layout
Comments
As much as possible.
Follow us
-
- Migrating off Netsuite - The hidden cost of Clouds..
- Javascript Templating, AngularJS and Roo.XTemplate
- Roo.XComponent introduction
- Roo J Solutions Limited is recruiting
- Free your data... seed webkit browser mirror button
- Deleting the View and Controller..
- What was I doing last night... Seed querying xscreensaver
- Watch-out PHP 5.3.7+ is about.. and the is_a() / __autoload() mess.
Blog Latest
-
Twitter - @Roojs
