Expert PHP 5 Tools

4 (1 reviews total)
By Dirk Merkel
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Coding Style and Standards

About this book

Even if you find writing PHP code easy, writing code that is efficient and easy to maintain and reuse is not so straightforward. Self-taught PHP developers and programmers transitioning from other languages often lack the knowledge to work with PHP on an enterprise level. They need to take their PHP development skills to that level by learning the skills and tools necessary to write maintainable and efficient code.

This book will enable you to take your PHP development skills to an enterprise level by teaching you the skills and tools necessary to write maintainable and efficient code. You will learn how to perform activities such as unit testing, enforcing coding standards, automating deployment, and interactive debugging using tools created for PHP developers – all the information in one place. Your code will be more maintainable, efficient, and self-documented.
From the design phase to actually deploying the application, you will learn concepts and apply them using the best-of-breed tools available in PHP.

Experienced developers looking for expertise in PHP development will learn how to follow best practices within the world of PHP. The book contains many well-documented code samples and recipes that can be used as a starting point for producing quality code.

Specifically, you will learn to design an application with UML, code it in Eclipse with PDT, document it with phpDocumentor, debug it interactively with Xdebug, test it by writing PHPUnit tests, manage source code in Subversion, speed up development and increase stability by using Zend Framework, pull everything together using continuous integration, and deploy the application automatically with Phing – all in one book. The author's experience in PHP development enables him to share insights on using enterprise tools, in a clear and friendly way.

Publication date:
March 2010
Publisher
Packt
Pages
468
ISBN
9781847198389

 

Chapter 1. Coding Style and Standards

A developer's coding style takes time to develop and often reflects his or her personality. Consequently, you are likely to encounter more than just a little resistance when trying to get your team members to adopt a different style. However, that is exactly what I will be arguing in this chapter. We will learn about the benefits of standardizing on a certain coding style. Along the way, we will develop our own style and learn how to automatically enforce that or any standard of your choice using the handy PHP_CodeSniffer utility. I am hoping that at the end of this chapter, you will be able to take a look at your own and your co-workers' coding styles and make the necessary changes to reap the benefits that include code readability and maintainability.

Coding standard considerations

Since you are reading this book, there is a good chance that you have a couple of years of programming experience under your belt. Even if it is in a programming language other than PHP, you have probably had plenty of time to develop your own style of writing code. In all likelihood, you have chosen or developed a style that appears to make the most sense to you and that you can adhere to and read easily.

However, if you are reading this book, it also means that you are looking to improve your knowledge of PHP. Consequently, I assume that you are willing to change your coding style or at least fine-tune it. First, let me try to convince you that this is worth the effort.

Even if you are writing code only for yourself and have no reasonable expectation that any other programmer will ever look at or work on it, you will probably want to follow your own coding standards. Whether you are aware of it or not, chances are you are doing this already. For example, at some point every programmer decides whether to put the opening brackets on the same line or the one following the if-statement. I would also guess that you are doing it the same way every time.

Personally, I have had many occasions to revisit code that I had written years ago. I can always tell how well defined my coding style was at the time. The more consistent the style, the easier it is to get back into the code and understand the intricacies of what it is doing.

I think we have all had trouble understanding our own code after not looking at it for a while. If you haven't been in that position already, imagine inheriting a project of substantial size and having to get used to someone else's coding habits and oddities. This is where agreeing on a common coding standard pays off. If all developers working on the same project agree on the same standard, collaboration becomes so much easier as it takes less time to find and figure things out. I'm not just talking about where to place the opening brackets, but rather things such as locations of classes and libraries, names of methods and attributes, and inline documentation.

Let's consider some of the pros and cons of having formally defined coding standards—starting with the pros.

Pros

It will be easier to understand code. Whether you are looking at your own code or that of another development team member, reading and understanding the code will be more fluent. This benefit extends not only to current contributors, but also to programmers who are new to the team or PHP in general. Not having to grapple with different styles and conventions will allow them to come up to speed more quickly and allow them to absorb the common style from the beginning.

Nowadays, software is often designed, developed, tested, and used in a distributed fashion. Team members could be located anywhere in the world. With todays tools for communication, the rules for where and how to assemble software teams are being rewritten. Just take a look at some of the very successful Open Source projects, many of which have no physical presence at all. Consider the Apache Software Foundation or in the PHP space, the Zend Framework project, both of which are very successful examples of highly successful projects with many distributors from all over the globe. Projects such as these are some prime candidates for reaping the benefits of a common coding standard.

I would go so far as to say that the right coding standard should go beyond style. It can improve the quality and robustness of the code. For example, having developers consistently validate method parameters will undoubtedly result in a more robust code base.

Cons

Developers tend to ignore coding standards. Adhering to a common standard requires everyone to change their ways some more so, some less. Unless someone takes on the responsibility of enforcing the standard, change is not going to come about by itself. When developers are too set in their ways or push back when being asked to change, you run the risk of alienating them. The best thing is to get everyone involved in developing the standard. With their own time and expertise invested in the project, they are more likely to abide by the rules the group agreed on.

There are also some common myths when it comes to coding standards in general. First, people tend to think that it stifles creativity. What people who are not familiar or experienced with software development often don't realize is that software is as much a creative process as writing a poem or composing a melody is. There are basic rules to follow in all those endeavors. Depending on what kind of poem you are writing, you might have to make sure it rhymes, follows a certain rhythm, or only has a certain number of syllables. Writing software is no different. At a basic level, you have some rules to define the playing field. Having a coding standard is just a small part of the rules. There are still endless possibilities for the developer to express his creativity and ingenuity.

