PHPLint Tutorial

Last updated: 2015-05-20

This (not so) brief tutorial explains how to make your programs PHPLint-compliant. I'm firmly convinced that a good PHP source must be so simple to read that also a dumb program can understand it. The vice-versa also holds: once a program passed the validation of PHPLint (the dumb program in question) your source is good, and ready to work nicely. I hope these notes will help you grab the "philosophy" behind PHPLint and its motivations.

It might be useful to test the examples presented here using the on-line version of PHPLint.

Index

General structure of the program
The Type will be with you, always
Type model of PHPLint in detail
Classes
Bad code

General structure of the program

Declare the required extension modules. PHP has several extensions modules, that may or may not be available in your installation of the PHP interpreter. You MUST specify ALL the extensions actually used by your program using the special meta-code statement require_module as in this example:

<?php
/*.
    require_module 'standard';
    require_module 'pcre';
    require_module 'mysql';
.*/
?>

The standard module is required by most applications, since it exports commonly used functions like strlen() and constants like PHP_OS. Note the special comment /*. .*/ that marks a block of PHPLint meta-code; there is exactly a period after the first asterisk, and another period before the second asterisk. Such meta-code is ignored by the PHP interpreter, since it appears inside a comment, but it is of fundamental importance for PHPLint.

PHPLint will complain with an error if it encounters an item (constant, variable, function or class) that cannot be found inside the specified modules. PHPLint will also complain if a module is required, but actually not used. Moreover, the required modules can also be inherited from packages imported via require_once. To sum-up, PHPLint will report into the documentation it generates which modules and packages your package actually needs. The list of modules and packages required is really useful when the package has to be deployed on the target system, typically a WEB server.

Use require_once to include other packages. The alternatives require, include and include_once are not reliable ways to include code. include is commonly used to include stubs of HTML code, as often is required to build the header and the footer of a WEB page, for example, but PHPLint does not parse recursively these files. The path of the required package must be absolute: this is the only safe way to ensure the referred file will be found either by PHPLint and by PHP, independently from any other external parameter or configuration. The magic constant __DIR__ (PHP5 only) comes into help here, because allows to write a path relative to the current source file:

require_once __DIR__ . "/../../AnotherPackage.php";

You may also use constants, provided that the resulting expression be statically evaluable:

define("LIBS", __DIR__ . "/../../mylibs/");
define("PKGS", __DIR__ . "/packages/");
require_once LIBS . "Class1.php";
require_once LIBS . "Class2.php";
require_once LIBS . "Class3.php";
require_once PKGS . "Package1.php";

You may also collect these constants and the most commonly required packages in some base package that will be included in any other script.


Always import the modules and the packages your source depends on.

In this way at runtime every package takes care to load any lower-level package he need, and PHPLint can parse every package separately. Modules can be imported with the /*. require_module 'MODULE'; .*/ pseudo-code statement, while packages can be imported with the usual PHP statement require_once ...;. An useful alternative is the class autoloading mechanism described in the reference manual; class autoloading saves you from the need to list all the required packages, and is more efficient at runtime because classes are loaded only when required. Unfortunately, autoloading works only for classes, not for functions and the other items.


PHPLint is case-sensitive. PHPLint promotes a clean programming style where everything must be written exactly as defined, including:

PHPLint complains with an "error" if any of such things is written down arbitrarily mixing uppercase and lowercase letters that do not match the definition.


GoodBAD - mixing case
use it\icosaedro\bignumbers\BigFloat;

if ( ! isset($_POST['price']) )
    die("missing 'price' field");
$s = trim( (string) $_POST['price'] );
if ( ! BigFloat::isValid($s) )
    die("invalid syntax in price: $s");
$price = new BigFloat($s);
Use it\icosAedro\BIGnumbers\bigfloat;

If ( ! IsSet($_POST['price']) )
    Die("missing 'price' field");
$s = Trim( (String) $_POST['price'] );
If ( ! bigFloat::isvalid($s) )
    DIE("invalid syntax in price: $s");
