PHP Standards

This is the text describing PHP standards. refs #SDLC-4


Table of Contents

 

  • Table of Contents
  • Introduction
  • Object-oriented programming (OOP)
    • Model-view-controller (MVC)
  • Frameworks
  • Naming Convention
  • Syntax
    • Code Commenting
    • Code Formatting
  • Configuration File
    • **Please Note:
    • The usage of INI files is discouraged, as they contain sensitive information such as passwords/passphrases. If, however, you would like to construct an INI file without the sensitive information, please follow the following standards.
    • If you are using INI files and the INI file(s) are under webroot (e.g. /apps/your_app_name/…), include a blank index.html or index.php file in a directory which contains your INI file
  • Project Setup (File Structure)
  • Security
    • Never trust user-entered data
    • Data integrity
    • Borrowed code
    • Anti-patterns
  • References
  • Suggested Resources
  • Appendix A – Frameworks Evaluation – (IN PROGRESS)
    • Evaluation Criteria
    • Evaluations
      • CakePHP:
      • Symfony: Goods Deliverable?
  • Appendix B – Security Anti-Patterns
    • header(‘Location: …’)
    • File inclusion
  • Appendix C – Performance Anti-Patterns
    • SQL Recursion and Iteration

 


Introduction

This document contains programming standards, best practices, and anti-patterns for PHP website development. Although we can not envision every potential project, scenario, or problem, this document hopes to tackle the most important and encompassing aspects of PHP development.  We aspire to produce more secure applications which perform well and scale with user needs.  We will make our applications easy to review and maintain through the use of standard syntax and common patterns.


Object-oriented programming (OOP)

Object-oriented programming allows function grouping and variable scoping which improves application security. Grouping functions in a logical way makes code easier to debug and maintain.  While this standard does not require OOP, we strongly recommend OOP as a best practice.

Model-view-controller (MVC)

One style of object-oriented programming, Model-View-Controller (MVC), defines three types of classes:

  • Model, a type of file that stores functions for data interaction (e.g., with the database, flat files, off-site APIs, etc.);
  • View, a type of file that stores a template for the outcome of a page;
  • Controller, a type of file that stores functions for handling the browser request, deciding which model to use, querying it, manipulating the received data, and passing it to the view.

GUIDELINE: Most projects should use MVC.

NOTE:

In most MVC-style websites programmed in PHP, the application handles views as a single class which takes a template file as a parameter, rather than a separate class for every view. We recommended this conciseness pattern.

Note

In addition to the three main types (model, view, controller) there may be other types of classes, such as a “helper” class, which stores functions accessible across multiple, unrelated sections of the website.

This diagram demonstrates how a request would propagate through a website:

Another request on the same example website:

In fact, nearly all modern PHP frameworks use MVC.


Frameworks

Before starting a project using a PHP framework, evaluate the pros and cons.  A developer can significantly reduce their development time as long as they chose the right framework to fit the business process.

Things to consider when evaluating a framework:

  • Hooks/Scaffolding for JSON RPC
  • Support for testing
  • Uses MVC design pattern
  • Scalable
  • Active online communities
  • Clear, complete documentation
  • Object-Oriented
  • Template usage
  • Easy integration with AJAX
    • Supports JavaScript libraries (such as jQuery)

See “Appendix A” for evaluations of specific frameworks.


Naming Convention

Convention For

Convention

Example

Class Name
  • Use a descriptive name
  • Avoid abbreviations
  • Begin with an uppercase letter
  • If the class belongs to a hierarchy, reflect this in the naming by using an underscore
  • UserProfile
  • SpaceSurvey
  • SpaceSurvey_Room_Mgmt
Class Variables and Methods (Public)
  • Named using “bumpy case” or “camel case”
  • Lower case word followed by words with their first letter capitalized
  • $userLastLoggedIn
  • getLastLoggedIn()
Class Variables and Methods (Private)*
  • Named using “bumpy case” or “camel case”
  • Start with an underscore, followed by a lowercase letter
  • An underscore will NOT make a variable or method “private”. Please specify!
  • private $_userLanguagePreference
  • private function _calculateSpaceUsage()
Class Variables and Methods (Protected)**
  • Applies to PHP5 ONLY
  • NOT preceded by a single underscore
  • protected $floorSqFt
  • protected function submitRoomInfo()
Constants
  • All uppercase
  • Use underscores to separate words
  • Prefix constant names with the uppercase name of the class or package
  • SALUTATION
  • SQUARE_FEET_LABEL
  • DB_USERNAME

—-

*Only the parent classe can access private members.

