Style Guide & Coding Standards for PHP

last revised March 6, 2011
effective starting March 6, 2011


Contents

Introduction
Overview
Comments
File Naming Conventions
Constants
Variables
Functions
Blocks
Output Functions
Security
Error Handling
Modules
Libraries
Sample Script


Introduction

This document describes source code standards for PHP scripts, both as standalone command line scripts and as Web page scripts, developed at Cliffson Solutions, LLC. Unless superseded by client requirements, these standards apply to all software developed by employees and subcontractors of this company for both internal use and client projects.

The purpose of these standards is to bring consistency to the structure of PHP scripts, including formatting and naming conventions. The PHP interpreter allows a large latitude in how the source code is organized. Although this flexibility has benefits in requiring only a relatively few, simple grammar rules, if some rules for structure and convention are not applied, scripts can become very difficult to read and maintain.

Most clients only care if the code does what it is designed to do, not how it is formatted. However, the practical consideration is that well-formatted, well-documented, and well-organized code is considerably easier to maintain and enhance, saving both the client and developers time and money in the long run. Given that a full-blown application (not just a one-use throw-away utility) may require on average as much as four times the original development time in debugging, maintenance, and feature tweaks over its lifetime, an investment in creating good source code initially is easily paid back in the long run. Moreover, applying some discipline to how the code is organized, particularly in variable naming conventions, tends to encourage better logic and fewer side-effects and bugs.


Overview

We readily acknowledge that many of the conventions described here are strictly personal preferences and that coding formats and naming conventions can ignite religious debates on par with the original Crusades. However, the conventions described here have been developed based on years of writing code and noting what works and what does not. In some cases these may result in a bit more typing, but any programmer worth his/her salt will 1) already be a decent typist, and 2) realizes that clear coding can save at least an order of magnitude in time trying to find functional blocks and in debugging.

Note that although this document discusses PHP scripts which create Web pages and conventions of PHP coding relative to HTML coding, the corresponding documents Style Guide & Coding Standards for HTML and Style Guide & Coding Standards for Javascript should also be consulted.

PHP code is always delimeted using <?php ... ?> tags. Alternative tags should not be used for maximum portability, and all Apache instances at Cliffson Solutions are configured to ignore the alternative tag conventions.

We have adopted a slightly modified Allman-BSD format for block formatting, the slight modification being two-space indents instead of the traditional four spaces (two spaces are sufficient to visually indicate blocks without using an inordinate amount of horizontal real estate). This means that both open and close curly brackets that delineate blocks appear on lines by themselves. Although the starting curly bracket works just as well at the end of the line preceeding the block, putting the opening bracket on an extra line by itself is well-worth the improvement in readability. See the section on Blocks below for more details.

We also use one-letter prefixes on all variable names to indicate their types. Several other naming schemes and even other programming languages also do this, and the benefit to keeping track if a variable is a pointer or an integer inherently in its name is well-worth the time taken to type an extra letter each time it is referenced. Although PHP automatically typecasts variables as needed, depending on context, in most cases a variable that starts as one type stays that type, and the type-prefixes still add a lot of value in keeping variables straight.

Moreover, such variables can be easily tied together by their base names: $sFile is the name of the file referenced by the handle $hFile, and $iFile is a character or line counter for the contents of that file. keeping track of multiple indices in nested loops is not nearly as difficult if each index is named after its use. See the section Variables below for more details.

All code is written assuming a 80-column display. Long lines that are longer than 80 characters must be broken into multiple lines at the logical locations. For example, a long printf() statement with multiple arguments may be broken into multiple lines at any comma separating those variables, or if necessary by using the cat operator (.) to break a single string across two or more lines. See the sections Blocks and Output Functions for more information.


Comments

PHP allows the use of both /* ... */ and // as comment delimeters. Because the /* ... */ is easily applied to disable large chunks of code, we reserve its use for that purpose and encourage the use of multiple asterisks to draw attention to it. The // prefix is used to initiate comments, with each comment line requiring that prefix.

Comments that start with // are indented to the current level of code indenting.

Example:


          function fnTest($itest)
          {
            // this is a test function that returns the square of the 
            //   supplied int if error, return NULL; otherwise return 0
   
            // sanity checks

            /*****************************
            /* disabled for now
      
            if (empty($itest) || ($itest < 0))
              return NULL;
            *****************************/

            return $itest * $itest;
          }
        

