Table of Contents

Summary

Information

Document Information

Author(s) Information

Past Author(s) Information

Discussion List

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:

Some of the benefits of require_once:

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:

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:

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:

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