Question

PHP, as most of us know, has weak typing. For those who don't, PHP.net says:

PHP does not require (or support) explicit type definition in variable declaration; a variable's type is determined by the context in which the variable is used.

Love it or hate it, PHP re-casts variables on-the-fly. So, the following code is valid:

$var = "10";
$value = 10 + $var;
var_dump($value); // int(20)

PHP also allows you to explicitly cast a variable, like so:

$var = "10";
$value = 10 + $var;
$value = (string)$value;
var_dump($value); // string(2) "20"

That's all cool... but, for the life of me, I cannot conceive of a practical reason for doing this.

I don't have a problem with strong typing in languages that support it, like Java. That's fine, and I completely understand it. Also, I'm aware of - and fully understand the usefulness of - type hinting in function parameters.

The problem I have with type casting is explained by the above quote. If PHP can swap types at-will, it can do so even after you force cast a type; and it can do so on-the-fly when you need a certain type in an operation. That makes the following valid:

$var = "10";
$value = (int)$var;
$value = $value . ' TaDa!';
var_dump($value); // string(8) "10 TaDa!"

So what's the point?


Take this theoretical example of a world where user-defined type casting makes sense in PHP:

  1. You force cast variable $foo as int(int)$foo.
  2. You attempt to store a string value in the variable $foo.
  3. PHP throws an exception!! ← That would make sense. Suddenly the reason for user defined type casting exists!

The fact that PHP will switch things around as needed makes the point of user defined type casting vague. For example, the following two code samples are equivalent:

// example 1
$foo = 0;
$foo = (string)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

// example 2
$foo = 0;
$foo = (int)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

A year after originally asking this question, guess who found himself using typecasting in a practical environment? Yours Truly.

The requirement was to display money values on a website for a restaurant menu. The design of the site required that trailing zeros be trimmed, so that the display looked something like the following:

Menu Item 1 .............. $ 4
Menu Item 2 .............. $ 7.5
Menu Item 3 .............. $ 3

The best way I found to do that wast to cast the variable as a float:

$price = '7.50'; // a string from the database layer.
echo 'Menu Item 2 .............. $ ' . (float)$price;

PHP trims the float's trailing zeros, and then recasts the float as a string for concatenation.

Was it helpful?

Solution

In a weakly-typed language, type-casting exists to remove ambiguity in typed operations, when otherwise the compiler/interpreter would use order or other rules to make an assumption of which operation to use.

Normally I would say PHP follows this pattern, but of the cases I've checked, PHP has behaved counter-intuitively in each.

Here are those cases, using JavaScript as a comparison language.

String Concatentation

Obviously this is not a problem in PHP because there are separate string concatenation (.) and addition (+) operators.

JavaScript
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15

String Comparison

Often in computer systems "5" is greater than "10" because it doesn't interpret it as a number. Not so in PHP, which, even if both are strings, realizes they are numbers and removes the need for a cast):

JavaScript
console.log("5" > "10" ? "true" : "false"); // true
PHP
echo "5" > "10" ? "true" : "false";  // false!

Function signature typing

PHP implements a bare-bones type-checking on function signatures, but unfortunately it's so flawed it's probably rarely usable.

I thought I might be doing something wrong, but a comment on the docs confirms that built-in types other than array cannot be used in PHP function signatures - though the error message is misleading.

PHP
function testprint(string $a) {
    echo $a;
}

$test = 5;
testprint((string)5); // "Catchable fatal error: Argument 1 passed to testprint()
                      //  must be an instance of string, string given" WTF?

And unlike any other language I know, even if you use a type it understands, null can no longer be passed to that argument (must be an instance of array, null given). How stupid.

Boolean interpretation

[Edit]: This one is new. I thought of another case, and again the logic is reversed from JavaScript.

JavaScript
console.log("0" ? "true" : "false"); // True, as expected. Non-empty string.
PHP
echo "0" ? "true" : "false"; // False! This one probably causes a lot of bugs.

So in conclusion, the only useful case I can think of is... (drumroll)

Type truncation

In other words, when you have a value of one type (say string) and you want to interpret it as another type (int) and you want to force it to become one of the valid set of values in that type:

$val = "test";
$val2 = "10";
$intval = (int)$val; // 0
$intval2 = (int)$val2; // 10
$boolval = (bool)$intval // false
$boolval2 = (bool)$intval2 // true
$props = (array)$myobject // associative array of $myobject's properties

I can't see what upcasting (to a type that encompasses more values) would really ever gain you.

So while I disagree with your proposed use of typing (you essentially are proposing static typing, but with the ambiguity that only if it was force-cast into a type would it throw an error — which would cause confusion), I think it's a good question, because apparently casting has very little purpose in PHP.

OTHER TIPS

You're mixing the weak/strong and dynamic/static type concepts.

PHP is weak and dynamic, but your problem is with the dynamic type concept. That means, variables don't have a type, values do.

A 'type casting' is an expression that produces a new value of a different type of the original; it doesn't do anything to the variable (if one is involved).

The one situation where I regularly type cast values is on numeric SQL parameters. You're supposed to sanitize/escape any input value you insert into SQL statements, or (much better) use parameterized queries. But, if you want some value that MUST be an integer, it's much easier to just cast it.

Consider:

function get_by_id ($id) {
   $id = (int)$id;
   $q = "SELECT * FROM table WHERE id=$id LIMIT 1";
   ........
}

if I left out the first line, $id would be an easy vector for SQL injection. The cast makes sure that it's a harmless integer; any attempt to insert some SQL would simply result in a query for id=0

One use for type casting in PHP that I've found:

I'm developing an android app which makes http requests to PHP scripts on a server to retrieve data from a database. The script stores data in the form of a PHP object (or associative array) and is return as a JSON object to the app. Without type casting I would receive something like this:

{ "user" : { "id" : "1", "name" : "Bob" } }

But, using PHP type casting (int) on the user's id when storing it the PHP object, I get this returned to the app instead:

{ "user" : { "id" : 1, "name" : "Bob" } }

Then when the JSON object is parsed in the app, it saves me from having to parse the id to an Integer!

See, very useful.

One example is objects with a __toString method: $str = $obj->__toString(); vs $str = (string) $obj;. There is much less typing in the second, and the extra stuff is punctuation, which takes longer to type. I also think it's more readable, although others may disagree.

Another is making a single-element array: array($item); vs (array) $item;. This will put any scalar type (integer, resource, etc.) inside an array.
Altenatively, if $item is an object, its properties will become keys to their values. However, I do think that object->array conversion is a bit strange: private and protected properties are part of the array, and renamed. To quote the PHP documentation: private variables have the class name prepended to the variable name; protected variables have a '*' prepended to the variable name.

Another use is converting GET/POST data into appropriate types for a database. MySQL can handle this itself but I think the more ANSI-compliant servers might reject the data. The reason I only mention databases is that in most other cases, the data will have an operation performed on it according to its type at some point (i.e. int/floats will usually have calculations performed on them, etc.).

This script:

$tags = _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

will run fine for script.php?tags[]=one but will fail for script.php?tags=one, because _GET['tags'] returns an array in the first case but not in the second. Since the script is written to expect an array (and you have less control over the query string sent to the script), the problem can be solved by appropriately casting the result from _GET:

$tags = (array) _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

It can also be used as a quick and dirty method to ensure untrusted data isn't going to break something eg if using a remote service which has crap validation and must only accept numbers.

$amount = (float) $_POST['amount'];

if( $amount > 0 ){
    $remoteService->doacalculationwithanumber( $amount );    
}

Obviously this is flawed and also handled implicitly by the comparison operator in the if statement, but is helpful in ensuring you know exactly what your code is doing.

One "use" of PHP re-casting variables on-the-fly that I see in use often is when retrieving data from external sources (user input or database). It lets coders (note that I did not say developers) ignore (or not even learn) the different datatypes available from different sources.

One coder (note that I did not say developer) whose code I have inherited and still maintain does not seem to know that there exists a difference between the string "20" that is returned in the $_GET super variable, to between the integer operation 20 + 20 when she adds it to the value in the database. She is only lucky that PHP uses . for string concatenation and not + like every other language, because I have seen her code "add" two strings (a varcahr from MySQL and a value from $_GET) and get an int.

Is this a practical example? Only in the sense that it lets coders get away with not knowing what datatypes they are working with. I personally hate it.

Licensed under: CC-BY-SA with attribution
scroll top