PHP Typecasting

PHP typecasting

(int)$value

saves one function call compares to intval($value) and settype($value, 'int').

And (int)$value is clean enough, so I prefer this way.

When you need to use intval($value) is that when you need to use the specified base for the conversion (the default is base 10). intval accepts a second parameter for the base for the conversion.

Type casting in PHP - security and efficiency

PHP always needs to know the "current type" of a value before it can use it for any purpose, including initializing a variable. This "current type" is metadata (an enumeration) that goes together with all values.

In your example code the casts are meaningless because you are initializing variables using literal values, which are always of the obvious type:

$s = "foo";
echo is_string($s); // 1

$s = (string)"foo";
echo is_string($s); // also 1

The same goes for the integer and array.

There is at least one case where the type of the variable would be something other than you might expect at first sight:

$i = PHP_INT_MAX + 1; // or use something like 999999999999
echo gettype($i); // "double"!

In this case using a cast would make $i an integer, but it would also change its value:

$i = (int)(PHP_INT_MAX + 1);
echo gettype($i); // "integer"
echo $i; // a very large negative number -- what?!?

Of course this is not caused by a missing cast, but is rather an artifact of how numbers are treated in PHP. So the conclusion is clear: there is no point in using casts when initializing with literals.

If you are initializing a variable that you intend to use as type X with a value that is of a different type Y (or of an unknown type) then there is a reason to use an explicit cast: documenting in code how the variable is going to be used going forward. But don't overestimate the benefit: this information is only for human consumption; PHP will automatically do the usual type conversions whenever you try to use a variable as a different type than it is.

PHP Typecasting - Good or bad?

For better or worse, loose-typing is "The PHP Way". Many of the built-ins, and most of the language constructs, will operate on whatever types you give them -- silently (and often dangerously) casting them behind the scenes to make things (sort of) fit together.

Coming from a Java/C/C++ background myself, PHP's loose-typing model has always been a source of frustration for me. But through the years I've found that, if I have to write PHP I can do a better job of it (i.e. cleaner, safer, more testable code) by embracing PHP's "looseness", rather than fighting it; and I end up a happier monkey because of it.

Casting really is fundamental to my technique -- and (IMHO) it's the only way to consistently build clean, readable PHP code that handles mixed-type arguments in a well-understood, testable, deterministic way.

The main point (which you clearly understand as well) is that, in PHP, you can not simply assume that an argument is the type you expect it to be. Doing so, can have serious consequences that you are not likely to catch until after your app has gone to production.

To illustrate this point:

<?php

function displayRoomCount( $numBoys, $numGirls ) {
// we'll assume both args are int

// check boundary conditions
if( ($numBoys < 0) || ($numGirls < 0) ) throw new Exception('argument out of range');

// perform the specified logic
$total = $numBoys + $numGirls;
print( "{$total} people: {$numBoys} boys, and {$numGirls} girls \n" );
}

displayRoomCount(0, 0); // (ok) prints: "0 people: 0 boys, and 0 girls"

displayRoomCount(-10, 20); // (ok) throws an exception

displayRoomCount("asdf", 10); // (wrong!) prints: "10 people: asdf boys, and 10 girls"

One approach to solving this is to restrict the types that the function can accept, throwing an exception when an invalid type is detected. Others have mentioned this approach already. It appeals well to my Java/C/C++ aesthetics, and I followed this approach in PHP for years and years. In short, there's nothing wrong with it, but it does go against "The PHP Way", and after a while, that starts to feel like swimming up-stream.

As an alternative, casting provides a simple and clean way to ensure that the function behaves deterministically for all possible inputs, without having to write specific logic to handle each different type.

Using casting, our example now becomes:

<?php

function displayRoomCount( $numBoys, $numGirls ) {
// we cast to ensure that we have the types we expect
$numBoys = (int)$numBoys;
$numGirls = (int)$numGirls;

// check boundary conditions
if( ($numBoys < 0) || ($numGirls < 0) ) throw new Exception('argument out of range');

// perform the specified logic
$total = $numBoys + $numGirls;
print( "{$total} people: {$numBoys} boys, and {$numGirls} girls \n" );
}

displayRoomCount("asdf", 10); // (ok now!) prints: "10 people: 0 boys, and 10 girls"

The function now behaves as expected. In fact, it's easy to show that the function's behavior is now well-defined for all possible inputs. This is because the the cast operation is well-defined for all possible inputs; the casts ensure that we're always working with integers; and the rest of the function is written so as to be well-defined for all possible integers.

Rules for type-casting in PHP are documented here, (see the type-specific links mid-way down the page - eg: "Converting to integer").

This approach has the added benefit that the function will now behave in a way that is consistent with other PHP built-ins, and language constructs. For example:

// assume $db_row read from a database of some sort
displayRoomCount( $db_row['boys'], $db_row['girls'] );

will work just fine, despite the fact that $db_row['boys'] and $db_row['girls'] are actually strings that contain numeric values. This is consistent with the way that the average PHP developer (who does not know C, C++, or Java) will expect it to work.