The second myth you commonly encounter is that it is unnecessary. You will often hear programmers say something like: "My code has been working flawlessly all along. Why do I need a coding standard?" or "If you can't understand the code I write, then you're not good enough to work on this project."

The former statement misses the point. A coding standard's primary purpose is not to make the code work (although it may help). There are many other tools available to help developers with that part of their work. What a coding standard does is make it easier and faster for the developer to understand their own and others' code.

The latter statement betrays the developer's attitude towards working in a group environment. In my opinion, exactly the opposite is true. The bigger the development team and the more complex the project, the more it can benefit from some common ground rules.

 

Coding standard considerations


Since you are reading this book, there is a good chance that you have a couple of years of programming experience under your belt. Even if it is in a programming language other than PHP, you have probably had plenty of time to develop your own style of writing code. In all likelihood, you have chosen or developed a style that appears to make the most sense to you and that you can adhere to and read easily.

However, if you are reading this book, it also means that you are looking to improve your knowledge of PHP. Consequently, I assume that you are willing to change your coding style or at least fine-tune it. First, let me try to convince you that this is worth the effort.

Even if you are writing code only for yourself and have no reasonable expectation that any other programmer will ever look at or work on it, you will probably want to follow your own coding standards. Whether you are aware of it or not, chances are you are doing this already. For example, at some point every programmer decides whether to put the opening brackets on the same line or the one following the if-statement. I would also guess that you are doing it the same way every time.

Personally, I have had many occasions to revisit code that I had written years ago. I can always tell how well defined my coding style was at the time. The more consistent the style, the easier it is to get back into the code and understand the intricacies of what it is doing.

I think we have all had trouble understanding our own code after not looking at it for a while. If you haven't been in that position already, imagine inheriting a project of substantial size and having to get used to someone else's coding habits and oddities. This is where agreeing on a common coding standard pays off. If all developers working on the same project agree on the same standard, collaboration becomes so much easier as it takes less time to find and figure things out. I'm not just talking about where to place the opening brackets, but rather things such as locations of classes and libraries, names of methods and attributes, and inline documentation.

Let's consider some of the pros and cons of having formally defined coding standards—starting with the pros.

Pros

It will be easier to understand code. Whether you are looking at your own code or that of another development team member, reading and understanding the code will be more fluent. This benefit extends not only to current contributors, but also to programmers who are new to the team or PHP in general. Not having to grapple with different styles and conventions will allow them to come up to speed more quickly and allow them to absorb the common style from the beginning.

Nowadays, software is often designed, developed, tested, and used in a distributed fashion. Team members could be located anywhere in the world. With todays tools for communication, the rules for where and how to assemble software teams are being rewritten. Just take a look at some of the very successful Open Source projects, many of which have no physical presence at all. Consider the Apache Software Foundation or in the PHP space, the Zend Framework project, both of which are very successful examples of highly successful projects with many distributors from all over the globe. Projects such as these are some prime candidates for reaping the benefits of a common coding standard.

I would go so far as to say that the right coding standard should go beyond style. It can improve the quality and robustness of the code. For example, having developers consistently validate method parameters will undoubtedly result in a more robust code base.

Cons

Developers tend to ignore coding standards. Adhering to a common standard requires everyone to change their ways some more so, some less. Unless someone takes on the responsibility of enforcing the standard, change is not going to come about by itself. When developers are too set in their ways or push back when being asked to change, you run the risk of alienating them. The best thing is to get everyone involved in developing the standard. With their own time and expertise invested in the project, they are more likely to abide by the rules the group agreed on.

There are also some common myths when it comes to coding standards in general. First, people tend to think that it stifles creativity. What people who are not familiar or experienced with software development often don't realize is that software is as much a creative process as writing a poem or composing a melody is. There are basic rules to follow in all those endeavors. Depending on what kind of poem you are writing, you might have to make sure it rhymes, follows a certain rhythm, or only has a certain number of syllables. Writing software is no different. At a basic level, you have some rules to define the playing field. Having a coding standard is just a small part of the rules. There are still endless possibilities for the developer to express his creativity and ingenuity.

The second myth you commonly encounter is that it is unnecessary. You will often hear programmers say something like: "My code has been working flawlessly all along. Why do I need a coding standard?" or "If you can't understand the code I write, then you're not good enough to work on this project."

The former statement misses the point. A coding standard's primary purpose is not to make the code work (although it may help). There are many other tools available to help developers with that part of their work. What a coding standard does is make it easier and faster for the developer to understand their own and others' code.

The latter statement betrays the developer's attitude towards working in a group environment. In my opinion, exactly the opposite is true. The bigger the development team and the more complex the project, the more it can benefit from some common ground rules.

 

A PHP coding standard


There are many ways to define a coding standard. Some like to set some basic guidelines and leave the rest to the developers. Others like to be as explicit as possible. What I am getting at is that there is no correct answer. Any given standard may or may not be appropriate for a given situation or project.

With that in mind, let's be true to the approach of this book and come up with a general-purpose coding standard that you can use as a starting point for your own projects. Our assumption will be that we are developing PHP5+ application, which is why our coding standard will specifically address and forbid some constructs and conventions commonly used in PHP4's object-oriented implementations.

Formatting

Formatting paints the overall picture of the code. It's the first thing you see when you glance at it. It is also the one chance the developer has to make the code easily readable that does not require understanding of what the code actually does.

PHP tags

All PHP code is to be enclosed by full PHP tags:<?php and ?>. Short tags (<? and ?>) or ASP-style tags (<% and %>) are not allowed.

Indenting

Tabs are to be replaced by four space characters. Most IDEs and text editors can be set to do this automatically.

Line length

