Clientes:

PHP CODING STANDARDS

 

This is the best coding standard for PHP, this Standard has been defined by PEAR Team.  And here is:

 

Indenting and Line Length

Use an indent of 4 spaces, with no tabs. This helps to avoid problems with diffs, patches, CVS history and annotations.

For Emacs you should set indent-tabs-mode to nil. Here is an example mode hook that will set up Emacs (ensure that it is called when you are editing PHP files):

(defun php-mode-hook ()
(setq tab-width 4
c-basic-offset 4
c-hanging-comment-ender-p nil
indent-tabs-mode
(not
(and (string-match "/\\\\(PEAR\\\\|pear\\\\)/" (buffer-file-name))
(string-match "\\.php$" (buffer-file-name))))))

Here are Vim rules for the same thing:

set expandtab
set shiftwidth=4
set softtabstop=4
set tabstop=4

It is recommended to keep lines at approximately 75-85 characters long for better code readability.

 


Control Structures

These include if, for, while, switch, etc. Here is an example if statement, since it is the most complicated of them:

if ((condition1) || (condition2)) {
action1;
} elseif ((condition3) && (condition4)) {
action2;
} else {
defaultaction;
}

Control statements should have one space between the control keyword and opening parenthesis, to distinguish them from function calls.

You are strongly encouraged to always use curly braces even in situations where they are technically optional. Having them increases readability and decreases the likelihood of logic errors being introduced when new lines are added.

For switch statements:

switch (condition) {
case 1:
action1;
break;

case 2:
action2;
break;

default:
defaultaction;
break;
}

 


Function Calls

Functions should be called with no spaces between the function name, the opening parenthesis, and the first parameter; spaces between commas and each parameter, and no space between the last parameter, the closing parenthesis, and the semicolon. Here’s an example:

$var = foo($bar, $baz, $quux);

As displayed above, there should be one space on either side of an equals sign used to assign the return value of a function to a variable. In the case of a block of related assignments, more space may be inserted to promote readability:

$short         = foo($bar);
$long_variable = foo($baz);

 


Class Definitions

Class declarations have their opening brace on a new line:

class Foo_Bar
{

//... code goes here

}

 


Function Definitions

Function declarations follow the “K&R style”:

function fooFunction($arg1, $arg2 = '')
{
if (condition) {
statement;
}
return $val;
}

Arguments with default values go at the end of the argument list. Always attempt to return a meaningful value from a function if one is appropriate. Here is a slightly longer example:

function connect(&$dsn, $persistent = false)
{
if (is_array($dsn)) {
$dsninfo = &$dsn;
} else {
$dsninfo = DB::parseDSN($dsn);
}

if (!$dsninfo || !$dsninfo['phptype']) {
return $this->raiseError();
}

return true;
}

 


Comments

Complete inline documentation comment blocks (docblocks) must be provided. Please read the Sample File and Header Comment Blocks sections of the Coding Standards to learn the specifics of writing docblocks for PEAR packages. Further information can be found on the phpDocumentor website.

Non-documentation comments are strongly encouraged. A general rule of thumb is that if you look at a section of code and think “Wow, I don’t want to try and describe that”, you need to comment it before you forget how it works.

