<?php
error_reporting(E_ALL | E_STRICT);
/*
Manual optional type checking for PHP functions
Basic example:
function log_error($line_number, $filename, $desc)
{
CheckFunctionArgs('integer', 'string', 'string');
[snip]
}
Object example:
class LogObject {}
function register_object($obj)
{
// Check for an object
CheckFunctionArgs('object');
// When an object is passed, you can optionally check for the class name
CheckFunctionArgs('LogObject');
[snip]
}
Wildcard example:
function log_anything($line, $thing)
{
// '*' really means anything, included true/false/null or an empty string
// however it doesn't mean the argument in optional
CheckFunctionArgs('integer', '*');
}
Notes:
Throws an exception on error
No support for functions that take a variable number of arguments
Must define the types of all arguments
The type '*' acts as a wild card, matching anything (including null)
Works with public and private methods in classes
*/
function CheckFunctionArgs()
{
$types = func_get_args();
$stack = debug_backtrace();
// Make sure there is some stack information
// Stack[1] contains the details about the function that called this function
if(!isset($stack[1]))
{
throw new Exception("No function stack present. Make sure CheckFunctionArgs() isn't called from the global scope");
}
// The arguments that were passed to the function we are checking
$arguments = $stack[1]['args'];
// Get the name of the class/function/file
$functionClass = (isset($stack[1]['class'])) ? $stack[1]['class'] . "::" : '';
$functionFile = (isset($stack[1]['file'])) ? basename($stack[1]['file']) . ':' . $stack[1]['line'] : '[No file information]';
$functionName = "{$functionClass}{$stack[1]['function']}()";
// Basic check, make sure the correct numbers of arguments were passed
if(count($arguments) != count($types))
{
$passed = count($arguments);
$expected = count($types);
throw new Exception("Incorrect number of argumemts passed to {$functionName} in {$functionFile}. Expected {$expected} got {$passed}");
}
// Now try and check each argument
for($i = 0; $i < count($arguments); $i++)
{
// Allow a check to be skiped, if the type equals '*'
if($types[$i] == '*')
{
continue;
}
$argumentType = gettype($arguments[$i]);
// Check basic types like integer/object ect
if($argumentType == $types[$i])
{
continue;
}
// Check to see if the type matches the classname of an object
if(($argumentType == 'object') && (get_class($arguments[$i]) == $types[$i]))
{
continue;
}
throw new Exception("Incorrect argument passed to {$functionName} in {$functionFile}. Argument {$i} was type {$argumentType} expected {$types[$i]}");
}
return true;
}
// Some really basic tests
function assertException($fun, $args)
{
try
{
call_user_func_array($fun, $args);
throw new Exception(sprintf("Error: No exception thrown in function %s\n", $fun));
}
catch(Exception $e)
{
}
}
function assertNoException($fun, $args)
{
try
{
call_user_func_array($fun, $args);
}
catch(Exception $e)
{
throw new Exception(sprintf("Error: Exception thrown in function %s\n", $fun));
}
}
function test_string($a)
{
CheckFunctionArgs('string');
}
assertNoException('test_string', array('abc'));
assertNoException('test_string', array(''));
assertNoException('test_string', array('123'));
@assertException('test_string', array());
assertException('test_string', array(123));
assertException('test_string', array(null));
function test_integer($a)
{
CheckFunctionArgs('integer');
}
assertNoException('test_integer', array(0));
assertNoException('test_integer', array(123));
@assertException('test_integer', array());
assertException('test_integer', array(null));
assertException('test_integer', array(''));
assertException('test_integer', array('a'));
assertException('test_integer', array(1.0));
assertException('test_integer', array(1.2));
function test_wildcard($a)
{
CheckFunctionArgs('*');
}
assertNoException('test_wildcard', array('a'));
assertNoException('test_wildcard', array(1233));
assertNoException('test_wildcard', array(null));
@assertException('test_wildcard', array());
class Foobar {}
$foobar = new Foobar();
function test_classname($a)
{
CheckFunctionArgs('Foobar');
CheckFunctionArgs('object');
}
assertNoException('test_classname', array($foobar));
assertException('test_classname', array('Foobar'));
?>