Type Hinting

User Contributed Notes 22 notes

People often ask about scalar/basic typehints. Here is a drop in class that I use in my MVC framework that will enable typehints through the use of a custom error handler.

Note: You should include this code above all other code in your include headers and if you are the using set_error_handler() function you should be aware that this uses it as well. You may need to chain your set_error_handlers()

Why?1) Because people are sick of using the is_* functions to validate parameters.2) Reduction of redundant coding for defensive coders.3) Functions/Methods are self defining/documenting as to required input.

Also..Follow the discussion for typehints in PHP 6.0 on the PHP Internals boards.

<?php

define('TYPEHINT_PCRE' ,'/^Argument (\d)+ passed to (?:(\w+)::)?(\w+)\(\) must be an instance of (\w+), (\w+) given/');

The scalar type hinting solutions are all overthinking it. I provided the optimized regex version, as well as the fastest implementation I've come up with, which just uses strpos. Then I benchmark both against the TypeHint class.

<?phpfunction optimized_strpos($ErrLevel, $ErrMessage) { if ($ErrLevel == E_RECOVERABLE_ERROR// order this according to what your app uses mostreturn strpos($ErrMessage, 'must be an instance of string, string') || strpos($ErrMessage, 'must be an instance of integer, integer') || strpos($ErrMessage, 'must be an instance of float, double') || strpos($ErrMessage, 'must be an instance of boolean, boolean') || strpos($ErrMessage, 'must be an instance of resource, resource');}

Please note, that with PHP 7, type hinting is faster than normal implementation.This has to do with type hinting before PHP 7 allowing only objects and arrays and anything else (scalars) would throw an error.

Daniel's typehint implementation was just what I was looking for but performance in production wasn't going to cut it. Calling a backtrace every time hurts performance. For my implementation I didn't use it, after all, PHP tells us what the data type is in the error message, I don't feel I need to evaluate the argument where I am using typehinting. Here is the cut down version I use in my error handling class:

I've done some tests of the overhead that class Typehint gives us.At my PC it goes as follows:teststringNormal took: 0.041965961456299teststringOverhead took: 0.48374915122986It's like 10x longer time (not mention about memory usage), it's just because exception is thrown EVERY SINGLE TIME, along with expensive preg_match() and debug_backtrace() calls.I think that using class in bigger applications will increase overhead like 100% or more.<?php

The manual's sample code says:<?php//...// Fatal Error: Argument 1 must not be null$myclass->test(null);//...?>

And this is true, unless a default value of NULL is given; in fact, this is the only way to give a default value for object arguments (as a default value must be a constant expression):<?php$mine = new MyClass();$mine->test(NULL);class MyClass{ public function __construct(OtherClass $arg = NULL){ if(is_null($arg)){//Apply default value here.} } public function test(array $arr = NULL){print_r($arr); }}class OtherClass{

$lines=hhb_mustbe('array',file("foofile"));//either file succeeds, and returns an array, or file returns FALSE which is not an array, which throws an unexpectedValueException.

$socket=hhb_mustbe('Socket',socket_create(AF_INET,SOCK_STREAM,getprotobyname('tcp')));//either socket_create succeeds, and returns a Socket, or socket_create returns False, which is not a resource of type Socket, and you get an UnexpectedValueException

$size=hhb_mustbe('int',filesize(somefile));//either filesize() returns an integer, or returns FALSE wich is not an int, and you'll get UnexpectedValueException.

function hhb_mustbe(/*string*/$type,/*mixed*/$variable){ //should it be UnexpectedValueException or InvalidArgumentException? //going with UnexpectedValueException for now... $actual_type=gettype($variable); if($actual_type==='unknown type'){ //i dont know how this can happen, but it is documented as a possible return value of gettype... throw new Exception('could not determine the type of the variable!'); } if($actual_type==='object'){ if(!is_a($variable,$type)){ $dbg=get_class($variable); throw new UnexpectedValueException('variable is an object which does NOT implement class: '.$type.'. it is of class: '.$dbg); } return $variable; } if($actual_type==='resource'){ $dbg=get_resource_type($variable); if($dbg!==$type){ throw new UnexpectedValueException('variable is a resource, which is NOT of type: '.$type.'. it is of type: '.$dbg); } return $variable; } //now a few special cases if($type==='bool'){ $parsed_type='boolean'; } else if($type==='int'){ $parsed_type='integer'; } else if($type==='float'){ $parsed_type='double'; } else if($type==='null'){ $parsed_type='NULL'; } else{ $parsed_type=$type; } if($parsed_type!==$actual_type && $type!==$actual_type){ throw new UnexpectedValueException('variable is NOT of type: '.$type.'. it is of type: '.$actual_type); } //ok, variable passed all tests. return $variable; }

I have made a little bench between three method of type hinting for native type (string, integer, ...).

First method : by test type in function like :<?phpfunction testTest($arg) { if (!is_string($arg)) {trigger_error('Argument $arg passed to test must be an instance of string, other given'); } return $arg;}?>

Daniel, thank you for the type hinting class. Very useful.For anyone having a problem where php isn't setting Daniel's error handler, as was the case for me, try changing initializeHandler function to the following:

Type hinting works with interfaces too. In other words, you can specify the name of an interface for a function parameter, and the object passed in must implement that interface, or else type hinting throws an exception.

i use eclipse ganymede as an IDE and it offers "intellisense" where it can, i.e. when variables are "declared" via type hinting or a "new"-statement . i found using the following pattern helps eclipse along as well:

<?php
class MyClass{

public static function Cast(MyClass &$object=NULL){
return $object;
}

public method CallMe(){
}
}

$x=unserialize($someContent);
$x=MyObject::Cast($x);
$x->CallMe();
?>

after calling Cast(), due to the type hinting, eclipse offers me the "CallMe" function in a dropdown when i type "$x->" in the code afterwards.

i found this very practical und included the Cast() function in my code template for new classes. i've been wondering, if there is a drawback i oversaw, but i haven't noticed any negative effects so far... maybe some of you will find this just as handy as i do ;o)

p.s. do note: when used in inherited classes a STRICT-notice comes up, because the function definition of the inherited class doesn't match the parent's definition (different type hinting) - but it works great!

One useful thing with Type Hinting that I could not find in the documentation (but tested) is that you can also use an Interface in the hint (versus a Class). This is a very useful tool if you are trying to code to Interfaces rather than Classes (which is common in Test Driven Development and Dependency Injection paradigms). It means your external class can present itself into the method as long as it implements the nominated Interface (obviously).