C style comments (/* */) and standard C++ comments (//) are both fine. Use of Perl/shell style comments (#) is discouraged.

 


Including Code

Anywhere you are unconditionally including a class file, use require_once. Anywhere you are conditionally including a class file (for example, factory methods), use include_once. Either of these will ensure that class files are included only once. They share the same file list, so you don’t need to worry about mixing them - a file included with require_once will not be included again by include_once.

 

Note: include_once and require_once are statements, not functions. Parentheses should not surround the subject filename.

 


PHP Code Tags

Always use <?php ?> to delimit PHP code, not the <? ?> shorthand. This is required for PEAR compliance and is also the most portable way to include PHP code on differing operating systems and setups.

 


Header Comment Blocks

All source code files in the PEAR repository shall contain a “page-level” docblock at the top of each file and a “class-level” docblock immediately above each class. Below are examples of such docblocks.

<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
* Short description for file
*
* Long description for file (if any)...
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category CategoryName
* @package PackageName
* @author Original Author <author@example.com>
* @author Another Author <another@example.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id:$
* @link http://pear.php.net/package/PackageName
* @see NetOther, Net_Sample::Net_Sample()
* @since File available since Release 1.2.0
* @deprecated File deprecated in Release 2.0.0
*/

/*
* Place includes, constant defines and $_GLOBAL settings here.
* Make sure they have appropriate docblocks to avoid phpDocumentor
* construing they are documented by the page-level docblock.
*/

/**
* Short description for class
*
* Long description for class (if any)...
*
* @category CategoryName
* @package PackageName
* @author Original Author <author@example.com>
* @author Another Author <another@example.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: @package_version@
* @link http://pear.php.net/package/PackageName
* @see NetOther, Net_Sample::Net_Sample()
* @since Class available since Release 1.2.0
* @deprecated Class deprecated in Release 2.0.0
*/
class Foo_Bar
{
}

?>

 


Required Tags That Have Variable Content

 

Short Descriptions
Short descriptions must be provided for all docblocks. They should be a quick sentence, not the name of the item. Please read the Coding Standard’s Sample File about how to write good descriptions.
PHP Versions
One of the following must go in the page-level docblock:

.
* PHP version 4
* PHP version 5
* PHP versions 4 and 5
@license
There are several possible licenses. One of the following must be picked and placed in the page-level and class-level docblocks:

.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @license http://www.freebsd.org/copyright/freebsd-license.html BSD License (2 Clause)
* @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
* @license http://www.freebsd.org/copyright/license.html BSD License (4 Clause)
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @license http://www.php.net/license/3_0.txt PHP License 3.0

For more information, see the PEAR Group’s Licensing Announcement.

@link
The following must be used in both the page-level and class-level docblocks. Of course, change “PackageName” to the name of your package. This ensures the generated documentation links back your package.

.
* @link http://pear.php.net/package/PackageName
@author
There’s no hard rule to determine when a new code contributor should be added to the list of authors for a given source file. In general, their changes should fall into the “substantial” category (meaning somewhere around 10% to 20% of code changes). Exceptions could be made for rewriting functions or contributing new logic.

Simple code reorganization or bug fixes would not justify the addition of a new individual to the list of authors.

@since
This tag is required when a file or class is added after the package’s initial release. Do not use it in an initial release.
@deprecated
This tag is required when a file or class is no longer used but has been left in place for backwards compatibility.

 


Optional Tags

 

@copyright
Feel free to apply whatever copyrights you desire. When formatting this tag, the year should be in four digit format and if a span of years is involved, use a hyphen between the earliest and latest year. The copyright holder can be you, a list of people, a company, the PHP Group, etc. Examples:

.
* @copyright 2003 John Doe and Jennifer Buck
* @copyright 2001-2004 John Doe
* @copyright 1997-2004 The PHP Group
* @copyright 2001-2004 XYZ Corporation
License Summary
If you are using the PHP License, use the summary text provided above. If another license is being used, please remove the PHP License summary. Feel free to substitute it with text appropriate to your license, though to keep things easy to locate, please preface the text with LICENSE: .
@see
Add a @see tag when you want to refer users to other sections of the package’s documentation. If you have multiple items, separate them with commas rather than adding multiple @see tags.

 


Order and Spacing

To ease long term readability of PEAR source code, the text and tags must conform to the order and spacing provided in the example above. This standard is adopted from the JavaDoc standard.

 


@package_version@ Usage

There are two ways to implement the @package_version@ replacements. The procedure depends on whether you write your own package.xml files or if you use the PackageFileManager.

For those authoring package.xml files directly, add a <replace> element for each file. The XML for such would look something like this:

<file name="Class.php">
<replace from="@package_version@" to="version" type="package-info" />
</file>

Maintainers using the PackageFileManager need to call addReplacement() for each file:

$pkg->addReplacement('filename.php', 'package-info',
'@package_version@', 'version');

 


Transition Policy

 

Existing Small Packages
Existing packages that have only a few files are required to adopt these docblocks before the next release.
Existing Large Packages
Existing packages with many files are encouraged to adopt the new headers as soon as possible. When such packages come out with a new major version upgrade, these docblocks must be implemented therein.
New and Unreleased Packages
New packages and existing packages which have no releases yet must include these docblocks before their first release.

 


Using CVS

This section applies only to packages using CVS at cvs.php.net.

Include the $Id$ CVS keyword in each file.

The rest of this section assumes that you have basic knowledge about CVS tags and branches.

CVS tags are used to label which revisions of the files in your package belong to a given release. Below is a list of the required and suggested CVS tags:

 

RELEASE_n_n_n
(required) Used for tagging a release. If you don’t use it, there’s no way to go back and retrieve your package from the CVS server in the state it was in at the time of the release.
QA_n_n_n
(branch, optional) If you feel you need to roll out a release candidate before releasing, it’s a good idea to make a branch for it so you can isolate the release and apply only those critical fixes before the actual release. Meanwhile, normal development may continue on the main trunk.
MAINT_n_n_n
(branch, optional) If you need to make “micro-releases” (for example 1.2.1 and so on after 1.2.0), you can use a branch for that too, if your main trunk is very active and you want only minor changes between your micro-releases.

Only the RELEASE tag is required, the rest are recommended for your convenience. Below is an example of how to tag the 1.2.0 release of the Money_Fast package:

$ cd pear/Money_Fast
$ cvs tag RELEASE_1_2_0
T Fast.php
T README
T package.xml

By doing this you make it possible for the PEAR web site to take you through the rest of your release process. Here’s an example of how to create a QA branch:

$ cvs tag QA_2_0_0_BP

$ cvs rtag -b -r QA_2_0_0_BP QA_2_0_0
$ cvs update -r QA_2_0_0
$ cvs tag RELEASE_2_0_0RC1
…and then the actual release, from the same branch:
$ cvs tag RELEASE_2_0_0

The QA_2_0_0_BP tag is a “branch point” tag, which is the start point of the tag. It’s always a good idea to start a CVS branch from such branch points. MAINT branches may use the RELEASE tag as their branch point.

 


Example URLs

Use example.com, example.org and example.net for all example URLs and email addresses, per RFC 2606.

 


Naming Conventions

 

Classes

Classes should be given descriptive names. Avoid using abbreviations where possible. Class names should always begin with an uppercase letter. The PEAR class hierarchy is also reflected in the class name, each level of the hierarchy separated with a single underscore. Examples of good class names are:

Log Net_Finger HTML_Upload_Error

 


Functions and Methods

Functions and methods should be named using the “studly caps” style (also referred to as “bumpy case” or “camel caps”). Functions should in addition have the package name as a prefix, to avoid name collisions between packages. The initial letter of the name (after the prefix) is lowercase, and each letter that starts a new “word” is capitalized. Some examples:

connect() getData() buildSomeWidget() XML_RPC_serializeData()

Private class members (meaning class members that are intended to be used only from within the same class in which they are declared; PHP does not yet support truly-enforceable private namespaces) are preceded by a single underscore. For example:

_sort() _initTree() $this->_status

 

Note: The following applies to PHP5.

Protected class members (meaning class members that are intended to be used only from within the same class in which they are declared or from subclasses that extend it) are not preceded by a single underscore. For example:

protected $somevar protected function initTree()

 


Constants

Constants should always be all-uppercase, with underscores to separate words. Prefix constant names with the uppercased name of the class/package they are used in. For example, the constants used by the DB:: package all begin with DB_.

 

Note: The true, false and null constants are excepted from the all-uppercase rule, and must always be lowercase.

 


Global Variables

If your package needs to define global variables, their name should start with a single underscore followed by the package name and another underscore. For example, the PEAR package uses a global variable called $_PEAR_destructor_object_list.

 


File Formats

All scripts contributed to PEAR must:

  • Be stored as ASCII text
  • Use ISO-8859-1 character encoding
  • Be Unix formatted

    “Unix formatted” means two things:

    1) Lines must end only with a line feed (LF). Line feeds are represented as ordinal 10, octal 012 and hex 0A. Do not use carriage returns (CR) like Macintosh computers do or the carriage return/line feed combination (CRLF) like Windows computers do.

    2) There should be one line feed after the closing PHP tag (?>). This means that when the cursor is at the very end of the file, it should be one line below the closing PHP tag.

 


