Summary
Information
Document Information
- Title: PEAR2 Coding Standards
- Version: 0.6.0
- Status: Draft
- Type: Standards
- Last updated: September 20th, 2009
Author(s) Information
- Name: Chuck Burgess
- Email: ashnazg@php.net
Past Author(s) Information
- Name: Gregory Beaver, Arnaud Limbourg
- Email: cellog@php.net, arnaud@php.net
Legal Information
- This proposal is under CC-By-Sharealike licensing.
Discussion List
- Mailing List: http://news.php.net/php.pear.dev
Introduction
PEAR 1.x is very successful at managing the universe of PEAR-installable code. The new Pyrus installer is designed to expand that universe to include code that can also be easily embedded in non-PEAR applications and that runs identically when simply unzipped and when installed. The PEAR2 repository must adhere to different coding conventions than the PEAR repository to make this possible.
This document itemizes all the changes to existing rules and coding standards.
Controversial items with regard to removal of handling dependencies have been extracted and moved to Controversial Changes
Expiration
These coding standards will expire on August 15, 2010, and must be reviewed and renewed by the next PEAR Group in order to continue.
Summary
This document describes the coding standards and coding conventions for the new PEAR2 repository.
Approach & Requirements
Packages that wish to be accepted into the PEAR2 repository must conform to these standards
Definition
Minimum PHP Dependency
The minimum PHP version supported will be the earliest version that supports namespaces, PHP version 5.3.0.
Requirement
No Exceptions to this rule
Package.xml 2.0
package.xml 2.0 or newer is required for all packages
Requirement
No Exceptions to this rule
Namespace prefix
All classes and functions must have a namespace of at the minimum PEAR2. An example:
<?php namespace PEAR2; class MyClass {} ?>
Classes may use longer namespaces, for instance an HTTP_Request class may instead choose to use this declarative syntax:
<?php namespace PEAR2\HTTP; class Request {} ?>
As such, underscores are no longer required of any classes if there is a namespace. Class PEAR2_HTTP_Request instead becomes PEAR2\HTTP\Request.
Package names, however, will use underscores, making PEAR2_HTTP_Request the package name.
Requirement
No Exceptions to this rule
use of include/require/require_once/include_once not allowed
Rule
include/require/require_once/include_once is not allowed for loading class files. Users will be expected to
load files either with autoload()
or a customized solution for more advanced users.
Instead, classes should simply be used. import with a comment describing the class's location must be used to document all internal dependencies (as written below). Instead of:
<?php require_once 'PEAR2/OtherPackage.php'; $class = new \PEAR2\OtherPackage; ?>
this class should simply be used:
<?php $class = new \PEAR2\OtherPackage; ?>
This allows packages to work without modification no matter how they are structured on-disk, including running out of a single large file, inside a phar archive, and provides much-needed flexibility.
Rationale
require_once introduces a rigidity to package structure that limits the possible uses of a PEAR package. Some of the problems:
- require_once can introduce up to a 10% performance penalty on high-volume sites using multi-processor web servers due to increased latency. However, most users would experience at most 2% performance penalty on single-processor systems (as measured by Yahoo! engineer Gopal Vijayaraghavan)
- include_path is required in order to use a package. This makes it difficult to bundle a PEAR package within another application with its own include_path, to create a single file containing needed classes, to move a PEAR package to a phar archive without extensive source code modification.
- when top-level require_once is mixed with conditional require_once, this can result in code that is uncacheable by opcode caches such as APC, which will be bundled with PHP 6.
- relative require_once requires that include_path already be set up to the correct value, making it impossible to use a package without proper include_path
Some of the benefits of require_once:
- you know right away if a file is missing, with a Fatal Error: missing file X (this is mitigated by using
autoload()
with PEAR2_Autoload()) - end-users don't need to know what files are within a package to use it. (also mitigated by using
autoload()
with PEAR2_Autoload())
The removal of require_once necessitates another method for loading internal dependencies, both files within a package and external files. This proposal introduces 2 possible methods for doing this:
- use
autoload()
in conjunction with PEAR2's custom autoload solution (found here in svn) - construct a customized solution for loading needed files
In all cases, the onus of loading needed files is shifted to the end user. However, for beginning users, the only required step is to load PEAR2/Autoload.php, which will be always bundled with new packages, but only extracted if used as unzip-and-go (pyrus would simply install the dependency on PEAR2, which would contain the needed base files PEAR2_Exception and PEAR2_Autoload).
<?php require '/full/path/to/PEAR2/Autoload.php'; // now you can start using all PEAR2 packages ?>
PEAR2/Autoload.php automatically sets up include_path if it does not contain the correct value, and also automatically declares autoload()
if
the user has not defined it.
Requirement
No Exceptions to this rule
Loading other classes
Inside optional component loading methods (like factory or driver loading, etc.) class_exists($classname, true) should be used where a “class not found” fatal error would be confusing. For example, when loading a driver, a graceful exit via exception with helpful error message is preferable to the fatal error:
<?php if (!class_exists("\PEAR2\PackageName\Driver\$class", true)) { throw new \PEAR2\PackageName\Exception('Unknown driver ' . $class . ', be sure the driver exists and is loaded prior to use'); } ?>
Requirement
This rule is optional and is a suggested coding practice
Directory structure
Follows the directory structure in the PEAR2 Subversion repository:
PEAR2/Package_Name/ src/ <-- all role="php" data/ <-- all role="data" tests/ <-- all role="tests" doc/ <-- all role="doc" www/ <-- all role="www" examples/ <-- role="doc" example files (php executable files that exemplify package usage)
Note that all package.xml files must specify a baseinstalldir of “/” for the src/ directory:
<contents> <dir name="/"> <dir name="src" baseinstalldir="/"> ... </contents>
Requirement
Exceptions may be made to this rule with approval from the PEAR Group
Class-to-file convention
All public classes must be in their own file with underscores (_) or namespace separators (\) replaced by directory separator, so that PEAR2_PackageName_Base class or PEAR2\PackageName\Base class is always located in PEAR2/PackageName/Base.php (this is required to make autoload work).
Requirement
Exceptions may be made to this rule only with explicit approval from the PEAR Group via a public vote.
Class Naming convention
The naming of individual classes follows these rules:
- Start with capital letter, e.g. class Foo {}
- CamelCase for multi-worded class names, e.g. class FooBarBaz {}
- Abbreviations only start with capital letter, e.g. class MrClean {}
- Acronyms should be fully capitalized, e.g. class PEARTree {}
- syntax/scope hints in the class name must be suffixed rather than prefixed, e.g. abstract class FooAbstract {}
- the suffix should be a full legible word, not a cryptic letter/abbreviation (e.g. FooAbst, FooA)
- a suffix for an Abstract or Interface class name is *required*
The only exception to the Interface suffix requirement is the base package exception, which must be named simply “Exception”.
Examples
Here are some real-life examples gleaned from PEAR1 packages:
- “class Text_Diff” in /Text/Diff.php becomes “class Diff”
- “class DB_DataObject” in /DB_DataObject/DataObject.php becomes “class DataObject”
- “class Auth_PrefManager” in /Auth_PrefManager/PrefManager.php becomes “class PrefManager”
- “class Services_Amazon_SQS” in Services_Amazon_SQS/Services/Amazon/SQS.php becomes “class SQS”
- “abstract class PHP_CodeSniffer_Standards_AbstractPatternSniff” in PHP_CodeSniffer/CodeSniffer/Standards/AbstractPatternSniff.php becomes “abstract class PatternSniffAbstract”
- “interface Testing_DocTest_RunnerInterface” in Testing/DocTest/RunnerInterface.php becomes “interface RunnerInterface”, or perhaps “interface RunnableInterface”
Requirement
The only exception to this rule is the Base Package Exception Interface, which is specifically required to be named “vendor\Package\Exception” rather than “vendor\Package\ExceptionInterface”.
Base Exception interface
Each package must define a base Exception interface which is implemented by any exception thrown within the package.
PEAR2/PackageName/Exception.php
<?php namespace PEAR2\PackageName; interface Exception {}
SPL exceptions are encouraged, and should be used when possible.
Extending SPL Example: PEAR2/PackageName/UnexpectedValueException.php
<?php namespace PEAR2\PackageName; class UnexpectedValueException extends \UnexpectedValueException implements Exception {}
Exception throwing example:
<?php namespace PEAR2\PackageName; class Foo { function run($method) { switch($method) { case 'add': case 'del': case 'up': $this->$method(); break; default: throw new UnexpectedValueException($method . ' is not a valid method.'); } } }
User exception catching example:
<?php require_once 'PEAR2/Autoload.php'; try { $p = new \PEAR2\PackageName\Foo(); $p->run('bar'); } catch (\PEAR2\PackageName\Exception $e) { echo "Caught exception from PEAR2\\PackageName"; }
Requirement
No Exceptions to this rule
Data files
package.xml replacement tasks should not be used to retrieve path locations for php, data, or www files. Replacements are still allowed in doc and test files.
The installation structure of a package has implicit php_dir/src installation location, and data files are always located in php_dir/data/channel/PackageName/. To retrieve a data file within PEAR2/PackageName/Subfile.php, you would use this code:
<?php ... // retrieve data from info.txt $info = file_get_contents(dirname(__FILE__) . '/../../../data/pear2.php.net/PEAR2_PackageName/info.txt'); ?>
Requirement
No Exceptions to this rule
Security Considerations
None