**Parent class and extended classes from the parent may access protected members.


Syntax

Code Commenting

  • Use the phpDoc specification
  • Precede every class and function with a comment block
  • Always comment code

Type of commenting in PHP:

Single Line / In-Line comment
// This is a comment
Single Line / In-Line comment
public function sampleFn($param /* String */$myvar = FALSE /* Boolean */) {
  if ($myvar) {
    echo 'sample ' $param;
  }
}
Multi-Line Comment
/* This is a multi-line comment;
this should describe what is
happening, in a chunk. */

All code files should start with the following block-comment:

Block Comment with Info
/**
 * Content Layout
 *
 * This file contains the template layout
 *
 * @author Rushikumar Bhatt
 * @version 1.0
 * @package sample
 */

Other (standard) phpDoc tags include:

  • @access
  • @copyright
  • @deprecated
  • @example
  • @ignore
  • @internal
  • @link
  • @see
  • @since
  • @tutorial
  • inline {@internal}
  • inline {@inheritdoc}
  • inline {@link}

Fore more information, please refer to the phpDocumentor tags page.

Putting it into Practice:

Use comments to describe application state.  For example, in a controller with a function to display all workout groups for a selected team, this comment will help the developers debug and maintain the application:

Function:Index
1
2
3
4
5
6
7
8
9
/**
 * Displays all WorkoutGroups for the selected Team.
     * @method index
     * @version 1.0
 **/
function index() {
    $data $this->WorkoutGroup->getAllByTeam($this->Session->read('selected_team.id'));
    $this->set('data',$data);
}

Code Formatting

  • Do not obfuscate code.
  • Indent code and use white space for readability
    • Adhere to one of the four main format styles: K&R Style (Kernel Style), Allman Style (BSD Style), Whitesmiths Style, and GNU Style
    • Use an IDE (integrated development environment) such as DreamWeaver, Tomcat, or Netbeans to help format code consistently

PHP beautifiers such as PHPFormatter and PHP Beautifier may help when dealing with poorly styled code.

K&R Style (Kernel Style)

Allman Style (BSD Style)

Whitesmiths Style

GNU Style

Explanation:
Named after Kernighan & Ritchiebecause of the formatting used in their examples.Also called kernel style because the Unix kernel uses this format and the “One True Brace Style” (abbrev. 1TBS).

K&R style indents the body of a code block by one tab (typically four spaces although eight spaces appears in examples written in C).

Explanation:
Named for Eric Allman who wrote a lot of the BSD utilities using this style.Resembles normal indent style in Pascal and Algol. It is the only style other than K&R in widespread use among Java programmers.

This style uses a basic indent per level shown below of eight spaces, although C++ and Java programmers may prefer four (or sometimes three).

Explanation:
Popularized by the examples that came with Whitesmiths C, an early commercial C compiler.This style uses a basic indent per level shown below of eight spaces but some programmers use four spaces.
Explanation:
Used throughout GNU EMACSand the Free Software Foundation code.GNU always indents four spaces per level, with “{” and “}” halfway between the outer and inner indent levels.
Example of K&R Style:
1
2
3
if ([cond]) {
    [body]
}
Example of Allman Style:
1
2
3
4
if ([cond])
{
    [body]
}
Example of Whitesmiths Style:
1
2
3
4
if ([cond])
    {
    [body]
    }
Example of GNU Style:
1
2
3
4
if ([cond])
  {
    [body]
  }

Note the difference between the two:

OKAY
1
2
3
4
5
function getFirstName($id){
     if ($loggedIn){
          return $this->name;
     }
}
DO NOT DO THIS
1
2
3
4
function getFirstName($id){
if ($loggedIn)
return $this->name;
}

Group Consensus

Icon

The PHP Standards Group recommends the K&R Style.


Configuration File

**Please Note:

  • The usage of INI files is discouraged, as they contain sensitive information such as passwords/passphrases. If, however, you would like to construct an INI file without the sensitive information, please follow the following standards.

  • If you are using INI files and the INI file(s) are under webroot (e.g. /apps/your_app_name/…), include a blank index.html or index.php file in a directory which contains your INI file

To prevent the need for unit testing or regression testing for simple variable changes, use a configuration file (.ini).  Since many of the variables needed for environment setup and control are sensitive (e.g., database connection strings, user names), do not include .ini files in your application build or source control systems.  Name the configuration files with the target environment name to allow the application to pick the right file based on the current runtime environment.

Example of a Configuration file