E_STRICT-compatible code

Starting on 01 January 2007, all new code that is suggested for inclusion into PEAR must be E_STRICT-compatible. This means that it must not produce any warnings or errors when PHP’s error reporting level is set to E_STRICT.

The development of existing packages that are not E_STRICT-compatible can continue as usual. If however a new major version of the package is released, this major version must then be E_STRICT-compatible.

More details on this part of the Coding Standards can be found in the corresponding PEPr proposal.

 


Error Handling Guidelines

This part of the Coding Standards describes how errors are handled in PEAR packages that are developed for PHP 5 and 6. It uses Exceptions, introduced in PHP 5.0 with Zend Engine 2, as the error handling mechanism.

 


Definition of an error

An error is defined as an unexpected, invalid program state from which it is impossible to recover. For the sake of definition, recovery scope is defined as the method scope. Incomplete recovery is considered a recovery.

Example 4-1. One pretty straightforward example for an error

/*
* Connect to Specified Database
*
* @throws Example_Datasource_Exception when it can't connect
* to specified DSN.
*/
function connectDB($dsn)
{
$this->db =& DB::connect($dsn);
if (DB::isError($this->db)) {
throw new Example_Datasource_Exception(
"Unable to connect to $dsn:" . $this->db->getMessage()
);
}
}

