RADICORE programming guidelines

Here are a few guidelines if you wish to start developing with the RADICORE framework.

If you wish to create a new project/application to run under the RADICORE framework then you must first create a new subdirectory to contain all all the files for the new project. Please refer to How do I start a new project with RADICORE?

Database design tips

Before you can build components to manipulate the contents of your database tables you must first construct your database. Here are a few tips:

  1. Make sure the database is properly normalised. Pease refer to The Relational Data Model, Normalisation and effective Database Design for details.
  2. Use only the characters A-Z, 0-9 and '_' (underscore) in any database, table and column names. Any other characters would require all names to be quoted in every SQL statement, and this is more effort than it is worth.
  3. SQL is totally case-insensitive, so don't waste time in using any particular case for your database, table and column names. All names will be shifted to lower case within the framework anyway, so anything else will be ignored. In particular this means that CamelCaps, as in "FirstName", should NOT be used. I prefer an underscore separator, as in "first_name". For more details on this please refer to Case Sensitive Software is EVIL.
  4. Do not use special prefixes to identify databases, tables, columns, primary keys, candidate keys or foreign keys. They have absolutely no meaning in any DBMS, and have absolutely no meaning in RADICORE.
  5. Database names should be short but meaningful, as documented in database names.
  6. Table names should be short but meaningful, and preferably singular, as documented in table names.
  7. Column names should be short but meaningful, as documented in field names, and help identify their content. Wherever possible fields with the same content should have the same name, and fields with different content should have different names, as described in Field names should identify their content.
  8. Do NOT use the name ID for every primary key field, for reasons which are documented in primary keys. Do not automatically use a technical primary key on every database table without first reading Technical Keys - Their Uses and Abuses.
  9. Foreign keys, wherever possible, should be given the same name as the primary key to which they are related, for reasons which are documented in foreign keys.
  10. If you wish to employ a method of obtaining unique sequence numbers please take a look at generating unique ids first.
  11. It may be a good idea to include a prefix on each table name which helps identify the application subsystem. For example, in my MENU database all tables are prefixed with "mnu_". This will be of use in those situations where there is a restriction on the number of different databases which can be created, in which case you will have to merge the tables for different application subsystems into a single database. If two different tables have the same name you immediately have a big problem, but having a different prefix will circumvent this problem.
  12. Note that Radicore uses a different database for the MENU, DICT, WORKFLOW and AUDIT subsystems, so you will need at least one more database for your own application. There is no facility to merge all these into a single database as the database names are hard coded in numerous places.
  13. The RADICORE framework does not use database stored procedures, database triggers or database constraints. The database is a place for storing data, not application logic. All business rules are best defined within the application code.