$price = new BIGfloat($s);

Declarations first. Before the code be actually executed, the PHP interpreter scans all the code looking for functions, classes and methods, so that they can appear in any order. (This is not the case for constants and variables, that must be defined and assigned in the order also in PHP).

By the contrary, PHPLint is a single-pass parser, so it needs to read the definitions before the usage. PHPLint raises an error if a function or a class gets used before being defined. Take care to sort your program in a bottom-up order: low-level functions, classes and methods first, high-level ones next.


Typical order of the declarations
<?php

# This package namespace:
namespace com\mycompany\dbtools;

# Required PHP extensions:
/*. require_module 'standard'
    require_module 'session';
    require_module 'mysqli'; .*/

# Required packages:
require_once __DIR_ . "/autoload.php";
require_once __DIR_ . "/Common.php";

# "Use" declarations:
use com\mycompany\dbtools\DbAbstractionLayer;
use com\mycompany\dbtools\InputValidator;
use it\icosaedro\bignumbers\BigFloat;

# Constants:
const QUERY_MAX_EXECUTION_TIME_MS = 2000;

# Functions and classes:
...

# Main body of the program:
...

The reference manual describes a feature of PHPLint meta-code named "prototypes declarations" or even "forward declarations", see chapter Recursive declarations. Prototypes can be also used to relax the strict bottom-up order of the declarations. The use of this solution should be avoided, and prototypes should be restricted to those cases in which the intrinsic recursive nature of the declarations requires them (example: function A calls B, and function B calls A).

The Type will be with you, always

In PHP a variable can hold any type of value. So, for example, you can assign to a variable $foo a number, then later you can assign to the same variable a string or also any other type of data. A given function may return several types of values depending on the arguments that are passed. Programs that use this "polymorphic" behavior are difficult to read and difficult to understand, and often they require the interpreter to do some expensive conversion at run-time with performance penalties.

In PHPLint things goes in a completely different way. Each variable, each function argument, and each value returned by a function must have a well defined type. PHPLint does not require the type of every thing be explicitly defined, since in most of the cases this type can be guessed from the code itself. For example, assigning 123 to a variable clearly makes that variable of the type int, and returning a string from a function clearly makes this function of the type string. Nevertheless, there are cases where a type must be explicitly indicated, as we will see in the examples below.

Always initialize all the variables. Some PHP programmers rely on the fact that an un-initialized variable behave like the zero number or the empty string, depending on the context where this variable appears. That's not a good programming style. Every variable must have a value (and then, a type) before being used.

The type of a variable cannot change. If a variable was determined to belong to some type, it must be used according to its type. You cannot assign a string to a variable already known to be of type int:

$i = 123;
$i = "hello";  # <== ERROR

The superglobal arrays $_GET $_POST and $_REQUEST have structure mixed[string] that is arrays of arbitrary values with keys of type string. In PHPLint every array has a type for the key and a type for its elements. The index can be int or string, while the elements must be all of the same type. The super-global arrays $_GET & Co. all have an index of the type string, while their elements are mixed. Typically these elements are strings, but in some cases they can be also arrays of strings, or array of arrays of strings, and so on. Your program must ensure the values gathered from these array be of the expected type. To ensure that, a value type-cast is needed at run-time:

# Example: acquiring the login mask:
$name = (string) $_POST["name"];
$pass = (string) $_POST["pass"];


# Example: acquiring optional URL parameter:
if( isset($_GET["chapter"]) )
    $chapter = (int) $_GET["chapter"];
else
    $chapter = 0; # intro

By the way, these value type-cast applied to a mixed value make PHPLint aware of the actual type expected by the program and make the program safer because data coming from the remote client are arbitrary values. Now, $name and $pass are strings, while $chapter is an integer number, and PHPLint ensures they will be used in the program according to their types.