The suggested maximum number of characters per line is 80 characters, although just about all GUI-based editing tools can easily accommodate more characters. This convention takes into consideration that command line interfaces are often limited to 80 characters. The absolute maximum number of characters per line is 120. Lines of code with more characters are not acceptable.

Line endings

Lines must end only with a standard Unix linefeed (LF). Linefeeds are represented as ordinal 10, or hexadecimal 0x0A.

Carriage returns (CR) (0x0D), commonly used on Macintosh computers, and carriage return/linefeed combinations (CRLF) (0x0D, 0x0A), commonly used on Windows computers, are not allowed.

Spacing

For readability, spaces are required at the following code sections:

  • After a comma separating method/function parameter lists or array members

  • Following control structure keywords, such as if, else, unless, switch, and so on

  • Before curly braces unless they are at the beginning of a line

  • Before and after logical operators, such as&&, ||, &, |, ==, !=, ===, and !==

  • Before and after arithmetic operators, such as +, -, *, and %

  • Before and after assignment operators, such as =, +=, -=, and *=

<?php
public function doSomething($arg1, $arg2, $arg3)
{
if ($arg1 == $arg2 == $arg3) {
// notice blank line above
$this->identical = true;
echo "All three arguments are identical.\n";
} else {
echo "At least one argument is different.\n";
}
}
?>

Statements

Put only one statement per line. Multiple statements on a single line are expressly forbidden because they are easily overlooked when reading the code. Spreading a single statement over multiple lines is discouraged unless it clearly improves readability of the code.

<?php
// unnecessary and confusing
echo ($errorCondition === true)
?
"too bad\n"
:
"nice\n";
// keep ';' on the same line as the statement
// don't do this:
echo "goodbye!\n"
;
// must not put multiple statements on a line
echo 'An error has occurred'; exit;
?>

Strings

Use only double quotes to define strings if you are taking advantage of variable interpolation or the string contains formatting characters or single quotes, such as', \n or \t. In all other instances, single quotes should be used as they result in less work for the parser and faster execution of your script.

Strings exceeding the maximum line length should be broken into smaller segments and concatenated using the dot notation.

Long strings should use the heredoc notation if possible.

<?php
// defining a simple string
$myOutput = 'This is an awesome book!';
$adjectives = array('nicer',
'better',
'cleaner');
// string with variable interpolation and formatting characters
echo "Reading this book makes a good PHP programer $adjectives[1].\n";
// double quotes containing single quote
echo "This book's content will make you a better developer!";
// defining long strings by breaking them up
$chapterDesc = 'In this chapter, we are tyring to explore how'
'a thorough and clear common coding standard'
'benefits the project as well as each individual'
'developer.';
// I'm not much of a poet, but this is how you use the heredoc syntax
$poem = <<<ENDOFSTRING
Roses are red,
violets are blue,
this is my poem
for the code in you.
ENDOFSTRING;
?>

Arrays

Numerically indexed arrays should have base index 0 whenever possible. For numerically indexed array, multiple values per line are allowed, but spaces must be inserted to align the first item of each line.

Associative arrays should be declared such that key-value pairs should be listed one to each line. White space must be inserted so as to align the names, assignment operators, and values of all items. Single quotes are always to be used if the key value is a string.

<?php
// simple numerically indexed array
$myFruits = array('apples', 'bananas', 'cherries');

// use bracket syntax within string to access array
echo "My favorite fruits are {$myFruits[2]}.\n\n";
// longer numerically indexed array
$myLongList = array('The', 'quick', 'brown' ,'fox',

'jumped', 'over', 'the', 'lazy',
'fox', '.');
// use bracket syntax with variable as index to access array
$listSize = count($myLongList);
for ($i = 0; $i < $listSize; $i++) {
echo $myLongList[$i];
echo ($i < $listSize - 2) ? ' ' : '';
}
echo "\n\n";
PHP coding standardPHP coding standardarrays// associative array; everything lines up
$spanAdj = array('green' => 'verde',

'little' => 'poquito',
'big' => 'grande');
// using a foreach construct to access both keys and values
foreach ($spanAdj as $english => $spanish) {
echo "'" . $spanish . "' means '" . $english . "' in Spanish.\n";
}
?>

The above code fragment outputs the following text when executed:

Control structures

Conditional tests should be written on one line if possible. For long and complex conditional tests, line breaks should be inserted before each combining operator with the line indented for combining operators to line up. The starting curly brace should appear on the same line as the last conditional test. Control statements should have one space between the control keyword and the opening parenthesis, so as to distinguish them from function calls.

<?php
// all conditionals on the same line
if ($myValue <= -1 || $myValue > 100) {

doSomethingImportant();
}
// too many conditional for one line
// break up conditionals like this ...
if ($myValue > 5
|| $myValue < 5
|| $myValue == 0
|| $myValue == -3) {

doSomethingElseImportant();
}
?>

If-elseif-else statements

The elseif and else keywords are to appear on the same line as the previous block's closing parenthesis:

<?php
if (is_array($myHash)) {
throw new Exception("myHash must be an array!");
} elseif (array_key_exists('index', $myHash)) {
echo "The key 'index' exists.\n";
} else {
echo "The key 'index' does NOT exists.\n";
}
?>

Switch statements

The body of the switch statement and the body of each case statement must be indented. The opening curly bracket should appear on the same line as the test expression. A space character should precede the parentheses of the test expression. Finally, the last case statement must be followed by a default statement:

<?php
switch ($temperature) {
case 50:
echo "Let's stay home and write some code.\n";
break;
case 80:
echo "Let's go to the beach\n";
break;
default:
echo "Go to jail. Do not pass go.\n";
break;
}
?>

Class and method definitions

