Not My Type... : )
by
, 11-29-2012 at 05:13 AM (30958 Views)
Yes, the tutorial is still coming. It's big. It's in progress.
I read this recently and was intrigued. Don't know if anyone else would care or not ...
The author, nikic, does a great job summarizing the difficulties and potential benefits of type hinting, as well as giving a good explanation as to why PHP's implementation is incomplete. Personally, I look forward to being able to type-hint any type. It could save me a lot of code validating args, and I could focus more on actually writing code.
Lost?
If you're not familiar with what is being talked about...
PHP (javascript too, coincidentially, but of course it's implemented differently) is loosely typed.In a "strictly typed" language (like C++ or Java, for example), this would result in an error:PHP Code:
<?php
# this is a string (a _text character_, no different than 'a', 'b', or 'c').
$string = '5';
# this is an integer (a _whole number_, like 1, 2, or 3).
$int = 5;
print $string + $int;
.....Error ID:10T [you can't do math with sentences]
or something like that. But PHP does somthing called "type juggling": when it needs a number, it does its darndest to _have_ a number. Since "5" is reasonably equivalent to 5, it casts the string "5" to an integer (5). The string becomes an integer: literally, silently, automatically; and the output is as expected:
.....10
PHP is loosely typed for a very, very important reason: it's a web language. Data comes and goes across the internet. Data comes and goes across the internet as strings.
That's right, there's no such thing as an "integer" over HTTP. When you type in a URL, it's sent as text. When you submit a form, it's sent as text. Even if it's all numbers. Every $_GET or $_POST variable that PHP populates starts out as a string. But, you might need to do math with them, and thanks to type juggling you can.
(Incidentially, type juggling is also the reason you can doif( $variable )
.if()
needs a boolean (TRUE/FALSE) value, so PHP type-juggles and casts$variable
to a boolean.
(The rules about this process are kinda interesting, and the results sometimes more so. For example:(...some of these might seem ...logical... once you think about it for a while. But did you really expect to add a string to a boolean and get an integer? Anyway... this is a different discussion.)PHP Code:
<?php
$int = 10000; // integer (10,000)
$str = "10,000"; // string "10,000"
$sum = $int + $str; // integer (10,010)
$int = 5; // integer (5)
$str = "5"; // string "5"
$int .= $str; // string "55"
$a = array( 'a','b','c' ); // array (3)
$a .= 'd'; // string "Arrayd"
$str = "No"; // string "No"
$bool = true; // boolean (TRUE)
$sum = $str + $bool; // integer (1)
$sen = $str.$bool; // string "No1"
if( "FALSE" ){ /* true. */ }
Need a Hint?
Along with type juggling and type casting, PHP 5.1 introduced type hinting, and 5.4 has added more to it. Basically, you can make your functions require a particular type of argument. Here's a function that needs an array:but what if you forget?PHP Code:
<?php
function loop_d_loo( $array ){
print "looping through array:\n";
foreach( $array as $item ){
/* do something loopy */
}
}But wait ...! You can use type hinting:PHP Code:
<?php
loop_d_loo( 42 );
# > looping through array:
# > Warning: Invalid argument supplied for foreach() ...You might say "an error is an error," and you'd be right, but the error in the second case is preferable for two reasons:PHP Code:
<?php
function loop_d_loo( array $array ){
print "looping through array:\n";
foreach( $array as $item ){
/* do something loopy */
}
}
loop_d_loo( 42 );
# > Catchable fatal error: Argument 1 passed to loop_d_loo() must be of the type array, integer given ...
- The function is not executed at all, leaving no "broken" data or half-baked output.
- The error message is more descriptive and more accurately describes where the problem really came from, leading to better bug-fixin'.
Actually, with exception based error handling andset_error_handler()
, you can deal with the problem at runtime (by skipping it, or even casting the bad argument to the proper type or substituting a default value).
PHP 5.1+ allows you to hint that your argument needs to be an object (even of a particular class, or using a particular interface) or an array. Version 5.4 allows you to require an argument to be callable (a function/method or name of a function/method).
Back To Basics
You'll notice that the basic types, like integer, string, boolean, and so forth, are missing: you cannot use type hinting for scalar types (a "scalar" type is one that can only hold one value at a time, as opposed to arrays or objects, which can hold multiple values). You might think that scalar type hinting would be easier than with other types - that's the misconception that the article I referred to earlier was discussing.
The non-scalar types are easier to "hint at" because they aren't easily type-juggled - and under normal circumstances, no one would want them to be. (Why would you want to treat an object that contains a dozen properties (values) as a single value? How would it even make sense to try?) I have to admit that there are complications I never even thought of, but the author's favorite solution is the one I had in mind before I started reading: Strict Weak Hinting (with casts).
I think it would be quite useful if you could specify that you wanted a integer, and, given an argument looked like an integer, you would get one.
This would involve type juggling and casting working together: if the argument can be cast to the hinted type, and there is no loss of data, then cast it to the hinted type. Otherwise, throw an error. For example:In ConclusionPHP Code:
<?php
# NOT REAL PHP #
function intHint( int $int ){ /* do us some maths */ }
intHint( 1 ); // integer (1): we're all good.
intHint( "1" ); // string "1" - but equivalent to (1), so cast it and we're all good.
intHint( 1.5 ); // float (1.5) - casting to int would give (1), which is *not* equivalent to 1.5: throw an error.
intHint( "one" ); // string "one" - would be cast to (0), which is not equivalent: throw an error.
intHint( true ); // bool (TRUE) - casting to int would give (1), which is equivalent - but...
// I'm not sure about this one.
That's my random thoughts for the day. Anyone care to share theirs?
Happy coding,
- Adrian