View RSS Feed

traq

Not My Type... : )

Rate this Entry
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.
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;
In a "strictly typed" language (like C++ or Java, for example), this would result in an error:
.....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 do if( $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:
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. */ }
(...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.)


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:
PHP Code:
<?php
function loop_d_loo$array ){
    print 
"looping through array:\n";
    foreach( 
$array as $item ){ 
        
/* do something loopy */ 
    
}
}
but what if you forget?
PHP Code:
<?php
loop_d_loo
42 );
# > looping through array:
# > Warning: Invalid argument supplied for foreach() ...
But wait ...! You can use type hinting:
PHP Code:
<?php
function loop_d_loo( array $array ){
    print 
"looping through array:\n";
    foreach( 
$array as $item ){ 
        
/* do something loopy */ 
    
}
}

loop_d_loo42 );
# > Catchable fatal error: Argument 1 passed to loop_d_loo() must be of the type array, integer given ...
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:
  1. The function is not executed at all, leaving no "broken" data or half-baked output.
  2. 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 and set_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:
PHP Code:
<?php
# NOT REAL PHP #

function intHintint $int ){ /* do us some maths */ }

intHint);       // integer (1): we're all good.
intHint"1" );     // string "1" - but equivalent to (1), so cast it and we're all good.
intHint1.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.
intHinttrue );    // bool (TRUE) - casting to int would give (1), which is equivalent - but...  
                    //    I'm not sure about this one.
In Conclusion

That's my random thoughts for the day. Anyone care to share theirs?

Happy coding,

- Adrian

Submit "Not My Type... : )" to del.icio.us Submit "Not My Type... : )" to StumbleUpon Submit "Not My Type... : )" to Google Submit "Not My Type... : )" to Digg

Updated 12-01-2012 at 04:48 AM by traq

Categories
Off beat topics , PHP coding

Comments

  1. keyboard's Avatar
    I was pondering what it would like to be a walrus earlier.... does that count as random?

    But seriously - this is interesting traq... I've never even thought about this...
    Not sure how much you would actually use this in a live environment... but still usefull to know.
  2. traq's Avatar
    Quote Originally Posted by keyboard1333
    I was pondering what it would like to be a walrus earlier
    Ever seen 50 First Dates?
    Quote Originally Posted by keyboard1333
    Not sure how much you would actually use this in a live environment...
    Actually, my current long-term side project is organizing all of my PHP "boilerplate" code into a web app framework-like-thing, and type hinting is pretty useful (especially where a method [function] needs an object of a particular class).
  3. keyboard's Avatar
    Quote Originally Posted by traq
    Ever seen 50 First Dates?

    Actually, my current long-term side project is organizing all of my PHP "boilerplate" code into a web app framework-like-thing, and type hinting is pretty useful (especially where a method [function] needs an object of a particular class).
    I think so... that's the one where the girlfriend has amnesia and everyday she forgets him?
    (I've only seen a little bit of that one anyway).
    Adam Sandler is the boyfriend?

    What's that got to do with pondering being a walrus?

    Anyway, interesting traq......
  4. traq's Avatar
    That's the one. The boyfriend (Sandler) is planning a walrus study expedition. There is size-related walrus innuendo throughout the film.

    Beyond "interesting," how PHP handles type juggling is worth the read. It leads to obscure problems sometimes, especially if one isn't familiar with how it works. In general, though, it's meant to be a "transparent" process, and it works "well enough" that most PHP coders may never have to worry about it - or even know it exists.
    Updated 11-29-2012 at 09:09 PM by traq
  5. djr33's Avatar
    On the other side of things, if you don't do this, you find yourself validating a lot of function input. I've written if (!is_array($var)) { return false; } far too many times to count.

    I may try using this instead. It seems useful.
  6. traq's Avatar
    Interesting discussion on the topic over at PHPBuilder.