Declare the type of each function argument. PHPLint implements a strong-typed language where every expression must have a well defined type. If the type of an argument cannot be determined, an error is displayed and its type is left unknown. The type of an argument can be declared using a DocBlock or the specific PHPLint meta-code. For example, a function like this one

<?php

function get_param($name, $max_len=20)
{
    if( ! isset( $_REQUEST[$name] ) )
        return NULL;
    $s = $_REQUEST[$name];
    if( strlen($s) > $max_len )
        $s = substr($s, $max_len);
    return $s;
}

?>
can be converted to the PHPLint-compliant version that follows using a DocBlock:

PHPLint also parses the special comments known as DocBlock. DocBlocks are an effective way to document the signature of a function and can be used in place of the PHPLint meta-code:

<?php

/*. require_module 'standard'; .*/

/**
 *  Return a parameter from the HTTP request.
 *
 *  @param   string  $name     Name of the parameter.
 *  @param   int     $max_len  Trunk the parameter to this length (bytes).
 *  @return  string            The parameter, or NULL if missing.
 */
function get_param($name, $max_len=20)
{
    ...
}

?>

Note that a DocBlock is nothing else than a comment for PHP, but it is read very carefully by PHPLint. A DocBlock is any multi-line comment that starts with an asterisk, so anything that starts with exactly these characters /** is a DocBlock. The following lines in the DocBlock may start with an asterisk, but this is not required and this leading asterisk is ignored by PHPLint.

The reference manual explains in details what can be entered in a DocBlock. Here we say only that, as a general rule, the content of the DocBlock is parsed as HTML text (leading asterisk apart), then the usual rules to escape the special characters < > & applies here too, and these characters must be written as &lt; &gt; &amp; respectively. In the particular case in which you had to write exactly */ inside a DocBlock, you may write &#42;/ instead, being 42 the ASCII decimal code for the asterisk.

The first line of a DocBlock that starts with @xxxx (optional leading asterisk apart) begins the line-tags section of the DocBlock, where the things that really interest to PHPLint are defined. In our case, the line-tag @param TYPE $V declares the type of a parameter; the following textual description does not care really to PHPLint, but it may really help programmers to understand what the function requires and what it does. This description continues up to the next line-tag or to the end of the DocBlock. These descriptive texts are collected by PHPLint and can be reported in the generated documentation.

Finally, the @return TYPE line-tag declares the type of values this function may return, in this case a string. Here too, a description can be entered and will be reported in the documentation.

As said, PHPLint also understands another kind of annotations that is shorter and that may be preferable in some cases, for example for little private functions that do not really need to be documented in detail. These special annotations are name "PHPLint meta-code", and are again multi-line comments /*. .*/ surrounding the PHPLint meta-code. We have already seen these special comment at the beginning of this document, used for the require_module meta-code statement:

<?php

/*. require_module 'standard'; .*/

/*. string .*/ function get_param(/*. string .*/ $name, $max_len=20)
{
    if( ! isset( $_REQUEST[$name] ) )
        return NULL;
    $s = (string) $_REQUEST[$name];
    if( strlen($s) > $max_len )
        $s = substr($s, $max_len);
    return $s;
}

?>

Note that this time the argument $max_len does not require a type, since its initial value already provides to PHPLint the correct answer: int. Although in DocBlocks usually all the parameters are listed in order to describe their meaning in detail, from the point of view of PHPLint the parameters that have a default value can be omitted because the type is exactly the type of the default value assigned. We will see how this rule has two exceptions: when the default type is NULL or it is the empty array array().

NULL should always have a formal type-cast. The null type is for internal use only of PHPLint. It has only one value: NULL. The same value can be used by variables of type string, array, resource and object. PHPLint needs a way to understand to which of these types the NULL constant actually belongs. In the example above PHPLint guesses that, since the returned value must be a string, the NULL value that appears inside the return statement must be string. As a general rule, you should not rely on these guesses, and you should provide an explicit formal type-cast:

return /*. (string) .*/ NULL;