As for casting return values: there is very little point in doing so, unless you know that you have a potentially mixed-type variable, and you want to always ensure that the return value is a specific type. This is more often the case at intermediate points in the code, rather than at the point where you're returning from a function.

A practical example:

<?php

function getParam( $name, $idx=0 ) {
$name = (string)$name;
$idx = (int)$idx;

if($name==='') return null;
if($idx<0) $idx=0;

// $_REQUEST[$name] could be null, or string, or array
// this depends on the web request that came in. Our use of
// the array cast here, lets us write generic logic to deal with them all
//
$param = (array)$_REQUEST[$name];

if( count($param) <= $idx) return null;
return $param[$idx];
}

// here, the cast is used to ensure that we always get a string
// even if "fullName" was missing from the request, the cast will convert
// the returned NULL value into an empty string.
$full_name = (string)getParam("fullName");

You get the idea.


There are a couple of gotcha's to be aware of

  • PHP's casting mechanism is not smart enough to optimize the "no-op" cast. So casting always causes a copy of the variable to be made. In most cases, this not a problem, but if you regularly use this approach, you should keep it in the back of your mind. Because of this, casting can cause unexpected issues with references and large arrays. See PHP Bug Report #50894 for more details.

  • In php, a whole number that is too large (or too small) to represent as an integer type, will automatically be represented as a float (or a double, if necessary). This means that the result of ($big_int + $big_int) can actually be a float, and if you cast it to an int the resulting number will be gibberish. So, if you're building functions that need to operate on large whole numbers, you should keep this in mind, and probably consider some other approach.


Sorry for the long post, but it's a topic that I've considered in depth, and through the years, I've accumulated quite a bit of knowledge (and opinion) about it. By putting it out here, I hope someone will find it helpful.

Type Hinting vs Type Casting in setters php

They are NOT functionally equivalent.

Type Hinting: You are dictating what type must be passed. If the given value is of the incorrect type, then an error is generated. This does not cast or "convert" the passed value into a specific type.

Type Casting: Regardless of what value is passed, you are "converting" it into the correct type. If your function "needs" an array, then why let a boolean be passed and then cast it to an array?

Also, type hinting allows you to specify an object instance of a specific class. In the following, $bar must be an instance of class Bar or else an error is generated:

public function setBar(Bar $bar)

You can not type cast a variable to an object of a specific class.

PHP Cast to my class

As I know, in PHP you can only cast to some types:

(int), (integer) - cast to integer
(bool), (boolean) - cast to boolean
(float), (double), (real) - cast to float (real deprecated in PHP 8.0)
(string) - cast to string
(binary) - cast to binary string (PHP 6)
(array) - cast to array
(object) - cast to object
(unset) - cast to NULL (PHP 5) (depracted in PHP 7.2) (removed in 8.0)

(see Type Casting)

Instead you could use instanceof to check of specific type:

if($yourvar instanceof YourClass) {
//DO something
} else {
throw new Exception('Var is not of type YourClass');
}

EDIT

As mentioned by Szabolcs Páll in his answer, it is also possible to declare a return type or parameter type, but in that cases an exception (TypeError) will be throwen, if the type does not match.

function test(): string
{
return 'test';
}

function test(string $test){
return "test" . $test;
}

Since PHP 7.2 it is also possible to make the types nullable by adding ? in front of them:

function test(): ?string
{
return null;
}

Typecasting vs function to convert variable type in PHP

There's no difference in the resulting value, just:

  • (float) is a language feature and very quick
  • floatval() incurs the overhead of a function call (minimal, but nonetheless...)
  • floatval() as a function can be used in ways that (float) cannot, e.g. array_map('floatval', $foo)

The last point is, I believe, the main reason for floatval's existence: so each casting operation has a function equivalent, which can be useful in some circumstances.

Type casting using string for type

You have to use the settype() function :

<?php 

$var = 42;
var_dump($var); // int 42
$type = 'string';
settype($var, $type);
var_dump($var); // string '42'

Adapted to your code snippet :

public function getValueAttribute($value){
settype($value, $this->type);
return $value;
}

PHP Type Casting

Was the task to round the float to the nearest whole number? In which case:

$float = 1.92;
$float += 2;

echo round($float);

outputs 4. Or a straight cast:

$float = 1.92;
$float += 2;

echo (int) $float;

outputs 3.

How does the typecasting mechanism work in PHP during division of different types of numbers?

Sadly, it's a bit muddy in PHP - it's not purely down to either the operator or the operands. As per the manual:

The division operator ("/") returns a float value unless the two operands are integers (or strings that get converted to integers) and the numbers are evenly divisible, in which case an integer value will be returned. For integer division, see intdiv().

So you'll get a float back unless both the operands are integers and they are exactly divisible.

<?php
var_dump(10 / 5);
var_dump(10.0 / 5.0);
var_dump(10 / 4);

int(2)
float(2)
float(2.5)


Related Topics



Leave a reply



Submit