PHP programming tips

  1. RADICORE works with register_globals and magic_quotes_gpc both turned OFF. Directives in the .htaccess file override anything which is set in your php.ini file.
  2. RADICORE works with default_charset set to "UTF-8" which means that data containing accented characters can be used without requiring the multi byte string functions to be enabled. Again there is a setting in the .htaccess file which overrides anything which is set in your php.ini file.
  3. Session variables - these are all referenced through the $_SESSION array and not via session_register.
  4. The RADICORE framework works in both PHP 4 and PHP 5. Where there are differences they are enclosed in user-defined functions and held in separate files. For example, when running with PHP 4 the framework uses the DOM XML and Sablotron XSLT extensions which are accessed from within file include.xml.php4.inc, but when running with PHP 5 the framework uses the DOM and XSL extensions which are accessed from within file include.xml.php5.inc. The framework then loads in the correct file with the following code:
    if (version_compare(phpversion(), '5.0.0', '<')) {
        require 'include.xml.php4.inc';
    } else {
        require 'include.xml.php5.inc';
    } // if
    
  5. Regular expressions - the RADICORE framework uses a mixture of the ereg* and preg* functions. There is an ugly rumour that the ereg* functions amy be phased out in PHP 6, so I may convert them. Where the pattern starts to be complicated it is a good idea to split it up into separate parts which can be properly commented, as in the following:
    $pattern = "/"                      // start pattern
             . "^[a-z0-9_-]+"           // valid chars (at least once)
             . "(\.[a-z0-9_-]+)*"       // dot valid chars (0-n times)
             . "@"                      // at
             . "[a-z0-9][a-z0-9-]*"     // valid chars (at least once)
             . "(\.[a-z0-9-]+)*"        // dot valid chars (0-n times)
             . "\.([a-z]{2,6})$"        // dot valid chars
             . "/i";                    // end pattern, case insensitive
    
    This makes it a LOT easier to understand than:
    $pattern = "/^[a-z0-9_-]+(\.[a-z0-9_-]+)*@[a-z0-9][a-z0-9-]*(\.[a-z0-9-]+)*\.([a-z]{2,6})$/i";
    
  6. PHP tags - the RADICORE framework does not use short tags. The RADICORE philosophy is to do things properly in long-hand instead of trying to make false economies with piddlng little short-cuts.
  7. Control structures - it is possible to define these either with or without curly braces. RADICORE uses curly braces, as in if () { ... } and not the if (): ... endif; alternative. This is because the vast majority of PHP programmers follow this convention, and to switch between the two would be annoyingly inconsistent.
  8. Indentation - statements following conditions should always be indented so that the <begin> and <end> statements start at the same column position while the <body> is indented by 1 tab position (or 4 spaces). The following is acceptable:
    foreach ($newarray as $item => $value) {
        if (array_key_exists($item, $oldarray)) {
            if (stripslashes($value) == stripslashes($oldarray[$item])) {
                unset ($newarray[$item]);
            } // if
        } // if
    } // foreach
    
    While the following is NOT acceptable:
    foreach ($newarray as $item => $value) {
    if (array_key_exists($item, $oldarray)) {
    if (stripslashes($value) == stripslashes($oldarray[$item])) {
    unset ($newarray[$item]);
    }}}
    
  9. Tabs or Spaces? If tabs are used they can be interpreted differently by different editors. To obtain the best of both worlds RADICORE was written using an IDE that allows tabs to be input, but which are automatically converted to spaces (1 tab = 4 spaces).
  10. One line conditions. Do NOT use them unless it is to execute a simple return statement.
    if (condition) return;
    
    is acceptable, but anything more complicated is not.
  11. error_reporting - should be set to E_ALL in the development environment, with display_errors and log_errors also set on to help track any errors.
  12. Strings - use single quotes unless there is a good reason to use double quotes (string contains variables or \n) as it gives PHP less work to do. String literal keys in array statements should always be contained within single quotes, as in:
    $foo = $bar['foobar'];
    
  13. Short-hand structures - do not waste time trying to type commands with as few keystrokes as possible in the mistaken belief that it is more efficient. When you consider that more time is spent in READING code than WRITING it, it is more important that the code is actually READABLE. Using short-hand notation defeats this objective and is therefore to be avoided. It is for this reason that the RADICORE framework does not use such constructs as the ternary operator.
  14. Comments/Documentation - code which is not well commented shows a lack of consideration for others. Those who say that PHP code is so easy that it is self-documenting are showing a lack of experience. It may be easy to say what a piece of code does, but not so easy to say how it fits into the current context. Comments are usually put immediately before a line of code, as in:
    if (!is_string(key($array1))) {
        // indexed by row, so use row zero only
        $array1 = $array1[0];
    } // if
    
    or may be put at the end of the line, as in:
    if (!is_string(key($array1))) {
        $array1 = $array1[0];  // indexed by row, so use row zero only
    } // if
    
  15. Comment style - PHP provides several different ways to write comments:
    // single line comment 
    
    # another single line comment 
    
    /* multi line comment 
       multi line comment */ 
    
    Switching between different styles looks messy and inconsistent, so RADICORE uses nothing but:
    // single line comment
  16. Functions and Methods - names may be defined using either an underscore separator, as in:
    function do_something_clever ()
    
    or with the first letter of each word in upper case (known as CamelCaps), as in:
    function doSomethingClever ()
    
    Descriptions should be placed after the function name and before the first line of code, as in:
    function getChanges ($newarray, $oldarray)
    // compare two arrays of 'name=value' pairs and remove items from $newarray
    // which have the same value in $oldarray.
    {
        // step through each 'item=value' entry in $newarray
        foreach ($newarray as $item => $value) {
            // remove if item with same value exists in $oldarray
            if (array_key_exists($item, $oldarray)) {
                if (stripslashes($value) == stripslashes($oldarray[$item])) {
                    unset ($newarray[$item]);
                } // if
            } // if
        } // foreach
    
        return $newarray;
    
    } // getChanges
    
    Note also that each closing brace is followed by a comment which identifies the construct which is being closed.
  17. Documentation using phpdoc - there is a universal assumption that any documentation produced by phpdoc (which is derived from javadoc) is automatically acceptable. However, in my experience all documentation produced by this method has been bland and sterile, full of facts but lacking in useful information. For that reason RADICORE does not use phpdoc.
  18. Alignment of '=' - where there is a series of statements which perform assignments it always looks better if the '=' appears in the same column. So instead of:
    $foo = 'foo';
    $foobar = 'foobar';
    $stuff = 'stuff';
    $morestuff = 'morestuff';
    $rubbish = 'rubbish';
    
    try this instead:
    $foo       = 'foo';
    $foobar    = 'foobar';
    $stuff     = 'stuff';
    $morestuff = 'morestuff';
    $rubbish   = 'rubbish';
    
    Code which looks clean and tidy shows that the author has a clean and tidy mind, and a clean and tidy mind generally produces better code than that of an untidy and disorganised slob.
  19. Line width - although it is possible to write lines of infinite length they become unmanageable in the viewing area of most IDEs. The simplest solution is to restrict line length to a maximum of 80 characters. This can easily be achieved using PHP's concatenation facilities, as in:
    $query = 'SELECT user_id, user_name, start_date, end_date, is_disabled, pswd_count, '
            .'pswd_chg_date, language_code, email_addr, role_id '
            .'FROM mnu_user '
            .'LEFT JOIN mnu_role ON (mnu_role.role_id=mnu_user.role_id)'
            ."WHERE user_id='$user_id'"; 
    
  20. Global variables - although it is good practice to pass variables in and out of functions by using function arguments, there are a small number of variables which are used by the framework which may be of use within any other function. It would be just too messy to pass these around as separate function arguments, so it is permissible to address them using:
    $GLOBALS['foobar']
    
  21. Variable names - use meaningful names wherever possible, and only use single-character names for temporary variables. Always use lower case with the underscore ('_') separator between each word. Do NOT use CamelCaps and UNDER NO CIRCUMSTANCES use Hungarian notation!
    $variable_name         // is good
    $varName               // is bad
    $sVarName              // is EVIL!
    

Published: 21 April 2006