Both class and method definitions follow the "one true brace" convention. That is to say, the opening and closing curly brackets are to appear on a line by themselves:

<?php
// showing "one true brace" convention
class Otb
{
// for metods as well
public method doSomething()
{
// your clever and immensely important code goes here
...
}
}
?>

Naming conventions

Choosing appropriate and descriptive names for your classes, methods, properties, and constants may seem like a trivial task, but it may well be the most important part of your job as a programmer — short of developing functional code. The names you choose will have a significant impact on the readability of your code and the ease with which other developers in your team will be able to follow your design and logic. It is also far from easy because it requires a deep and thorough understanding of the overall structure of your application.

Note

Stringing words together to construct the name of a method, function, variable, property, or class is commonly referred to as a camelCase if each new word starts with a capital letter, but all other letters are lowercase. No other word delimiters are used. Examples of camelCase formatting are: getStackLength(), updateInterval, and DbLogWriter.

Class names

Classes should be named after the objects they represent. Try to stay away from descriptive phrases (too long), incorporating parent class or interface names, or using verbs.

Examples of bad class names are:

  • ExcessInventoryProductOrderReceivedAndFilled (too long and contains verb)

  • IterableWidgetList (incorporates interface name)

Examples of good names are as follows:

  • WidgetStack

  • DbFileCache

Class names should reflect the path relative to the root class directory. Directories are to be delimited by the underscore character ("_"). The first letter of each word/directory name is to be capitalized. No other characters are to be capitalized. In particular, abbreviations should adhere to this convention as well. Furthermore, only alphanumeric characters and the underscore character are allowed. Use of numbers is discouraged.

The following PHP segment takes a class file path and converts it to the corresponding class name:

<?php
class ClassNameConverter
{
public static $classRootDir = array('var', 'www', 'sites', 'my_app', 'includes', 'classes');
public static function makeClassName($absolutePath)
{
$platformClassRootDir = DIRECTORY_SEPARATOR .
implode(DIRECTORY_SEPARATOR, self::$classRootDir) .
DIRECTORY_SEPARATOR;
// remove path leading to class root directory
$absolutePath = str_replace($platformClassRootDir, '', $absolutePath);
// replace directory separators with underscores
// and capitalize each word/directory
$parts = explode(DIRECTORY_SEPARATOR, $absolutePath);
foreach ($parts as $index => $value) {
$parts[$index] = ucfirst(strtolower($value));
}
// join with underscores
$absolutePath = implode('_', $parts);
// remove trailing file extension
$absolutePath = str_replace('.php', '', $absolutePath);
return $absolutePath;
}
}
$classNameExamples =
array('/var/www/sites/my_app/includes/classes/logging/db/Mysql.php',
'/var/www/sites/my_app/includes/classes/logging/db/MysqlPatched.php',
'/var/www/sites/my_app/includes/classes/caching_lib/Memcached.php'
);
foreach ($classNameExamples as $path) {
echo $path . ' converts to ' .
ClassNameConverter::makeClassName($path) . "\n";
}
?>

Here is the corresponding output:

Property and variable names

Properties and variables should start with a lower case letter, contain only alphanumeric characters, and generally follow the "camelCase" convention. Every effort should be made to make property names as descriptive as possible without their length getting excessive. Underscore characters are not allowed in property and variable names.

Short variable names, such as $i or $cnt are only allowed in very short looping constructs.

Constant names

Names of constants should only contain alphanumeric characters. Words are to be separated by the underscore character. Names should be as descriptive as possible, but more than three words are discouraged.

Method and function names

Similar to property names, method and function names should contain only alphanumeric characters and follow the "camel case" convention. They should start with a lower case letter and contain no underscore characters. Class methods must always be preceded by one of the three visibility specifiers: public, protected, or private.

Method names should be descriptive as to their purpose. However, excessive length is discouraged.

Accessor methods for object properties should always be named set<PropertyName> and get<PropertyName>.

The following listing illustrates proper names for properties, methods, classes, and constants:

<?php
class MessageQueue
{
// upper case constant with underscores
const MAX_MESSAGES = 100;
// descriptive properties using camelCase
private $messageQueue = array("one\ntwo");
private $currentMessageIndex = -1;
// setter method for $currentMessageIndex
public setCurrentMessageIndex($currentMessageIndex)
{
$this->currentMessageIndex = (int)$currentMessageIndex;
}
// getter method for $currentMessageIndex
public getCurrentMessageIndex()
{
return $this->currentMessageIndex;
}
// is<Attribute> method returns boolean
public function isQueueFull()
{
return count($this->messageQueue) == self::MAX_MESSAGES;
}
// has<Attribute> method returns boolean
public function hasMessages()
{
return (is_array($this->messageQueue) && count($this->messageQueue) > 0);
}
// descriptive take action method
public function resetQueue()
{
$this->messageQueue = null;
}
// descriptive take action method
public function convertMessagesToHtml()
{
// local copy of message queue
$myMessages = $this->messageQueue;
// $i is acceptable in a short for-loop
for ($i = 0; $i < sizeof($myMessages); $i++) {
$myMessages[$i] = nl2br($myMessages[$i]);
}
return $myMessages;
}
// additional methods to manage message queue ...
}
?>

Methodology

In this section, we will look at some conventions that take advantage of object-oriented features found in PHP5 and later. Some of the recommendations listed as follows are generally considered best practices.

Type hinting

Whenever possible, functions should type-hint by specifying the class of the parameter. Furthermore, as of PHP 5.1, arrays can be type-hinted. Therefore, arrays should always be type hinted—especially when passing a hashed array instead of individual parameters.