In this example the objective of the method is to connect to the given DSN. Since it can’t do anything but ask PEAR DB to do it, whenever DB returns an error, the only option is to bail out and launch the exception.

Example 4-2. Error handling with recovery

/*
* Connect to one of the possible databases
*
* @throws Example_Datasource_Exception when it can't connect to
* any of the configured databases.
*
* @throws Example_Config_Exception when it can't find databases
* in the configuration.
*/

function connect(Config $conf)
{
$dsns =& $conf->searchPath(array('config', 'db'));
if ($dsns === FALSE) throw new Example_Config_Exception(
'Unable to find config/db section in configuration.'
);

$dsns =& $dsns->toArray();

foreach($dsns as $dsn) {
try {
$this->connectDB($dsn);
return;
} catch (Example_Datasource_Exception e) {
// Some warning/logging code recording the failure
// to connect to one of the databases
}
}
throw new Example_Datasource_Exception(
'Unable to connect to any of the configured databases'
);
}

This second example shows an exception being caught and recovered from. Although the lower level connectDB() method is unable to do anything but throw an error when one database connection fails, the upper level connect() method knows the object can go by with any one of the configured databases. Since the error was recovered from, the exception is silenced at this level and not rethrown.

Example 4-3. Incomplete recovery

/*
* loadConfig parses the provided configuration. If the configuration
* is invalid, it will set the configuration to the default config.
*
*/
function loadConfig(Config $conf)
{
try {
$this->config = $conf->parse();
} catch (Config_Parse_Exception e) {
// Warn/Log code goes here
// Perform incomplete recovery
$this->config = $this->defaultConfig;
}
}

The recovery produces side effects, so it is considered incomplete. However, the program may proceed, so the exception is considered handled, and must not be rethrown. As in the previous example, when silencing the exception, logging or warning should occur.

 


Error Signaling in PHP 5 PEAR packages

Error conditions in PEAR packages written for PHP 5 must be signaled using exceptions. Usage of return codes or return PEAR_Error objects is deprecated in favor of exceptions. Naturally, packages providing compatibility with PHP 4 do not fall under these coding guidelines, and may thus use the error handling mechanisms defined in the PHP 4 PEAR coding guidelines.

An exception should be thrown whenever an error condition is met, according to the definition provided in the previous section. The thrown exception should contain enough information to debug the error and quickly identify the error cause. Note that, during production runs, no exception should reach the end-user, so there is no need for concern about technical complexity in the exception error messages.

The basic PEAR_Exception contains a textual error, describing the program state that led to the throw and, optionally, a wrapped lower level exception, containing more info on the lower level causes of the error.

The kind of information to be included in the exception is dependent on the error condition. From the point of view of exception throwing, there are three classes of error conditions:

  1. Errors detected during precondition checks
  2. Lower level library errors signaled via error return codes or error return objects
  3. Uncorrectable lower library exceptions

Errors detected during precondition checks should contain a description of the failed check. If possible, the description should contain the violating value. Naturally, no wrapped exception can be included, as there isn’t a lower level cause of the error. Example:

Example 4-4.

function divide($x, $y)
{
if ($y == 0) {
throw new Example_Aritmetic_Exception('Division by zero');
}
}

Errors signaled via return codes by lower level libraries, if unrecoverable, should be turned into exceptions. The error description should try to convey all information contained in the original error. One example, is the connect method previously presented:

Example 4-5.

/*
* Connect to Specified Database
*
* @throws Example_Datasource_Exception when it can't connect to specified DSN.
*/
function connectDB($dsn)
{
$this->db =& DB::connect($dsn);
if (DB::isError($this->db)) {
throw new Example_Datasource_Exception(
"Unable to connect to $dsn:" . $this->db->getMessage()
);
}
}

Lower library exceptions, if they can’t be corrected, should either be rethrown or bubbled up. When rethrowing, the original exception must be wrapped inside the one being thrown. When letting the exception bubble up, the exception just isn’t handled and will continue up the call stack in search of a handler.

Example 4-6. Rethrowing an exception

function preTaxPrice($retailPrice, $taxRate)
{
try {
return $this->divide($retailPrice, 1 + $taxRate);
} catch (Example_Aritmetic_Exception e) {
throw new Example_Tax_Exception('Invalid tax rate.', e);
}
}