Note that, apart the /*. .*/ symbols, this formal type-cast is similar to a PHP value type-cast, where the type name is enclosed between round parenthesis.

Use void for functions that do not return a value. PHPLint always tries to guess the returned type from the return EXPR; statement: the type resulting from the evaluation of the EXPR is the type of the function. Functions containing only return; are void. As a general rule, it is better to always declare explicitly the returned type, since this make the interface to the function more readable to the programmer.

Use /*. args .*/ for functions accepting a variable number of arguments. Examples:

function f(/*. args .*/){}
function g($a, $b /*. , args .*/){}

The first function can be called with any number of arguments, while the latter requires at least two arguments. Note the presence of the comma inside meta-code of the second function.

Type model of PHPLint in detail

PHP already has "types", because at runtime every value and every variable belongs to a well specific type. To the proper PHP type, PHPLint adds the mixed type, which does not really exist in PHP but is used extensively in its official documentation to indicate "any type of value".


Types under PHPLint. Arrows indicate assignment compatibility.


The mixed type

Under PHPLint, mixed is a box inside which any type of data can be boxed. Variables and parameters of this type can carry any type of data. mixed values can be assigned to other mixed variables, or can be compared with the strict comparison operators === and !== but nothing really useful can be made until the value they contain is extracted to some specific type, that is, is unboxed.

Unboxing, under PHPLint, requires to apply either one of the PHP value-conversion operators, for example

$name = (int) $_GET["name"];

or apply the magic PHPLint type-cast function cast(T,V):

$name = cast("string", $_GET["name"]);

Thre are at least two important differences between PHP value-conversion operators and the PHPLint cast() magic function. One difference is that the PHP's value-conversion operators (TYPE) actually convert mixed to a value of the specified type, while the cast(T,V) type-cast magic function merely checks at runtime the actual type and throws ErrorException if it does not match those expected, then return the unboxed value, unmodified.

The other difference is that the cast(T,V) function can check any type of data PHPLint can understand, including arrays (with specified keys and elements) and objects. The cast() function is implemented in the cast.php package available under the stdlib directory.

The cast() magic function serves to two purposes, one at validation time, and one at runtime. Take for example this statement:

$names = cast("string[string]", $_GET);

It has two effects:

  1. At validation time, tells to PHPLint that $names is an array of strings with keys of type string. This resolves the problem from the point of view of the static validation.
  2. At runtime, the cast() function will check the value $_GET element by element and key by key, and will throw CastException if it does not match the expected type, so rejecting the invalid value. This resolves the safety and security problems that might arise if unexpected types of values enter the program.

The array type

As we already said, arrays under PHPLint may have a well defined type for the key and for the elements. To specify the structure of the array, PHPLint introduces the special notation E[K] where E is the type of the element and K is the type of the key (that is, int or string). So, for example, a list of names with integer index is represented by

string[int]

while an associative array name-to-object might be

Person[string]

The empty key is also allowed, which means unspecified key integer and/or string; if, moreover, the type of the elements if mixed, we end with the more generic type of array:

mixed[]

Several indeces can be specified. A rotational matrix:

float[int][int]

These types can be used in several contexts, lets see some examples. Explicitly declaring the type of a variable:

/*. resource[int] .*/ $files = NULL;
/*. string[int] .*/ $names = array();

Formal-typecast to specify the structure of an empty array or the NULL value, with the same effect we had above:

$files = /*. (resource[int]) .*/ NULL;
$names = /*. (string[int]) .*/ array();

Defining the signature of a function (here using a DocBlock):

/**
 * Returns the array of strings in alphabetical order.
 * @param string[int] $a Unordered array of strings.
 * @return string[int] Same strings, but in alphabetical order.
 */
function mySort($a){
	if( $a === NULL )
		return NULL;
	for($i = count($a)-1; $i >= 1; $i--){
		for($j = $i-1; $j >= 0; $j--){
			if( strcmp($a[$j], $a[$i]) > 0 ){
				$t = $a[$i];  $a[$i] = $a[$j];  $a[$j] = $t;
			}
		}
	}
	return $a;
}

