Contents of this chapter
PHPLint's Standard Library
Handling errors -- errors.h
Autoloading classes -- autoload.php
The typecast magic function -- cast.php
Include all -- all.php
PHPLint's Standard Library
PHPLint comes with its own library of real PHP code named PHPLint
Standard Library. These libraries provide features intended to be
strictly tight to the PHPLint validator. These libraries are available
under the stdlib/ directory of the PHPLint package.
Handling errors -- errors.h
The stdlib/errors.php sets the error log level to the maximum
value and maps errors, warnings and notices into exceptions, to be
more precise into ErrorException. This provides two benefits:
-
Errors can be now managed with only one tool, that is the try/catch statements.
Note that ErrorException is checked, so you must choose to either
handle these errors, or declare them as thrown by the function or method.
This improves the safety of the program because it is garanteed the
error is handled somewhere.
-
Protects the program against the access to missing entries in arrays. In fact,
by default under PHP accessing an unexisting array element produces a simple
E_NOTICE, altought this error might have severe conseguences on the safety of
the program. Moreover, selecting an invalid entry generates NULL as value,
which might bring to unexpected errors several statement later, making
debugging more difficult. So detecting these events may be really important for
the safety of the whole program.
The following example illustrates how simple can become error handling
of I/O functions using exceptions:
require_once "stdlib/errors.php";
$fn = "doesNotExist.txt";
try {
$f = fopen($fn, "r");
$content = fgets($f, filesize($fn));
fclose($f);
}
catch( ErrorException $e ){
echo $e->getMessage(), ", using default empty content.\n";
$content = "";
}
echo $content;
=> WARNING [2] fopen(doesNotExist.txt): failed to open stream:
No such file or directory in /home/salsi/test.php:18, using
default empty content.
The following example illustrates what happen accessing an invalid index
of an array:
$a = array("zero", "one", "three");
echo $a[0];
echo $a[1];
echo $a[2];
echo $a[3];
=> Uncaught exception 'ErrorException' with message
'WARNING: Undefined offset: 3 in /home/salsi/test.php:13'
If you are unsure if a certain entry of an array does exist of not, you must
then use the function array_key_exists($key, $array).
Autoloading classes -- autoload.php
This package contains the magic __autoload($class) which is invoked
automatically by PHP (and then by PHPLint too) in order to resolve unknown
classes. Using this package you don't need to require_once every class
your source require, as this is performed automatically by PHP at runtime.
PHPLint applies this algorithms also to classes that appear in the meta-code,
in DocBlocks and in the cast() function.
This package should be located in the root directory of all your PHP sources
as the path of the path of the class it contruct is made relative to the
directory where the package autoload.php resides:
const SRC_BASE_DIR = __DIR__;
/*. unchecked .*/ class AutoloadException extends Exception {}
/*. void .*/ function __autoload(/*. string .*/ $name)
{
/*. pragma 'autoload' './' '/' '.php'; .*/
$fn = (string) str_replace("\\", "/", $name) . ".php";
if( ! is_readable($fn) )
throw new AutoloadException("__autoload($name): file $fn does not exist or not readable");
require_once $fn;
}
The SRC_BASE_DIR constant can also be used to load packages that
are not bare classes:
require_once SRC_BASE_DIR . "/mylib/mypkg.php";
To improve the safety of yours programs, this package also checks that the
proper php.ini file had been loaded, that is the php.ini file that resides
in the root directory.
The final structure of the PHP's source directory might then look similar
to this one:
(PHP's sources root)
|-- all.php
|-- autoload.php
|-- cast.php
|-- errors.php
|-- php.ini
|-- com
| `-- acme
| |-- framework
| | `-- ...
| |-- utils
| | `-- ...
| `-- website
| `-- ...
`-- it
`-- icosaedro
|-- bignumbers
| |-- BigFloat.php
| `-- BigInt.php
|-- examples
| |-- FunnyMessages.php (class it\icosaedro\examples\FunnyMessages)
| |-- User.php (class it\icosaedro\examples\User)
| |-- cast-test.php
| |-- err-test.php
| `-- serialize-test.php
`-- utils
|-- Floats.php (class it\icosaedro\examples\Floats)
|-- Floats-test.php
|-- Integers.php (class it\icosaedro\examples\Integers)
|-- Integers-test.php
|-- Strings.php (class it\icosaedro\examples\Strings)
`-- Strings-test.php
The typecast magic function -- cast.php
This package provides the cast() magic function that performs a
runtime check over the type of an expression, and then return its value only if
it matches the expected type and throws an exception if don't match.
In other words, a statement of this form
$x = cast(T, EXPR);
tells to PHPLint that the final result of the expression must be of type T;
moreover, at runtime the cast() function will check that the type of the
expression V be of the expected type T, otherwise a CastException is thrown.
Note that this function does not performs conversions of values, but merely
checks the type of V agains the type T.
To be more precise the first argument of the cast(T,V) function must
be a string (or any static expression giving a string as a result) that
describes the expected type, then including boolean, int, float, string,
resource, user defined classes and arrays. It is important to note that classes
defined inside a namespace must be indicated with their absolute or fully
qualified name:
require_once "cast.php";
use it\icosaedro\utils\BigInt;
use it\icosaedro\utils\BigFloat;
...
if( $n instanceof BigInt )
$bi = cast("it\\icosaedro\\utils\\BigInt", $n);
else if( $n instanceof BigFloat )
$bf = cast("it\\icosaedro\\utils\\BigFloat", $n);
As first concrete example, we see how this function can be used to
validate user's submitted parameters:
$name = cast("string", $_GET["name"]);
As we already know, $_GET["name"] can be a string, an array or even it
can do not exist at all. If the error handler we see above is loaded, an
exception will be thrown if this element of the $_GET[] array does not exist.
Moreover, an exception will be raised by cast() if it exists but it is
not a string. The final result is that the variable $name will get the
expected type of value, and an error (which is fatal in this simple example) is
detected and signaled otherwise. In the same manner we can acquire entire arrays:
if( isset($_GET["selected_boxes"]) )
$selected_boxes = cast("array[int]string", $_GET["selected_boxes"]);
else
$selected_boxes = /*.(array[int]string).*/ array();
It is very important to note that the formal typecast /*.(array[int]string).*/ is allowed by PHPLint only if applied to the empty array() or to
the NULL constant, and it is not allowed in any other case.
The cast() becomes really useful dealing with some complex libraries
that return generic containers of object, so there is the problem of how to
extract these objects while checking also their type, and making happy
PHPLint too. Here is an example with the DOM library:
/*. void .*/ function process_children(/*. DOMNode .*/ $root)
{
$children = $root->childNodes;
foreach($children as $elem){
$node = cast("DOMNode", $elem);
switch($node->nodeType){
case XML_ELEMENT_NODE:
$node_element = cast("DOMElement", $elem);
process_children($node_element, $level+1);
break;
...
}
}
$dom = new DOMDocument();
$dom->load("dom-test.xml");
$root = $dom->documentElement;
process_children($root);
As a general rule, cast("CLASS", expr) returns a value of type
CLASS only if expr is NULL or it is an instance of CLASS, otherwise it throws
CastException. Note that if the value is NULL, NULL is returned
by the function, so if you are expecting values that may be NULL you must
check this value separately either with if( $value === NULL )
or with if( is_null($value) ).
The syntax of the type accepted by cast() can be described
in EBNF form as follows:
type = "boolean" | "int" | "float" | "string" | "resource"
| "object" | CLASS_NAME | array_type;
array_type = "array" [ index_type { index_type } type ];
index_type = "[]" | "[int]" | "[string]";
Note that there is not a "mixed" type, as this type does not really exists in
PHP (there is no a is_mixed() function) but it is purely fictional in PHPLint.
Here object matches any instance of a class, while CLASS_NAME
is the name of any existing class. Note too that the abbreviation bool
and the alternate names integer real double are not allowed to make
faster the tests the cast() must perform at runtime.
| cast(T,V) |
Type of the expression V |
| boolean |
int |
float |
string |
array |
resource |
CLASS |
mixed |
| T |
"boolean" |
OK(b) |
NO |
NO |
NO |
NO |
NO |
NO |
OK(a) |
| "int" |
NO |
OK(b) |
NO(e) |
NO(d) |
NO |
NO |
NO |
OK(a) |
| "float" |
NO |
NO(f) |
OK(b) |
NO(d) |
NO |
NO |
NO |
OK(a) |
| "string" |
NO(g) |
NO(h) |
NO(h) |
OK(b) |
NO |
NO |
NO(i) |
OK(a) |
| "array..." |
NO |
NO |
NO |
NO |
OK(j) |
NO |
NO |
OK(j) |
| "resource" |
NO |
NO |
NO |
NO |
NO |
OK(b) |
NO |
OK(a) |
| "CLASS" |
NO |
NO |
NO |
NO |
NO |
NO |
OK(c) |
OK(a) |
Legenda:
a) code("T",mixed) performs runtime check on the actual
type of the mixed expression, and performs unboxing of simple types boolean,
int and float.
b) cast("T",T) is unuseful has PHPLint already has detected
the type of the expression.
c) In cast("CLASS", $obj) $obj must be NULL or an instance of the
class CLASS.
d) To convert a string representing a number into number use the PHP's typecast
operators (int) and (float).
e) To convert float into int you may use the PHP's typecast operator
(int).
f) To convert int into float you may use the PHP's typecast operator
(float).
g) To convert boolean into string you may use this espression:
((V?) "TRUE" : "FALSE").
h) To convert int or float into string you may use the PHP's typecast operator
(int) and (float). The function
sprintf() provides a finer control on the result.
i) Objects that implements the __toString() method can be converted
into string either using the PHP's (string) typecast operator or
calling directly that method. That method is invoked automatically for objects
that appear in string concatenation and inside the echo statement.
j) As a general rule, cast("array[I]E",V) matches only V
of type mixed that at runtime is NULL or an array of the specified exact type,
or a generic array of unspecified index type and elements that at runtime
has the exact specified type.
The discussion below details how this match is performed.
Arrays have a semanthic much more articulated. First of all there is the type of
the index. The empty index [] meas that the intended array can have
indexes that can be both integer numbers and strings, while [int]
matches only arrays whose indexes are all integer numbers, and [string]
matches only arrays whose indexes are strings.
If a type for the elements of the array is specified, then
all the elements of the array must match that type.
NULL values match any string, resource, array and object.
In the following examples, the result of the cast() magic function
is always exactly a value of the type specified, although in some cases this value
at runtime might be also NULL according to the type model of PHPLint for types
that are "dynamically" allocated; for simple types (boolean, int and float)
NULL is never returned:
cast("boolean", $expr)
$expr must be either boolean or mixed (that is, a boxed boolean).
At runtime, if $expr is NULL or does not contain a boxed boolean, a
CastException is thrown.
cast("int", $expr)
$expr must be either int or mixed (that is, a boxed int).
At runtime, if $expr is NULL or does not contain a boxed int, a
CastException is thrown.
cast("float", $expr)
$expr must be either float or mixed (that is, a boxed float).
At runtime, if $expr is NULL or does not contain a boxed float, a
CastException is thrown.
cast("string", $expr)
$expr must be either string or mixed (that holds a string).
At runtime, $expr must be either NULL or string, otherwise a
CastException is thrown.
cast("resource", $expr)
$expr must be either resource or mixed (that holds a resource).
At runtime, $expr must be either NULL or resource, otherwise a
CastException is thrown.
cast("object", $expr)
$expr must be either an object of any class, or mixed (that holds
an object).
At runtime, $expr must be either NULL or object, otherwise a
CastException is thrown.
cast("CLASS_NAME", $expr)
$expr must be either an instance of the CLASS_NAME, or any of it
derived classes, or mixed (that holds an object).
At runtime, $expr must be either NULL or an object derived from
CLASS_NAME, otherwise a CastException is thrown.
cast("array", $expr)
$expr must be either NULL, an empty array, an array of any type, or
mixed (that holds an array).
At runtime, $expr must be either NULL or an array of any structure,
otherwise a CastException is thrown.
cast("array[]", $expr)
The same as array.
cast("array[int]", $expr) $expr must be either
NULL, an empty array, an array with integer indexes and elements of any
type, or mixed (that holds an array[int]).
At runtime, $expr must be either NULL, an empty array, or an array with
integer indexes, otherwise a CastException is thrown.
cast("array[]int", $expr) $expr must be either
NULL, an empty array, an array with any type of indexes and elements of
type integer, or mixed (that holds an array[]int).
At runtime, $expr must be either NULL, an empty array, or an array with
any indexes but whose elements are all int, otherwise a CastException
is thrown.
cast("array[int]string", $expr) $expr must be
either NULL, an empty array, an array with integer indexes and string
elements, or a mixed (that holds array[int]string). an array). In this
case the matching rules applies recursively, so any single element of
the array matches just what string matches, that is either
NULL or string. NULL entries should be checked with
array_key_exists($key, $array) for existance.
At runtime, $expr must be either NULL, an empty array, or an array with
int indexes and whose elements are string (or NULL), otherwise a
CastException is thrown.
cast("array[int][int]CLASS_NAME", $expr) $expr
must be either NULL, an empty array, a matrix of CLASS_NAME, or a
mixed (that holds array[int][int]CLASS_NAME). Here too, any single
element of the array can be NULL, then some of the entries given by the
first index may be NULL just like as some entries of the second index
may be NULL.
At runtime, $expr must be either NULL, an empty array, or an array with
int indexes and whose elements are etc. etc., otherwise a
CastException is thrown.
Include all -- all.php
Finally, the all.php package simply loads all the packages above,
so providing full support for class autoloading, safe error handling and
safe typecasting in your programs.
|