Example 4-7. Letting exceptions bubble up

function preTaxPrice($retailPrice, $taxRate)
{
return $this->divide($retailPrice, 1 + $taxRate);
}

The case between rethrowing or bubbling up is one of software architecture: Exceptions should be bubbled up, except in these two cases:

  1. The original exception is from another package. Letting it bubble up would cause implementation details to be exposed, violating layer abstraction, conducing to poor design.
  2. The current method can add useful debugging information to the received error before rethrowing.

 


Exceptions and normal program flow

Exceptions should never be used as normal program flow. If removing all exception handling logic (try-catch statements) from the program, the remaining code should represent the “One True Path” — the flow that would be executed in the absence of errors.

This requirement is equivalent to requiring that exceptions be thrown only on error conditions, and never in normal program states.

One example of a method that wrongly uses the bubble up capability of exceptions to return a result from a deep recursion:

Example 4-8.

/**
* Recursively search a tree for string.
* @throws ResultException
*/
public function search(TreeNode $node, $data)
{
if ($node->data === $data) {
throw new ResultException( $node );
} else {
search( $node->leftChild, $data );
search( $node->rightChild, $data );
}
}

In the example the ResultException is simply using the “eject!” qualities of exception handling to jump out of deeply nested recursion. When actually used to signify an error this is a very powerful feature, but in the example above this is simply lazy development.

 


Exception class hierarchies

All of PEAR packages exceptions must be descendant from PEAR_Exception. PEAR_Exception provides exception wrapping abilities, absent from the top level PHP Exception class, and needed to comply with the previous section requirements.

Aditionally, each PEAR package must provide a top level exception, named <Package_Name>_Exception. It is considered best practice that the package never throws exceptions that aren’t descendant from its top level exception.

 


Documenting Exceptions

Because PHP, unlike Java, does not require you to explicitly state which exceptions a method throws in the method signature, it is critical that exceptions be thoroughly documented in your method headers.

Example 4-9. Exceptions should be documented using the @throws phpdoc keyword

/**
* This method searches for aliens.
*
* @return array Array of Aliens objects.
* @throws AntennaBrokenException If the impedence readings indicate
* that the antenna is broken.
*
* @throws AntennaInUseException If another process is using the
* antenna already.
*/
public function findAliens($color = 'green');

In many cases middle layers of an application will rewrap any lower-level exceptions into more meaningful application exceptions. This also needs to be made clear:

Example 4-10.

/**
* Load session objects into shared memory.
*
* @throws LoadingException Any lower-level IOException will be wrapped
* and re-thrown as a LoadingException.
*/
public function loadSessionObjects();

In other cases your method may simply be a conduit through which lower level exceptions can pass freely. As challenging as it may be, your method should also document which exceptions it is not catching.

Example 4-11.

/**
* Performs a batch of database queries (atomically, not in transaction).
* @throws SQLException Low-level SQL errors will bubble up through this method.
*/
public function batchExecute();

 


Exceptions as part of the API

Exceptions play a critical role in the API of your library. Developers using your library depend on accurate descriptions of where and why exceptions might be thrown from your package. Documentation is critical. Also maintaining the types of messages that are thrown is also an important requirement for maintaining backwards-compatibility.

Because Exceptions are critical to the API of your package, you must ensure that you don’t break backwards compatibility (BC) by making changes to exceptions.

Things that break BC include:

  • Any change to which methods throw exceptions.
  • A change whereby a method throws an exception higher in the inheritance tree. For example, if you changed your method to throw a PEAR_Exception rather than a PEAR_IOException, you would be breaking backwards compatibility.

Things that do not break BC:

  • Throwing a subclass of the original exception. For example, changing a method to throw PEAR_IOException when before it had been throwing PEAR_Exception would not break BC (provided that PEAR_IOException extends PEAR_Exception).

 


Best practices

There are other things not covered by PEAR Coding Standards which are mostly subject of personal preference and not directly related to readability of the code. Things like “single quotes vs double quotes” are features of PHP itself to make programming easier and there are no reasons not use one way in preference to another. Such best practices are left solely on developer to decide. The only recommendation could be made to keep consistency within package and respect personal style of other developers.

 

AddThis Feed Button

Sisfo Ltda. 2004-2008
Soluciones integrales para ambientes de negocios
Calle 19 No. 7-53, Piso # 9
Teléfono de Servicio al Cliente: +57 6 334 99 55