From the examples above, emerges how some types allows NULL as a value, and nothing prevent, at runtime, to access an entry of the array that does not exists. That's why it is strongly recommended to always require the errors.php package available under the stdlib directory:

require_once __DIR__ . "/errors.php";

That package sets the maximum level of error reporting and maps any error, even the smallest E_NOTICE, into an ErrorException that terminates the program. This dramatically improves the safety and the security of a program how can be clearly seen from the following examples:

<?php
require_once __DIR__ . "/errors.php";
/*. string[int] .*/ $a = array("a");
echo $a[3];

Output:
Uncaught exception 'ErrorException' with message 'E_NOTICE: Undefined offset: 3 in C:\Users\Umberto\Desktop\Projects\phplint\stdlib\test.php:5' in C:\Users\Umberto\Desktop\Projects\phplint\stdlib\errors.php:125
Stack trace:
#0 C:\Users\Umberto\Desktop\Projects\phplint\stdlib\test.php(5): phplint_error_handler(8, 'Undefined offse...', 'C:\Users\Umbert...', 5, Array)
#1 {main}

FIXME: And what happen accessing a NULL array? See these PHP bugs: https://bugs.php.net/bug.php?id=65484, https://bugs.php.net/bug.php?id=62769.

Classes

All the properties of a class MUST be declared. Moreover, assign to them a type and/or an initial value. As you might guess at this point, providing an initial value lets PHPLint to determine its type. Example:

class Test {
    public $num = 123;
    public $arr = array(1, 2, 3);
    private /*. string[int] .*/ $arr2;

    function __construct($first = "")
    { $this->arr2 = array($first); }
}

Note that the array $arr2 lacks its initial value, so an explicit declaration of type is required. Remember that in this case the PHP interpreter assign NULL as initial value.

Properties cannot be added dynamically at run-time to an object. If you need to store a variable number of data inside an object, use a property of the type array.

Bad code

Constants should be... constant! PHPLint expects the expression giving the value of a constant be statically determinable. In any other case a variable is more appropriate. Moreover, some programmers take advantage from the fact that constants "lives" in the global namespace, so you can get their value simply writing their name:

# WRONG CODE:
define("MY_IMGS", $_SERVER['DOCUMENT_ROOT'] . "/imgs");
if ( PHP_OS == 'WINNT' )
    define("ROOT", "C:\\");
else
    define("ROOT", "/");

function f()
{
    echo "MY_IMGS=", MY_IMGS, " ROOT=", ROOT;
}

You should try to submit the code above to PHPLint: it will complain that the constant MY_IMGS cannot be statically evaluated, and ROOT is re-defined. Since these values are determined at run-time, you should use two variables instead:

# Right code:
$MY_IMGS = $_SERVER['DOCUMENT_ROOT'] . "/imgs";
if ( PHP_OS === 'WINNT' )
    $ROOT = "C:\\";
else
    $ROOT = "/";

function f()
{
    echo "my_imgs=", $GLOBALS['MY_IMGS'], " root=", $GLOBALS['ROOT'];
}

Write proper boolean expressions. As a general rule under PHPLint, the integer number 1 is not TRUE, and an empty array() or an empty string are not FALSE. If a boolean expression is expected, you must build a proper boolean expression. Statements like if(EXPR) while(EXPR) do{}while(EXPR) all require a proper boolean expression.

Some functions of the standard library, for example those that normally return a resource on success, may return FALSE to indicate an error: these special returned values must be checked with the === or the !== operators. For example, the fopen() function returns FALSE if the file cannot be opened for the required operation, so you must check for a possible error:


WRONG CODE Right code
The ! operator cannot be applied
to a value of the type resource:
if( ! $f = fopen("myFile.txt", "r") ){
    die("error opening the file");
}
if( ($f = fopen("myFile.txt", "r")) === FALSE ){
    die("error opening the file");
}
or even better:
$f = fopen("myFile.txt", "r");
if( $f === FALSE ){
    die("error opening the file");
}