<?php
class SomeClass
{
// method requires an object of type MyClass
public function doSomething(MyClass $myClass)

{
echo 'If we made it this far, we know that the name ' .
'of the class in the parameter: ' . get_class($myClass);
}
// method requires an array
public function passMeAnArray(array $myArray)

{
// we don't need to test is_array($myArr)
echo "The parameter array contains the following items:\n";
print_r($myArr);
}
}
?>

Separating object creation from initialization

If something goes wrong during object creation, the calling code may be left in a state of uncertainty. For example, throwing an exception in the constructor will prevent the object from being instantiated. Therefore, it is advisable to separate construction and initialization of the object. A short constructor will be responsible for instantiating the object, after which, an init function is responsible for handling object initialization.

<?php
class MyClass
{
private myAttrib;
// short constructor - no parameters this time
public function __construct()
{
// intentionally left blank
}
// initialization method to be called
// immediately after object instantiation
public function init($var)

{
$this->myAttrib = trim$(var);
}
}
// first we instantiate the object
$myObject = new MyClass();
// then we initialize the object
$myObject->init('a string literal');
?>

Class files

Each class definition should be in a separate source file. And, there should be no additional code (outside the class) in that file. The name of the file should reflect the name of the class. For example, class Message should be defined in the file Message.php.

Class names and directory structures

The directory hierarchy in which the class files are organized should be reflected in the name of the class. For example, assume that we decided to put our classes directory inside an includes directory:

includes/
classes/
Parser/
FileParser/
CommonLog.php

The file CommonLog.php should contain a class called Parser_FileParser_CommonLog. This convention will make it easier to comprehend a class's position in the class hierarchy. It also makes it easier for the autoload() function to locate classes.

Visibility and access

All properties and methods of a class must have one of three visibility specifiers: public, protected, or private. Direct access to properties is discouraged in favor of corresponding getter and setter methods: get/set<Attribute>()—even when accessing properties from within a class. Magic methods may be used to provide access to the properties.

<?php
class AlwaysUseGetters
{
// private property not accessible outside this class
private $myValue;
// setter method
public function setMyValue($myValue)
{
$this->myValue = $myValue;
}
// getter method
public function getMyValue()
{
return $this->myValue;
}
public function doSomething($text)
{
// use getter to retrieve property
return $text . $this->getMyValue() . '!';
}
}
// instantiate object
$myAwc = new AlwaysUseGetters();
// use setter to set property
$myAwc->setMyValue('book');
// call method to illustrate use of getter
echo $myAwc->doSomething('This is an awesome ');
?>

Including source files

Use require_once to unconditionally include a PHP file. Analogously, use include_once to conditionally include a PHP file, for example in a factory method. Since require_once and include_once share the same internal file list, a file will never be included more than once even if you're mixing the two constructs.

Never use require or include as they provide the same functionality as require_once and include_once, but leave open the possibility of unintentionally including the same PHP file multiple times.

<?php
// use of require_once
require_once('logging/Database/DbLogger.php');
class DbConnector
{
// these are the RDBMs we support
public static $supportedDbVendords = array('mysql',
'oracle',
'mssql');
// factory method using include_once
public static function makeDbConnection($dbVendor = 'mysql')
{
if (in_array($dbVendor, self::$supportedDbVendords)) {
// construct the class name from the DB vendor name
$className = ucfirst($dbVendor);
include_once 'database/drivers/' .
$className . '.php';
return new $className();
} else {
// unsupported RDBMs -> throw exception
throw new Exception('Unsupported RDBSs: ' . $dbVendor);
}
}
}
PHP coding standardPHP coding standardsource files, including// use factory method to get DB connection
$dbHandle = MakeAnObject::makeDbConnection();
?>

Comments

