====== 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 [[http://creativecommons.org/licenses/by-sa/3.0/|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 [[pear:rfc:pear2_standards: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: Classes may use longer namespaces, for instance an HTTP_Request class may instead choose to use this declarative syntax: 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: this class should simply be used: 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 [[http://svn.php.net/viewvc/pear2/Autoload/trunk/src/Autoload.php?view=markup|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). 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: === 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: ... === 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** SPL exceptions are encouraged, and should be used when possible. Extending SPL Example: **PEAR2/PackageName/UnexpectedValueException.php** Exception throwing example: $method(); break; default: throw new UnexpectedValueException($method . ' is not a valid method.'); } } } User exception catching example: 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: === Requirement === No Exceptions to this rule ===== Security Considerations ===== None