Functions must always return only one type of value. Don't write functions that "return the result on success or FALSE on failure" because mixing types that are different prevent PHPLint from doing its job and make the code harder to read and to debug. Here there is a list of possible alternatives:

Do not mix elements of different types in arrays. For example, this table mixes strings, numbers and boolean values:

# WRONG:
$people = array(
#   Name    Age   Married
    "Gery",  34,   FALSE,
    "Sara",  23,   TRUE,
    "John",  56,   TRUE);

echo "Married persons younger than 30: ";
for($i = 0; $i < count($people); $i += 3)
    if( $people[$i+2] and $people[$i+1] < 30 )
        echo $people[$i], " ";

PHPLint cannot parse effectively such a code, and neither humans can understand it very well. The solution to the problem requires to introduce a class Person where all the data about a person are stored. The resulting code might look similar to this one, that can be validated by PHPLint:

# Right:
class Person {
    public /*. string .*/ $name;
    public $age = 0;
    public $married = FALSE;

    function __construct(/*. string .*/ $name, /*. int .*/ $age, /*. bool .*/ $married)
    {
        $this->name = $name;
        $this->age  = $age;
        $this->married = $married;
    }
}

$people = array(
    new Person("Gery",  34,   FALSE),
    new Person("Sara",  23,   TRUE),
    new Person("John",  56,   FALSE)
);

echo "Married persons younger than 30: ";
foreach($people as $p)
    if( $p->married and $p->age < 30 )
        echo $p->name, " ";

Ok, I agree: this second version of the same program is longer, but the first one remembers to me the old times of the BASIC when the arrays were the only data structure available. Moreover, trying the first example while writing this document, I made a mistake with the offset of the index and the program did not work properly; the second version, instead, worked perfectly just at the first run.

Proper use of ini_get(). Sometimes programs need to check at run-time their configuration file php.ini for some parameter. All the parameters declared here are available through the function ini_get($param) where $param is the name of the parameter. The value returned by this function is always a string or the NULL value. For those parameters that are simple flags, the value returned is the empty string "" or "0" for FALSE/No/Off, and "1" for TRUE/Yes/On. The other parameters return a string, although they can be actually numbers. The right way to handle this in PHPLint is shown in the following examples, that may be useful in actual applications:


if( ini_get("magic_quotes_gpc") === "1"
or  ini_get("magic_quotes_runtime") === "1")
    exit("ERROR: please disable magic quotes in php.ini");

if( ini_get("file_uploads") !== "1" )
    exit("ERROR: please enable file upload in php.ini");


/**
 *  Converts size in bytes, possibly with scale factor.
 *  Converts a numeric value from the php.ini, possibly
 *  containing some scale factors as K, M and G.
 *  Example taken from the PHP manual.
 *  @param string $s  Encode size in bytes, possibly with scale factor.
 *  @return int  Number of bytes.
 */
function return_bytes($s)
{
    $v = (int) $s;
    $last = strtolower($s[strlen($s)-1]);
    switch($last) {
        // The 'G' modifier is available since PHP 5.1.0
        case 'g': $v *= 1024;  /*. missing_break; .*/
        case 'm': $v *= 1024;  /*. missing_break; .*/
        case 'k': $v *= 1024;  /*. missing_break; .*/
        /*. missing_default: .*/
    }
    return $v;
}

$upload_max_filesize =
    return_bytes( trim( ini_get("upload_max_filesize" ) ) );
$post_max_size =
    return_bytes( trim( ini_get("post_max_size" ) ) );
$max_upload = min($upload_max_filesize, $post_max_size);
echo "Max uploadable file size is $max_upload bytes.";