File Naming Conventions

All directly invoked PHP scripts are named with .php file extensions. Helper files, such as those that contain database credentials, common utility functions, and global constants, are all named with .inc extensions. These extensions indicate the files are not be called directly. Moreover, the company's Apache Web servers are configured to block direct access to .inc files, providing a bit of security and encapsulation of implementation details.


Headers

We define three types of headers: headers at the top of all script files, headers that separate major blocks of code, and function headers.

File Headers:

All script files, including both .php and .inc files, are required to contain file headers. The lines of these headers are delimeted with double slashes (//), and must contain the following:

  • file name, including relative path if in a subdirectory of the main project directory
  • the purpose of the file and the code it contains
  • dependencies on other files, if any
  • expected input and output, if the file contains main()
  • the original creation date, author, and affiliation
  • the last revision date, author, and affiliation

An example of a header for a two-file project:


    //////////////////////////////////////////////////////////////////////////
    //  test.php                                                            //
    //                                                                      //
    // a program to demonstrate bubble sort algorithm; demonstration is     //
    //   based on constants defined in test.inc, and requires no user input;//
    //   any command line parameters entered are ignored; output is text    //
    //   to stdout; running this program invokes two bubble sort functions, //
    //                                                                      //
    // Written 2/27/2011, L.Pritchett, Cliffson Solutions, LLC              //
    // Revised 2/27/2011, L.Pritchett, Cliffson Solutions, LLC              //
    //////////////////////////////////////////////////////////////////////////
 
        

The corresponding helper file:


    //////////////////////////////////////////////////////////////////////////
    //  test.inc                                                            //
    //                                                                      //
    // common include file that contains global constants and utility       //
    //   functions for test.php; refer to test.php for addtional comments   //
    //   and descriptions                                                   //
    //                                                                      //
    // Written 2/27/2011, L.Pritchett, Cliffson Solutions, LLC              //
    // Revised 2/27/2011, L.Pritchett, Cliffson Solutions, LLC              //
    //////////////////////////////////////////////////////////////////////////

        

Dates should be in the format shown above, with four-digit years. Blank lines are used to improve readability.

During initial development of the code, if only a single programmer is making changes, a single revision line with a current date is sufficient. If more than one programmer is working on the same file, each programmer should have a separate revision line, with the latest revision line always appearing last. Details as to the types of revisions made are not required, as during a rapidly evolving project those types of notes should be entered into the project management system or added as the code is checked back into the software repository.

Once the code has gone into production, the last pre-release revision lines should be left intact and new revision lines should be added. At this point each revision line should include summary comments as to the changes made.

Section Headers:

Section headers are simply comment blocks that delineate major blocks of code, including:

  • require() and require_once() statements
  • define() statements
  • declarations of global array constants
  • functions
  • inline code which represents the main program

These are used primarily as search aides, as well as to prevent the different blocks from simply flowing into one another. To make them stand out visually, they include a long line of = characters to cover the width of the code.

Example:


    //=========================================================================
    // global constants
    //=========================================================================

        

If appropriate, subheaders may also be used, built from + characters. Subheaders may be useful if a large number of functions are defined but may be grouped by type.

Example:


    //=========================================================================
    // functions
    //=========================================================================

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // input functions
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        

Function Headers:

Function headers are basically the comments that start each function definition. Unless the function is obvious and trivial, these headers are required immediately after the curly bracket starting the function definition and and before the local variable declarations (or first line of code, if no local variables are used).

These headers should include the purpose of the function, expected inputs, return values, and the behavior upon encountering errors. If any of the supplied values are passed by reference and are modified by the function, this must be noted here as well.

Example:


    function fnSquare($icount)
    {
      // accept positive integer and return its square; if variable is invalid,
      //   call error handler, which halts script
    
      // sanity checks
  
      if (empty($icount) || ($icount < 0))
        fnErr_Msg("invalid parm passed to fnSquare()");

      return $icount * $icount;
    }
        

Constants

Scalar constants should be defined using define() statements. If the entire program is contained in one file, these statements should appear immediately after require() or require_once() statements at the top of the file. If multiple source code files are involved, all define() statements should be collected in a master include (.inc) file.

So-called "magic numbers" (numbers that are basically constants) should not hardcoded into functions, but should be collected as a set of global define() statements. The rationale for this is facilitate future updates if those values change: rather than search for all possible references to a particular value, it can be changed in one place. This should be done not only for numbers which can reasonably be expected to change over time (e.g., number of employees), but also for all other situtation-dependent values even if those are not anticipated to change (e.g., number of vice presidents) – because at some point they may.

All constants should include the same one-letter lowercase prefixes used in variable names (see "Variables" below), but the rest of the names are in capital letters. All constant names must be descriptive and meaningful. Abbreviations are allowed for the sake of typing and space efficiencies, but the overall name must be clear and not cryptic. Because mixed case is not allowed in constant names, underscores should be used for readability.

Examples:


    define("sPGM_NAME", "test_program");
    define("fPI", 3.14159);
    define("iEMPLOYEES", 334);

        

Constants that are defined in standard PHP libraries may be used as is, or they may be remapped by define() statements to create constant names with local naming conventions.


Variables

Using global variables to share values across multiple script files or declared in functions as global should be avoided if all possible. PHP has fairly decent scope enforcement to prevent unintentional alterations to variables outside the immediate scope, and those should not be bypassed. Functions that need access to variables should be passed those variables as part of the function call. This includes both numerical values and handles of various types. Constants must be defined in a single location (see Constants above) and are automatically available globally.

An exception to the above rule is using $_SESSION variables, which may be used to track state information on persistent Web sessions, particularly those requiring user authentication. However, the data stored in this array should be minimal, should never include sensitive data such as plain text passwords, and should be destroyed at the end of the session. All session array elements should be named with a prefix that is unique to the current script to avoid collisions if two scripts using session variables are active at the same time. See Security for additional rules for $_SESSION variables.

Example:


    define("sSESSION_PREFIX", "pubs_");

    if (!empty($_SESSION[sSESSION_PREFIX . "AuthToken"]))
      ...

        

All variables should be named with descriptive and meaningful designations. Abbreviations are allowed if and only if the overall name is not cryptic. Variable names which are comprised of acronyms are allowed only if the acronyms are commonly recognized (e.g., url, ssl, html); otherwise, a short descriptive name must be used. The only exceptions to these rules are simple loop counters (e.g, $i, $j, $k). However, nested loops should use more descriptive counters to make the loop logic more self-documenting.

All variables must include a one-letter prefix that indicates the variable type or use. These are defined as

i = integer
f = floating point number
s = string
c = char
b = Boolean (although this is defined as an int, its use is specific)
h = handle (including memory, file, database query results, and process handles)
a = array

If global variables must be used, they must include at least one capitalized letter after the one-letter type designator. All variables in the main inline section of the script also follow this convention, as they are automatically global. All variables defined in a function, either explicitly or as formal parameters, are always lowercase. Mixed case names (aka, CamelCase or camelBack notation), underscores, or a mixture of the two should be used as necessary for readability. Adhering to these conventions will help easily identify the scope of a given variable at a glance.

Examples:


    $iPi                         [global integer or integer used in main()]
    $fTotal                      [global float or float used in main()]
    $sFirst_Name                 [global string or string used in main()]
    
    for ($i = 0; $i < 10; $i++)  [okay for simple loop]

    function fnTest($icount)     [integer formal parameter in function prototype]
    $ftotal                      [float defined in function]
    $surl                        [string defined in function]

        

All variables must be explicitly initialized before use. If the state of a variable is not known (as for a specific $_POST parameter), the function empty() should be used in preference to isset(), as the latter generates a log file message when applied to an uninitialized variable, whereas empty() does not. However, empty() also evaluates as true if the variable exists and contains NULL, 0, or an empty string, so some care is required when using it in conditions where those values are possible.


Functions

All function names are prefixed with fn, followed by a capital letter, followed by mixed-case characters, numbers, and/or underscores. This naming convention is consistent with using mixed-case names for global variables, as functions are typically global in scope. All function names must be meaningful and descriptive, and should avoid acronyms that are not commonly known. For example, fnURL_Parse_String() is okay, but fnGDIC_ADC_Handler() is not particularly descriptive. Functions which may be used in conditional statements should be named consistently with the values they return.

Example:

 
    if (!fnFile_Exists())
     ...

        

Function parameters which are optional must appear at the end of the function parameter list in order of decreasing likelihood of use. This avoids unnecessary placeholders in order to access commonly used parameters. Optional parameters should always be assigned default values.

Functions may be defined to be local within other functions by referencing the appropriate include (.inc) files only within the functions that need them. However, globally including these is just as easy in most cases.

All functions should validate their incoming parameters, both that the parameter is not NULL (unless this is a valid possibility) and that values are within expected ranges. For example, if one of the parameters is the maximum length of the string to which a value is to be copied, that integer cannot be NULL, nor can it be zero or a negative number. Just because the function is a library routine and will never be called outside the current program is not sufficient reason not to validate function parameters. Bounds checking should be applied to final results before being returned as well. See Error Handling below.

Note that checking function parameters is required regardless of the use of default values that may be defined in the function parameter list. These default values apply if a parameter is not passed in that position by the caller. A parameter that is passed but is the wrong type or invalid value still must be detected and handled. Because of PHP's loose type checking, these checks are even more important than with other languages like C that provide strong type checks against function prototypes.

Unlike C and some other programming languages, PHP functions gracefully handle input values of arbitrary lengths and not easily subject to buffer overflows. However, checking for maliciously large values against reasonable upper limits is a good precaution. Such checks are required if the value will be passed to an external program that does have length limitations (such as a compiled C utility) or to a database field.

Functions may return single values of any type, depending on what they are intended to do. Functions which return Boolean values may return either true or false, depending on what makes sense with the name of the function. For example, fnFile_Exists() would return true if the specified file is found, but fnFile_Missing() would return false if the file is present. This demonstrates the importance of picking meaningful function names, particularly if those are used in conditional statements (if, while, etc.).

Returning multiple values from functions may be accomplished by returning an array or by directly modifying formal parameters passed by reference. Directly setting global program variables as a way to return multiple values is not allowed. The one exception is using session variables ($_SESSION) for user authentication, permissions tracking, and other stateful tracking across multiple scripts or Web pages. See Variables above for more information.

Ideally, main scripts should always return integer values when they finish. These values are used by the operating system or Web server daemon to determine the overall success of the scripts. Returning a non-zero value as part of a Web page script will cause the server to abort display of the page and return a HTTP error code instead. For this reason, even when errors are detected in Web pages, these should be caught and displayed as meaningful Web messages. See Error Handling below for more information.

Non-Web PHP scripts that are run from the command line generally should return 0 if successful and a non-zero value if an error is encountered. That is particularly true if several scripts are chained together in cron jobs, as each script should check the return code of the previous script before proceeding. This may vary if the purpose of the script is to return the count of something, where 0 is the abnormal result. In any case, the meaning of the final return value must be clearly defined in the main script header.


Blocks

Web page scripts which contain a mix of HTML and PHP code are configured to keep the two clearly separated from each other. Inline forms of PHP code (e.g., <?php echo "something"?>) inside HTML code are not allowed. The intent is to make the PHP code as visible as possible for troubleshooting purposes. The that end, the HTML and PHP code are indented independently of each other. PHP blocks always start and end with <?php and ?> tags on lines by themselves. Alternate tag formats such as <? ... ?> or <%php ... %> are not used to ensure maximum portability.

In cases where page code is rapidly changing between HTML and PHP, the preference is to stay in PHP mode and use printf() statements to display the HTML code.

Example:


    <table summary="test data" border="1">
      <tr>

    <?php
      for ($i = 0; $i < 10; $i++)
      {
        // note the additional spaces before <td> to maintain the level of
        //   HTML indenting at this point

        printf("      <td>\n");
        printf("        cell #%d\n", $i);
        printf("      </td>\n");
      }
    ?>

      </tr>
    <table>
  
        

All program blocks are indented by two spaces per level. Tabs are never used, as they do not always render consistently across different terminal software. Editors which handle indents as an optimal mix of tabs (assuming tabs are equivalent to eight characters) and spaces must have this feature configured to use spaces only or this feature must be disabled.

Curly brackets which start blocks always appear on lines by themselves, indented to the level equal to the statement immediately preceeding the block. Ending curly brackets also appear on lines by themselves and are indented to the same level as the corresponding opening bracket.

Example:


    function fnTest()
    {
      for ($icount = 0; $icount < 10; $icount++)
      {
        printf("interation #%d\n", $icount);
        printf("advancing ...\n");
      }

      return;
    }

        

All statements which are longer than 80 characters after indenting must be manually wrapped to fit in an 80-column display. Breaks in such lines should be made where convenient (e.g., at commas, logical operators such as && or ||, and other natural breaks). Long printf() statements with no natural breaks will need to broken into multiple printf() statements or use the concatenation operator (.) to break long strings into two or more pieces. Continuation lines should always be indented with additional two spaces until the entire logical line is complete.

Example:


    printf("this is an example of a really long line that just " .
      "never seems to end: %s\n", $bLong_Line ? "absolutely long" : "not!");

        

Note that single-line blocks do not require curly brackets unless the statement immediately above is broken into continuation lines which are already indented two spaces. In that case, adding curly brackets is required for readability.

Example:


    for ($i = 0; $i < 10; $i++)
      printf("%d\n", $i);

        

Example:

    
    if ($bFirst_Name_Found && $bLast_Name_Found && $bHighSchool_Grad &&
      $bEmployed && ($cGender == 'M'))
    {
      printf("Found match on criteria\n");
    }

        

The blocks corresponding to case() statements are indented by two spaces and otherwise handled the same as any other block except they do not require start and stop curly brackets.

Example:


    switch ($sOption)
    {
      case "Start":
        printf("starting script.\n");
        break;
      case "Stop":
        printf("script is ending.\n");
        break;
      default:
        printf("invalid option specified\n");
    }

        

All built-in functions such as if, for, and while are separated by one space from their opening parentheses. All characters representing logical, mathematical, and concatenation operators are separated from their surrounding arguments by one space for readability.

Example:


    if (($iLine < 10) && (!feof($hFile) && ($sTmp = fgets($hFILE))
    {
      $iLine++;
      printf("reading character #%d\n", $iLine * 60);
    }

        

Example:


    die("Error encountered: " . $sMsg . "\n");
     
        

Output Functions

In general, printf() is always used instead of echo statements. This convention is based on the much better formatting control of printf(), the ability to use compact if-then-else constructs (? :), and general overall consistency. Variable interpolation (inherent inclusion of values by variable name) inside double-quoted strings is allowed in simple cases, but full use of printf() formatting requires they be included as separate parameters to the printf() function.

Example:


    printf("Reading line #${$iLine}\n");

    printf("Reading line #%3d\n", $iLine);

        

Command line scripts should always include a help option, as well as display of proper usage with expected and optional command line parameters if the script is called improperly. Command line scripts, particularly those that take some time to run, must display messages regarding their progress. Not only does this reassure the user that something is happening, but these are great debugging aides. When these scripts are run as cron jobs, the messages written to stdout may be redirected to /dev/null. Messages to stderr should not be redirected (in which case crond will automatically mail the error messages to the cron owner), unless other provisions for logging and alerting the appropriate people are implemented.

All data strings, especially those from user input or database queries, must be passed through the htmlspecialchars() function before being included in a Web page.

Errors should be reported as described in the section Error Handling below.


Security

All sensitive data, including database connection parameters, encryption keys, and access controls are defined in separate include files with extensions .inc, never in the body of the main scripts or in any files with extension .php. Cliffson Solutions Web servers are configured to block any attempt by an outside user to browse the contents of files with .inc extensions.

PHP scripts are interpreted, not compiled, so encryption keys must be obfuscated or encrypted themselves to prevent exposure in the source code files. Preferably, encryption and decryption processes are handled by calls to compiled C utilities ("shims"). Note that even when these "shims" are used, sensitive data should not be passed as plain text to system calls, as these may be exposed to anyone monitoring inter-process communications or using the UNIX ps command. Instead, the parameters should be encrypted using a mutual key or (preferred) only digest hashes are passed to the "shims" for validation. Passwords are never stored in plain text anywhere, including $_SESSION variables.

Sensitive data, including personally identifiable information (PII), user authentication credentials, and credit card information, are always transmitted on SSL connections using industry-standard encryption and certificate validation technology. All HTTPS connections must use registered (not self-signed) certificates from recognized commercial Certificate Authorities. All Web forms which require sensitive information must include a link to the site's privacy policy or privacy statement, as well as a clickable seal from the Certificate Authority to validate the current site's SSL certificate. For more information, refer to our Privacy Policy and Security Policy documents.

All program inputs, including databases, files, Web forms, and keyboard input, must be checked for missing values, out-of-range values, and over-length values (buffer overflows). All input is considered to be suspect until validated in this fashion.

In addition, all data from Web forms is validated for expected value ranges, and all mandatory data elements must be present. Although Web applications use client-side validation and pre-processing functions (e.g., Javascript, AJAX), all such data must be rechecked when submitted to processing scripts (server-side validation). Javascript routines are far too easy to hack, and all values must be rechecked for validity and self-consistency.

All data destinated for storage in databases must be validated and sanitized [using functions such as pg_escape_string()], particularly to avoid SQL injection attacks. All data which may influence calls to compiled "shims" or operating system functions or utilities must be validated and sanitized to prevent injection of malicious software or damaging commands, using utilities such as escapeshellcmd() or escapeshellarg().

Uploaded files, particularly from the Web, should be restricted to the smallest possible set of file types, as validated by file extension and examination of file headers. If these files are made available to other users, they must be subjected to malware scanners.

If session variables are used to track system logins and access controls, the application must provide a logout option to the user. In addition, all such session variables must include application and server timeouts. Because garbage collection by PHP is somewhat haphazard, a server script is run periodically to ensure all session files older than 12 hours are removed. Network addresses, browser IDs, and cookies may also be used to control and validate open sessions.

Access and handling of personally identifiable information (PII), credit card information, and other sensitive data is strictly controlled. Details of how this data is handled (and not handled) is in our Privacy Policy document.

For more information regarding security, also refer to our Security Policy document.


Error Handling

Errors may be handled directly by the section of the program that encounters the problem, or a separate error reporting function may be invoked. The latter is preferable, because the format of the error message may be easily modified in one place. Error handling may use PHP's throw-catch exception handling or by customized error reporting functions that are called directly.

Parameters for both die() and exit() may be either a text string or a numerical return value, but not both. For PHP scripts driving Web pages, any numerical return value must be 0 (the default) or the server returns an HTML error code rather than a meaningful message. Because of this behavior, providing an error message string to die() or exit() in Web scripts is more useful than returning an integer value. If the script is part of a Web application, errors should be detected and reported to the user as part of a valid HTML page, with optional e-mail notifications to the Web site operator. Otherwise, error messages should be written to stderr or to a specified log file, again with an optional e-mail to appropriate responsible parties.

Command-line scripts, however, are required to return a meaningful integer code to the operating system to allow detection of problems, particularly for cron jobs or when multiple dependent scripts are invoked as a processing chain. This these cases, the error message must be displayed as fprintf(stderr, ...), sent to syslog(), or written to an application-specific log file. Optionally, e-mail messages to the appropriate maintainers or group e-mail alias may be generated. For critical functions other alert mechanisms may also be used, including text messages and SNMP traps.

Functions may also return values to their callers to indicate an error has occurred, in which case the callers are reponsible for checking and acting on these. Functions which expect callers to handle errors must clearly document this in the function headers. In such cases, the callers always check for valid results and never assume a good value is returned.

Messages should always be meaningful and never just include an error number. Error numbers may be included to help the programmer quickly locate the problem, although a better approach is to include the name of the function in which the error occurred in the message.

A generic error function should accept the name of the caller and a string specifying the type of error. If writing to log files or e-mailing technical support personnel, the specific value that caused the problem should be included, although this may not be possible in cases of sensitive information (e.g., personally identifiable information or credit card information). Such logged or e-mailed messages should also include the name of the program and the date/time when the error occurred.

If the error is not fatal, the script should gracefully recover and resume; otherwise, the open resources should be closed and the script gracefully ended. In every case, the user should be notified that the problem has occurred, what is being done about it, and what the user's next step is. The user should always be left knowing what happened and that someone has been notified about the problem. Error reporting pages should never be dead ends and should always include links to continue browsing.

Scripts that depend on processes and technology outside themselves must always provide error checking, flag files, and timeouts, as appropriate. For example, any application that uses network sockets must implement timeouts. If data is transferred over network connections, some form of data integrity check must be performed. Programs that depend on the successful completion of other programs (e.g., data feeds and processing) must use flag files or other means to determine if the previous processing has finished successfully, and not just blindly assume the data is ready at a given time.


Modules

Large programming projects commonly split development efforts among multiple programmers working with multiple source code files. All such files are maintained within a single directory on one of our servers, although subdirectories may be created along functional lines to make management and organization easier. All projects use a software repository for backup and version control purposes. All programmers working on such projects will be clearly notified which pieces are their responsibility and how function interfaces are expected to work.

All included files, including common library utility files and project-specific constants and utilites, are named with .inc extensions. All such files are referenced with require() or require_once() statements. The include() statement provides no real error reporting if the specified file is missing, and it is therefore not used.


Libraries

Commonly used functions with applicability to multiple projects may be gathered into a centrally compiled and maintained library of include files. Because the impact of these functions is potentially much larger, all changes to function prototypes, calculations, return values, and error handling must be carefully reviewed by the management of Cliffson Solutions before being implemented. All such changes must be thoroughly documented and tested against all programs known to use the affected functions.

Only the Manager of Applications Development has the authority to update working libraries.


Sample Script

The following is an example of a simple, two-file script demonstrating all major expected components in source code developed at Cliffson Solutions, LLC.

Main file:


    #!/usr/local/bin/php

    <?php
      //////////////////////////////////////////////////////////////////////////
      // /develop/test1.php                                                   //
      //                                                                      //
      // [purpose of program]; [dependencies]; [expected inputs];[expected    //
      //   outputs];[other relevant notes regarding its operation]            //
      //                                                                      //
      // [identification of client or final destination]                      //
      //                                                                      //
      // Written [mm/dd/yyyy], [author]                                       //
      // Revised [mm/dd/yyyy], [author]                                       //
      //////////////////////////////////////////////////////////////////////////

      require("test1_utils.inc");

      //========================================================================
      // functions
      //========================================================================

      function fnExample1($icount, $smsg)
      {
        // repeats specified string to stdout; return 0 on success 1 on error
    
        if (bPGM_DEBUG)
          printf("** entering fnExample1()\n");

        // sanity checking

        if (!$icount || ($icount ≤ 0) || empty($smsg))
        {
          fnErr_Msg("fnExample1()", "bad parameter passed to function");
          return 1;
        }

        for ($i = 0; $i < $icount; $i++)
          printf("%s", $smsg);

        if (bPGM_DEBUG)
          printf("** leaving fnExample1()\n");

        return 0;
      }


      //========================================================================
      // main inline script
      //========================================================================

      $sMessage = "This is a test";

      if (fnExample1(iCOUNT, smessage))
      {
        printf("Program encountered an error -- aborting\n\n");
        return 1;
      }
      printf("Program completed successfully\n\n");
      return 0;
    ?>

        

Include file:


    <?php
      //////////////////////////////////////////////////////////////////////////
      // /develop/test1_utils.inc                                             //
      //                                                                      //
      // include file for test1.php, with global constants and utility        //
      //   functions; refer to test1.php for full documentation and comments  //
      //                                                                      //
      // Written [mm/dd/yyyy], [author]                                       //
      // Revised [mm/dd/yyyy], [author]                                       //
      //////////////////////////////////////////////////////////////////////////

      // local library required

      require("string_utils.inc");

      //========================================================================
      // global constants
      //========================================================================

      define("sPGM_NAME",   "test1");
      define("sPGM_VER",    "1.0");
      define("bPGM_DEBUG",  false);


      //========================================================================
      // global functions
      //========================================================================

      void fnErr_Msg($scaller, $smsg)
      {
        // print specified error message to stderr

        fprintf(stderr, "Error encountered in %s: %s\n\n",
          !empty($scaller) ? $scaller : "[unknown function]",
          !empty($smsg) ? $smsg : "[unknown problem]");
 
        return;
      }

      //========================================================================
      // global inline code
      //========================================================================

      session_start();
    ?>