Developers are encouraged to provide inline comments to clarify logic. Double forward slashes (//) are used to indicate comments. C or Perl-style hash marks (#) to signal comments are not allowed. For readability, all inline comments are to be preceded by a blank line. Here is the previous listing with inline comments. Notice how blank lines were added to make it more readable.

<?php
// inline comment preceding if statement
if (is_array($myHash)) {
// inline comment indented with code
throw new Exception("myHash must be an array!");
// inline comment preceding elseif (preceded by blank line)
} elseif (array_key_exists('index', $myHash)) {
echo "The key 'index' exists.\n";
// inline comment preceding else (preceded by blank line)
} else {
echo "The key 'index' does NOT exists.\n";
}
?>

Inline documentation

The phpDocumentor type documentation blocks are required in four places:

  • At the beginning of each PHP source code file

  • Preceding each class definition

  • Preceding each method definition

  • Preceding each class-level property definition

Following is a table of required and recommended phpDocumentor tags. Please refer to the chapter on documentation for more details.

Tag name

File

Class

Method

Property

Usage

abstract

x

x

x

  

access

  

x

x

public, private, or protected

author

x

x

[x]

[x]

author name <[email protected]>

copyright

x

   

name date

deprec

  

[x]

[x]

 

deprecated

  

[x]

[x]

 

example

[x]

[x]

[x]

 

path or url to example

extends

 

[x]

  

class name

global

   

[x]

type $variableName

link

[x]

[x]

[x]

[x]

url

package

x

x

  

package name

param

  

x

 

type [$name] description [default]

return

  

x

 

type description

see

[x]

[x]

[x]

 

file, class, or function name

since

  

x

 

date added to class

static

  

[x]

[x]

 

subpackage

[x]

[x]

  

sub-package name

throws

  

[x]

 

exceptionName

todo

[x]

[x]

[x]

 

task description

var

   

x

type $name description [default]

version

 

x

[x]

 

auto-generated by source control

where x = required [x] = recommended where applicable blank = do not use

 

Coding standard adherence and verification


Now that we have gone through the effort of defining a detailed coding standard, how do we make sure that everybody on the team adheres to it? Even if you are the only one writing code, it would help to have a way of checking that you are following your own standard. Looking through every single file and inspecting it for coding standard adherence would do the job, but it would also be mind-numbingly boring and repetitive. Luckily, repeating things is something at which computers excel.

PHP_CodeSniffer for automated checking

PHP_CodeSniffer is a free package that parses PHP source files and checks them for compliance with pre-defined coding standards. The software comes pre-defined with some common coding standards, namely the PEAR, Zend, Squiz, MySource, and PHPCS standards. But luckily, the author made the package easily extensible. Defining your own coding standard against which PHP_CodeSniffer can check is simply a matter of extending some classes and implementing some methods. Naturally, PHP_CodeSniffer is written in object-oriented PHP.

Note

In their own words: "PEAR is a framework and distribution system for reusable PHP components."

What this means is that PHP comes with a simple installer that can be used to automatically install any of the various libraries that are categorized, organized, documented, and made available for download from the PEAR site:

http://pear.php.net

Installing PHP_CodeSniffer

Let's start by installing PHP_CodeSniffer directly from the PEAR site using their handy command line installer. When I built and installed PHP on my system, I had it put everything in a sub-folder of my local Apache installation. In my case, the PHP root folder is therefore in /usr/local/apache2/php/. This is where you can find the PEAR executable among other handy PHP utilities. The most recent version at the time of this writing is 1.2.1.

Installing the package is simply a matter of telling the pear executable to download and install it. You should see something similar to the following lines cascading down your screen.

Afterwards, listing the contents of your PHP installations bin directory should show the phpcs executable in addition to everything else you had in that directory.

Note

Just like PHP itself, the PEAR installer supports many operating systems. I installed PHP_CodeSniffer on Mac OS X, but the same procedure will work on any of the operating systems on which PHP itself runs. Also, if you are running into problems during the installation, I urge you to visit the PEAR site's support section and FAQ page.

Basic usage

Using PHP_CodeSniffer to validate one or more files against a coding standard is pretty straightforward. You simply invoke the phpcs executable and pass the file or directory to check as an argument.

Here is some sample output from checking one of the listings for this chapter:

As you can see from the output, checking against the default coding standard (Zend), phpcs has found three errors and one warning. For each of the problems it finds, phpcs reports the line number of the source file, the severity of the issue (error, warning, and so on) and a description. The three errors it found aren't really part of the coding standard that we defined earlier in this chapter. However, the warning indicates that the maximum line number, which we defined in our own standard to be 80 characters, was exceeded. We could have suppressed the warnings by adding the n switch to the command line. At this point, we should fix the issue and re-run phpcs to confirm that there are no additional issues detected.

Slightly advanced usage

Rather than duplicating the PHP_CodeSniffer documentation here, I want to use this section to briefly list the available runtime options and highlight some of the more useful ones.

There are various command line options that let you customize what to check, how to check, and how to format the ouput. Type phpcs --help to see all of the arguments understood by the script.

PHP_CodeSniffer comes with several predefined coding standards. Typing phpcs i will tell which coding standards are installed. The version of the tool I installed came with the following standards: MySource, PEAR, PHPCS, Squiz, and Zend. To tell phpcs to check against a specific standard, simply add the following option to the command line: --standard=Zend.

The last useful command line switch I would like to point out is the --report=summary argument. When recursively checking a directory of source files, the output can get rather long. This switch prints a summary report with one line per file rather than outputting each individual issue it identified. Here is the summary report for all code listings in this chapter up to this point.

Here is a list of additional features you can customize from the command line. Please refer to the PHP_CodeSniffer online documentation for details.

  • Including/excluding files based on their extension

  • Excluding files or directories based on their name

  • Limiting the checking to select sub-sections of the coding standard (sniffs)

  • Verbosity of the output

  • Permanently setting / deleting configuration options for future invocations of phpcs including defaults for:

    • Coding standard

    • Report format

    • Show/hide warnings in output

    • Tab width (numbers of space characters)

    • Coding standard specific configuration options

Validating against your own coding standard

Being able to check code against an existing and established coding standard, such as Zend, is useful, but the real power of PHP_CodeSniffer lies in its extensibility. By defining our own coding standard in a format PHP_CodeSniffer understands, we can use it to check conformance to our own standard.

Defining your own coding standard involves a three-step process. The steps are as follows:

  1. 1. Create the directory structure to contain your PHP_CodeSniffer coding standard definition.

  2. 2. Create a class file to allow PHP_CodeSniffer to interact with and learn about our coding standard.

  3. 3. Create the individual rule files, called Sniffs in PHP_CodeSniffer lingo.

Note

Terminology: In PHP_CodeSniffer parlance, Sniffs are individual class files that contain the logic to validate a single coding standard rule. For example, you might have a file called LineLengthSniff.php that contains the definition for class LineLengthSniff. The logic in this class will be invoked each time PHP_CodeSniffer wants to check whether the length of a line of code conforms to the defined standard.

Creating the directories

The directory structure for each coding standard definition is simple. From the command line, the following set of commands can be used to create the initial set of directories and files of a hypothetical coding PHP_CodeSniffer standard definition.

We start by creating a directory to hold all the files associated with our coding standard definition, ProjectStandard. In that directory, we create a class file called ProjectStandardCodingStandard.php that identifies our directory as one containing a PHP_CodeSniffer coding standard definition. This class is also responsible for communicating with the main phpcs executable regarding some of the details of our coding standard (more about that later).

Next we create a directory called Sniff, which will contain descriptions of individual coding standard rules, or collections (directories thereof). In our case, we plan on creating rules regarding naming conventions and line length, which is why we are creating directories NamingConventions and LineLength inside the Sniffs directory. Each of these sub-directories then contains one or more individual sniff files: LimitLineLengthSniff.php, ClassNamesSniff.php, VariableNamesSniff.php, and PropertyNamesSniff.php.

The main coding standard class file

In the previous step, we have already created a placeholder for the main class file in our ProjectStandard directory. Now let's put some code in there and identify ourselves as a PHP_CodeSniffer coding standard.

<?php
// make sure the parent class is in our include path
if (class_exists('PHP_CodeSniffer_Standards_CodingStandard', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Standards_CodingStandard not found');
}
// our main coding standard class definition
class PHP_CodeSniffer_Standards_ProjectStandard_ProjectStandardCodingStandard extends PHP_CodeSniffer_Standards_CodingStandard
{
// include sniffs from other directories or even whole coding standards
// great way to create your standard and build on it
public function getIncludedSniffs()
{
// return an array of sniffs, directories of sniffs,
// or coding standards to include
return array(
'Generic'
);
}
// exclude sniffs from previously included ones
public function getExcludedSniffs()
{
// return a list of sniffs or directories of sniffs to exclude
return array(
'Generic/Sniffs/LineLengthSniff.php'
);
}
}
?>

Our main class extends PHP_CodeSniffer_Standards_CodingStandard. This is required of all classes identifying a coding standard to be used with phpcs. However, the two methods we are implementing, getIncludedSniffs() and getExcludedSniffs() are pretty important because they let us assemble our own coding standard from parts of existing standards, thus saving us a lot of time since we don't have to write all the sniffs ourselves. Both classes return simple arrays. The items in the array are either names of existing coding standards, paths to directories of sniffs of existing coding standards, or paths to individual sniff files. For example, it turns out that our own coding standard is pretty close to the "Generic" coding standard included with PHP_CodeSniffer. Therefore, to make things easier for us, we include the whole "Generic" coding standard in the getIncludedSniffs() method, but choose to exclude that standard's LineLengthSniff.php in the getExcludedSniffs() method.

Creating Sniffs

Each of the rules we formulated to express our coding standard earlier in the chapter, can be described using a sniff class file that PHP_CodeSniffer can use. However, before we jump in and really get our hands dirty, it is a good idea to review how tokenization works. After all, PHP_CodeSniffer builds on and expands PHP's inbuilt tokenizer extension.

Note

Tokenization is the process of breaking input text into meaningful parts. When combined with a classification and/or description, each such part is considered a token.

Tokenization

PHP uses the Zend Engine's tokenizer at its core to parse and interpret PHP source files. Lucky for us, the tokenizer is also directly accessible via two functions in the language's top-level namespace: token_get_all() and token_get_name().

Tokenization consists of taking input text and breaking it up into meaningful segments. Each segment can optionally carry a label, explanation, or additional detail. Let's look at an example of PHP code being tokenized by the Zend Engine.

<?php
// get the contents of this file into a variable
$thisFile = file_get_contents(__FILE__);
// get the token stack
$tokenStack = token_get_all($thisFile);
$currentLine = 0;
// output each token & look up the corresponding name
foreach ($tokenStack as $token) {
// most tokens are arrays
if (is_array($token)) {
if ($currentLine < $token[2]) {
$currentLine++;
echo "Line $currentLine:\n";
}
echo "\t" . token_name($token[0]) . ': ' . rtrim($token[1]) . "\n";
// some tokens are just strings
} else {
echo "\tString: " . rtrim($token) . "\n";
}
}
?>

The above code snippet runs itself through the tokenizer, which results in the following output:

We formatted our output a littler nicer, but the first token essentially looks like this:

Array(
[0] => 367
[1] => <?php
[2] => 1
)

In this case, 367 is the value of the parser token, which corresponds to T_OPEN_TAG when we look it up with the token_name() function.<?php is the actual text of the token and 1 is the line number on which the token occurs. You can look up the complete list of tokenizer token constants in the online PHP manual, or the following code snippet will list the ones that are defined for your version of PHP.

<?php
// get all constants organized by category
$allTokens = get_defined_constants(true);
// we're only interested in tokenizer constants
print_r($allTokens["tokenizer"]);
?>

As you can see for yourself, tokens contain a lot of information that is useful to programmatically understand what an analyzed portion of code is doing. PHP_CodeSniffer builds upon the existing tokenization extension and built-in tokens by providing additional tokens to provide even finer granularity when examining PHP code.

Writing our first sniff

Now that you know what tokens are, it will be much easier to understand what the individual sniffs are doing. First of all, a sniff registers with the main executable the tokens in which it is interested using the register() method. That way, the main code can hand over execution to the sniff's process() method whenever it encounters such a token. For example, a sniff trying to validate that a code file has the proper PHP opening and/or closing tag might register interest in the T_OPEN_TAG with the parent code. That is exactly what we're doing in the following listing:

<?php
// sniff class definition must implement the
// PHP_CodeSniffer_Sniff interface
class ProjectStandard_Sniffs_Syntax_FullPhpTagsSniff implements PHP_CodeSniffer_Sniff
{
// register for the tokens we're interested in
public function register()
{
return array(T_OPEN_TAG);
}
// process each occurrence of the token in this method
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// warn if the opening PHP tag is not the first token in the file
if ($stackPtr != 0) {
$phpcsFile->addWarning('Nothing should precede the PHP open tag.', $stackPtr);
}
// error if full PHP open tag is not used
if ($tokens[$stackPtr]['content'] != '<?php') {
$phpcsFile->addError('Only full PHP opening tags are allowed.', $stackPtr);
}
// all files must have closing tag
if ($token[sizeof($tokens) - 1]['type'] != T_CLOSE_TAG) {
$phpcsFile->addError('All files must end with a closing PHP tag.', $stackPtr);
}
}
sniffssniffswriting}
?>

Let's take a closer look at the process() method, which takes two parameters. The first one is a reference to a PHP_CodeSniffer_File object, which we can use to access the token stack. The second argument is an array index to the current token in the stack.

Armed with that information, we can start validating the code via the token stack. First, we use the PHP_CodeSniffer_File's addWarning() method to display a warning message whenever the very first token is not the PHP open tag. Next, we use the addError() method to display an error message to the user whenever the opening tag doesn't match the string "<?php" since that is the only opening tag that our coding standard allows. Lastly, we display an error message if the last token in the stack is anything other than the closing PHP tag.

That's it. The main phpcs executable does the rest. It tokenizes the input file(s), calls all registered sniffs for each occurrence of their respective token, and displays nicely formatted output to the user.

You may have noticed in the above listing that we used the values of the token's 'content' and 'type' attributes. If you recall, the tokens returned by the standard PHP tokenizer did not have those attributes. Instead, PHP_CodeSniffer adds those and other attributes. Following is a list of token attributes that are always available. Depending on the type of token, additional attributes might be available. You should consult the PHP_CodeSniffer API documentation for details.

Attribute name

Example

Description

code

301

The token type code (see token_get_all())

content

if

The token content

type

T_IF

The token name

line

56

The line number when the token is located

column

12

The column in the line where this token starts (starts from 1)

level

2

The depth a token is within the scopes open

Conditions

Array(

2 => 50, 9 => 353

)

A list of scope condition token positions => codes that opened the scopes that this token exists in (see conditional tokens)

Extending existing sniffs

We have already seen that we can include sniffs from other coding standards in our own. However, we can take it a step further and make an existing sniff do all the work while still implementing our own standard. For example, the "Generic" coding standard includes a sniff to check for maximum line length. As it happens, the suggested maximum line length is 80 characters— the same as in our own standard. However, the absolute maximum line length is 100; whereas, our standard allows for up to 120 characters per line. Therefore, all we have to do is extend the existing sniff and overwrite the protected property $absoluteLineLimit as in the following listing.

<?php
if (class_exists('Generic_Sniffs_Files_LineLengthSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class Generic_Sniffs_Files_LineLengthSniff not found');
}
// class to check line length in number of characters
// note: we're overwriting an existing sniff from the generic coding standard
class Zend_Sniffs_Files_LineLengthSniff extends Generic_Sniffs_Files_LineLengthSniff
{
// we generate an error when exceeding the absolute
// maximum line length
protected $absoluteLineLimit = 120;

}
?>

Automated code checks

Even though PHP_CodeSniffer is available, there is no guarantee that individual developers will actually take advantage of it. However, in a team environment, the lead developer can take several steps to make sure the team members adhere to the chosen common standard. First, the code base should be scheduled for an automated check once a day during active development. A simple (insert you favorite scheduler utility here) job can process all the source files and send an email to everybody in the team.

However, it is possible to take things a step further. Assuming that you are using a source code control system, most of these systems provide hooks at various stages of checking out or committing source code. The most commonly used hook is the pre-commit hook. In other words, the source code control system executes any number of user-configurable steps before committing the code. The outcome of these steps impact whether the use is allowed to commit the code or not. In the case of our coding standard, we can configure the pre-commit hook to run any PHP source files being committed through PHP_CodeSniffer and only proceed if no errors and/or warnings are being generated. In essence, this is a way that your team only accepts contributions from individual developers if they adhere to the team's coding standard.

For a detailed example of how to configure the Subversion source code control system with a PHP_CodeSniffer pre-commit hook, please consult the chapter on source code and version control.

 

Summary


I think we have come full circle within the course of this chapter. We started with a philosophical discussion; as well as an examination of the pros and cons of a common coding standard. We then proceeded to formulate a coding standard that can serve as a foundation for any PHP development project— whether it consists of a single developer or dozens spread throughout the globe.

Realizing that having a standard alone is not enough, we looked at PHP_CodeSniffer as a tool for validating code against a pre-defined standard. We even learned how to translate our coding guidelines to PHP code that PHP_CodeSniffer can use when checking the source files. Lastly, we briefly discussed that automating or integrating source validation is an effective way of actually enforcing the standard without having to waste too much time reviewing code manually.

The standard we defined in this chapter is not the answer to all your coding standard needs. I'm sure you were objecting to some of the rules I defined as you were reading through them. That's ok. The important thing is to have a coding standard at all. You can never make everybody happy, but you can make sure that the team benefits from the coding standard, even if the members don't agree with each and every detail.

Rather than blindly adopting the coding standard in this or any other standard for that matter, you might want to take the time to examine it and customize it for your purposes. Also, a coding standard evolves over time along with the language itself. With PHP6 due to be released in the near future, we will have to revisit our standard and see how to best improve it to reflect all the exciting new features.

About the Author

  • Dirk Merkel

    Dirk Merkel is the owner of Waferthin Web Works LLC. In his spare time, he likes to ruin perfectly good open-source projects by submitting unsolicited patches. He also writes about Web development. He lives in San Diego with his lovely wife and two wonderful daughters. Dirk can be reached at  [email protected]

    Browse publications by this author

Latest Reviews

(1 reviews total)
Inderdaad voor geschoolde starters. Goed overzicht van installatie opzet.
Book Title
Access this book and the full library for FREE
Access now