Do not use each() and list() to assign a list of variables. PHP allows the special syntax list($x,$y)=EXPR; where EXPR is an expression generating an array, typically the value returned from a function or the special language construct each(). Never use these syntaxes, because PHPLint cannot determine the types of the values $x and $y. Rather, assign to an array, then use the resulting elements.


WRONG CODE Right code
$a = array(1, 2, 3);

reset($a);
while( list($k, $v) = each($a) ){
    echo $k, $v;
}
$a = array(1, 2, 3);

foreach( $a as $k => $v ){
    echo $k, $v;
}

For example, this function may be useful to measure with precision the time elapsed:

function elapsed($a)
{
    $b = microtime();
    list($b_dec, $b_sec) = explode(" ", $b);
    list($a_dec, $a_sec) = explode(" ", $a);
    return ((float)$b_sec - (float)$a_sec)
        + ((float)$b_dec - (float)$a_dec);
}

$start = (string) microtime();
/**** here do something time-consuming ****/
$e = elapsed($start);
if( $e > 1.0 )  echo "WARNING: elapsed time $e s";

Note the presence of two list() constructs. That code can be easily converted to the following PHPLint-compliant code, where the result of the explode() function is assigned to two arrays; some meta-code was also added:

/*.float.*/ function elapsed(/*.string.*/ $start)
{
    $a = explode(" ", $start);
    $b = explode(" ", (string) microtime());
    return ((float)$b[1] - (float)$a[1])
        + ((float)$b[0] - (float)$a[0]);
}

String comparisons should be made using strcmp(). Never use the weak comparison operators < <= == != >= > with strings, because they are unreliable. Apply this simple conversion rule:

$a OP $b     ==>    strcmp($a, $b) OP 0

where OP is the comparison operator. Use === and !== for strict equality/inequality.

die() is a statement, not a function! This syntax is invalid:

$f = fopen(...) or die(...);

because die() does not return a boolean value (actually, it does not return anything at all). Use the longer form we shown above. The same holds for exit(), actually a synonym of die().

Do not use "variable name" classes, for example

$obj = new $class();

because $class might be any string, without any relation with the known classes; this source is difficult to understand for the human reader of the source, and impossible to check at all for PHPLint. Consider to use an abstract class instead. PHP 5 also introduced the interfaces, intended just to address elegantly these problems. Adding these "glue-classes" makes the code more readable and PHPLint helps to keep the complexity under control.

Returning to the example above, if $obj has to be an instance of some class dynamically determined at run-time, certainly these classes are in some way related, i.e. them exhibit the same interface. This interface (i.e. a common set of constants, properties and methods) will be used in the following code. Two classes that share the same interface must have a common ancestor, that may be an abstract class or an interface. The example below illustrates this scheme:

    interface Ancestor {
        function doThis();
        function doThat();
    }

    class ConcreteClass1 implements Ancestor {
        public function doThis() { /* ...implementation... */ }
        public function doThat() { /* ...implementation... */ }
    }

    class ConcreteClass2 implements Ancestor {
        public function doThis() { /* ...implementation... */ }
        public function doThat() { /* ...implementation... */ }
    }

    # Declare the variable $obj to be a generic Ancestor.
    # This says to PHPLint that $obj is an object that
    # implements "Ancestor":
    $obj = /*. (Ancestor) .*/ NULL;

    if( we_need_ConcreteClass1 )
        $obj = new ConcreteClass1();
    else /* we need ConcreteClass2 instead */
        $obj = new ConcreteClass2();

    # Now we can use $obj according to the interface as specified
    # for Ancestor, whichever its actual implementation may be:
    $obj->doThis();
    $obj->doThat();

    # The same strategy can be used also inside the functions:
    function doThisAndThat(/*. Ancestor .*/ $obj)
    {
        $obj->doThis();
        $obj->doThat();
    }

    doThisAndThat($obj);

The advantage of using abstract classes and interfaces is that the PHP interpreter, the PHPLint validator and humans reading the source can understand the meaning of the source and detect possible violations of the "contract" rules in the extended and implemented classes.