Code Listing 1. Sample Configuration file localhost . ini
1
2
3
4
5
6
7
8
9
; Comments start with ';'as in php.ini
[debug]
enable = true
[database]
type = OracleSQL
server = //cbatest2.uits.uconn.edu:1521/FAMIS.cbatest2.uits.uconn.edu
user = my_secret_user

A word of caution!

Icon

The following characters must not be used for Key/Value Pairs:
?{}|&~![()^”

The aforementioned characters have special meanings, when used in value.

Example Configuration Class
/**
 * Configuration
 *
 * This file contains the Configuration class
 *
 * @author Paul Grenier
 * @version 1.0
 * @package DAO
 * @example Configuration::getInstance()->database_type;
 */
class Configuration {
    static private $_instance = NULL;
    private $_iniSettings;
    protected function __construct() {
        //trick to getting relative path
        $path str_replace('//''/', dirname(__FILE__).'/').'../Config/';
        $file $_SERVER['SERVER_NAME'] . '.ini';
        if(file_exists($path $file)) {
            $this->_iniSettings = parse_ini_file($path $file, true);
        else {
            throw new Exception("Config file $file not found at $path.");
        }
    }
    public function __get($name) {
        $arr explode('_'$name, 2);
        $ini $this->_iniSettings;
        $section $arr[0];
        $item $arr[1];
        if (array_key_exists($section$ini)) {
            if ($item && array_key_exists($item$ini[$section])) {
                return $ini[$section][$item];
            }
            return $ini[$section];
        }
        return null;
    }
    public function __destruct() {
    }
    public static function getInstance() {
        if(self::$_instance == NULL) {
            self::$_instance new Configuration();
        }
        return self::$_instance;
    }
    public function __clone() {
        throw new Exception('Clone is not allowed.');
    }
}
?>

Project Setup (File Structure)

Use an organized file structure to lay out projects (example below):

Level

Type of Level/Directory

Type of files

/ ROOT This directory contains all of the files called directly by the browser.
/include INCLUDES This directory contains all of the files to be included into other files. Also, this directory holds other sub-directories, such as “javascripts.”
/include/javascripts SCRIPTS This directory contains JavaScript files and resides inside the “include” directory.
/include/images
OR
/images
IMAGES This directory contains all image files. This can either go under the include directory or at the root (/) level.

Security

Never trust user-entered data

Assume all data is user-entered data and escape data immediately when it needs to be escaped, not earlier.  This avoids both unescaped data and redundant escaping (e.g., ').

For example, when escaping data for a SQL query, escape everything, and escape during the concatenation, not before:

<?php
$example $_GET['example'];
if ($_GET['example2'] == true) {
  $example2 "foo";
else {
  $example2 "bar";
}
$query "SELECT 'col1', 'col2' FROM 'tbl' WHERE 'col3' = '".mysql_real_escape_string($example)."' AND 'col4' = '".mysql_real_escape_string($example2)."'";
?>

Note

Icon

Some frameworks or database functions using bind variables may supply a separate means of securing input.{{}}

When echoing a string not containing HTML, escape it inside the echo parameter, not before you store it:

<?php
$example "This is an example string.";
echo htmlspecialchars($example);
?>

Data integrity

Do not rely solely on client-side (JavaScript) validation as users can easily circumvent this. Include a server-side version of any data validation.

Also, to ensure data integrity, consider including constraints in the database (i.e., foreign keys, unique constraints) to reduce the chances that invalid data reaches your application through others means (e.g., direct database manipulation, improper code).

Borrowed code

Many developers take advantage of the abundance of free and open-source code on the internet. However, these scripts can create security vulnerabilities because of a) the large number of people working on them; b) the availability of the code to anyone who wishes to view it; and c) the sometimes heavy use of these scripts on many sites, resulting in more incentive for hackers to find holes in them.

Do not waste time reinventing the wheel (as often in-house renditions can have many of the same security issues).  But stay alert and always watch for any released security vulnerabilities for the code you use and always patch your code with the supplied updates or patch it yourself and share your changes with the code community.

Anti-patterns

See Appendix B for security anti-patterns.


References

Suggested Resources

  • FishEye
    • Fisheye nicely ties into an SVN or CVS (or Perforce, Git, or ClearCase, for that matter).

Appendix A – Frameworks Evaluation – (IN PROGRESS)

This Appendix describes evaluations performed for the listed/mentioned frameworks. Evaluating the growing landscape of ever-evolving frameworks takes time. Please check back often for updates.

Evaluation Criteria

The test process for each framework includes:

  • Download the Framework
  • Install it on a development machine
  • Configure the framework so to use CAS
  • Develop a simple application
  • Configure the framework to communicating with a database (Oracle is preferred)
  • Record results at each step
    • This may include information such as the time it took to do a task

For “developing a simple application” task, we used a “ThoughtBoard” application with the following requirements:

  • Application shall be CASified
  • Application shall be able to take an input by the user, “thought”, and save it in the database
  • Application shall be able to display all the inputs, “thoughts”, of the users – retrieving it from the database
  • Application shall be able to edit a given input (“thought”)
  • Application shall be able to delete a given input (“thought”)
  • Application shall be able to search through inputs (“thoughts”) for a specified query string

The entity-relation diagram for the “ThoughtBoard” database looks as follows:

Evaluations

To date, we have evaluated frameworks: CakePHP and Symfony.  We plan to evaluate six more frameworks: CodeIgniter, Solar, Yii, Kohana, Akelos, and Agavi.

We used CakePHP for one of our projects and as a direct result of that experience will not recommend it. However, CakePHP has some benefits.

If you are a fan of PHP CLI (PHP Command Line Interface), configuration via YAML files, using ORM, like RoR, you may like Symfony.  Symfony’s strong documentation, vibrant user-community, and great examples will encourage adoption.

CakePHP:

Pros

Cons

Active, friendly community Poor Documentation
MVC Architecture No Template Support
Code Generation No modules integration
Integrated CRUD for database Interaction Steep learning curve due to poor documentation.
Data Sanatization
Localization
ORM Built-in
Fast / Flexible templating
Application Scaffolding
Smaller learning Curve
Symfony: Goods Deliverable?

Pros

Cons

Good Documentation PHP CLI – most developers will NOT have access to all three environments/boxes (DEV / TEST /AND PROD)
MVC Architecture A lot of configuration, before seeing results.
– Populating a YAML files, for configuration
Code Generation
Data Sanatization
Localization
ORM Choice (Propel or Doctrine)
Highly Modular**
– Many of the components work on their own
Model definition with YAML or PHP
– If you don’t like configuration files, stay away from YAML
Symfony CLI is very well documented
A lot of similarities with RoR
Symfony admin generator!

Appendix B – Security Anti-Patterns

This section outlines some common patterns that leave security holes in website code and provides an explanation and fix for the code.

header('Location: ...')

<?php
if ($notAuthorized) {
  header('Location: denied.php');
}
echo 'Sensitive data';
?>

This code would supposedly direct any unauthorized users away from the page. However, clients are not required to listen to header() commands.Therefore, if a malicious client chose to ignore the headers, they would still be able to access the sensitive data.

The proper fix would be as follows:

<?php
if ($notAuthorized) {
  header('Location: denied.php');
  exit();
}
echo 'Sensitive data';
?>

This way, the sensitive data would never be sent if the user is not authorized.

File inclusion

Often a pattern used for routing a whole website through a single PHP page is as follows:

<?php
include($_GET['page'].'.php');
?>

However, if $_GET['page'] were set to a URL, or a path such as ‘admin/deleteEverything.php’ it could open vulnerabilities.

Either use a proper router, or the following code to ensure sanity in your inclusion path:

<?php
function validFilename($n) {
  return (substr($n, 0, 1) != "." && strpos($n'/') === FALSE && strpos($n'') === FALSE);
}
$path $_GET['path'].'.php';
if (validFilename($path)) {
  include($path);
}
?>

Appendix C – Performance Anti-Patterns

SQL Recursion and Iteration

Try to limit database calls on a page.  Do not place database calls inside functions or methods that will be invoked recursively or iteratively.  Even when executed in a single transaction with bind variables,  this pattern will not scale well.  In nearly every case, you can solve this type of problem by writing better SQL.

<?php
/* DON'T DO THIS */
function printUserName($id) {
  //SELECT first_name, last_name FROM users WHERE id = $id;
  echo $first_name ' ' $last_name;
}
foreach($users as $user) {
  printUserName($user->id);
}
/* INSTEAD DO THIS */
function getUserNames($idArray = NULL /* Array */) {
  if ($idArray && is_array($id_array) {
    $idList = implode(', '$idArray);
    $filter "WHERE id IN ($idList)";
  } else {
    $filter '';
  }
  //SELECT id, first_name, last_name FROM users $filter;
  return $results;
}
$userNames = getUserNames($users);
foreach($userNames as $user) {
  echo $user->first_name . ' ' $user->last_name;
}
?>

In this example, we made a more robust function that can return all user names or just ones from a list (array).  We then iterate over the database results rather than call the database iteratively.

EoF – Last Updated 07/31/2012