What Do Strict Types Do in PHP

Do strict types in PHP affect variables?

It is because you are strict typing only the input of the function, not the return or any of the variables in the function itself. From the docs -

Strict typing applies to function calls made from within the file with strict typing enabled, not to the functions declared...

So your call to the function with a non-integer:

add('example',2);

will return the error you expect -

Fatal error: Uncaught TypeError: Argument 1 passed to add() must be of the type int, string given, called in...

But sending integers to your function will allow the function call to proceed and variables within will be weakly typed.

strict_types=1' does not seem to work in a function

"Strict types" mode only checks types at specific points in the code; it does not track everything that happens to the variable.

Specifically, it checks:

  • the parameters given to the function, if type hints are included in the signature; here you are giving two ints to a function expecting two ints, so there is no error
  • the return value of the function, if a return type hint is included in the signature; here you have no type hint, but if you had a hint of : int, there would still be no error, because the result of $a + $b + $c is indeed an int.

Here are some examples that do give errors:

declare(strict_types=1);
$a = '1';
$b = '2';
function FunctionName(int $a, int $b)
{
return $a + $b;
}
echo FunctionName($a, $b);
// TypeError: Argument 1 passed to FunctionName() must be of the type integer, string given

Or for a return hint:

declare(strict_types=1);
$a = 1;
$b = 2;
function FunctionName(int $a, int $b): int
{
return $a . ' and ' . $b;
}
echo FunctionName($a, $b);
// TypeError: Return value of FunctionName() must be of the type integer, string returned

Note that in the second example, it is not the fact that we calculated $a . ' and ' . $b that throws the error, it's the fact that we returned that string, but our promise was to return an integer. The following will not give an error:

declare(strict_types=1);
$a = 1;
$b = 2;
function FunctionName(int $a, int $b): int
{
return strlen( $a . ' and ' . $b );
}
echo FunctionName($a, $b);
// Outputs '7'

Do we still need 'declare(strict_types=1)' in PHP 8.1?

Yes! Tested with PHP 8.1.7 there is still a difference.

See this question to understand what the strict types do.

New in PHP 8.1: when a float value is coerced to an int value implicitly, like in the example from the other question, a deprecation notice is triggered:
PHP Deprecated: Implicit conversion from float 1.4 to int loses precision. More details

Further reading

  • What do strict types do in PHP?
  • PHP 8.1: Implicit incompatible float to int conversion is deprecated

Will type declaration in functions make my code perform better?

Ultimately, performance should not be your concern when adding type declarations. They exist to make the programmer's life easier, not the interpreter's. Use them if you think they will be helpful to you. Whether or not you use type declarations is unlikely to be the main determiner of your code's performance.

With that being said, currently (PHP 7.0) there is actually a small performance penalty when type declarations are used, because PHP now has to check the types of the values you pass in and out of functions. It is faster to not check at all, which is what happens when you don't have a type declaration.

However, it's a bit more nuanced than that, and the situation is changing.

Strict typing forces you to pass the correct types

If you're using strict typing, you have to pass a value of the ideal type (e.g. int), rather than being able to pass some other type (e.g. string) and have PHP convert it for you. Passing the correct type is faster than having PHP convert from some other type for you: type conversions take time.

Type declarations can avoid repeated type conversions

If you use type declarations, whether with strict typing or not, you usually avoid repeated type conversions for the same value.

Normally, if you have a function that does several number operations, and you pass in a string argument, PHP will have to convert it to the appropriate number type (int or float) each time it does an operation with it.

But if you have a type declaration on that argument, it gets converted only once, either automatically (weak typing mode) or because you explicitly converted it yourself.

Type declarations enable PHP to elide type-checks

Type declarations give your PHP runtime guarantees that it can sometimes use to avoid type checks it otherwise has to do. For example, take the following line of code:

$a = $b * $c;

Without type declarations, PHP first has to do an internal type check on $b and $c before it can actually multiply them; PHP needs to make sure they're both number types, convert them if they're not, and then perform the appropriate type of multiplication (int×int, float×int, etc.).

But if you've declared the types of $b and $c beforehand, then PHP doesn't need to check them here, and can immediately do the actual multiplication. That's faster. This is especially true if you do multiple operations with these variables inside the function.

This optimisation is implemented in PHP 7.1 when using OPcache.

Similar optimisations can be implemented to get rid of argument and return value type checks, too. For example, if one function declares a parameter is an int, there's no need to check its type again if it then passes that argument to another function which also expects an int. PHP 7.1 can currently drop some unnecessary return type checks (and more when using OPcache), but not argument type checks.

Because of these types of optimisations, the performance penalty for code with type declarations is getting smaller, and there's considerable potential for code with type declarations to be faster than code without in future.

Why does PHP strict typing allow function arguments of wrong type?

strict_types only affects function calls within the file in which it's declared. From the PHP docs:

Note:
Strict typing applies to function calls made from within the file with strict typing enabled, not to the functions declared within that file. If a file without strict typing enabled makes a call to a function that was defined in a file with strict typing, the caller's preference (coercive typing) will be respected, and the value will be coerced.

In your case, the examples are not calling the callback itself, they are passing it as an argument for array_map, meaning that wherever the function array_map is implemented, it has preference for coercive typing when array_map is calling your callback.

A possible solution to this would be to wrap array_map and make the call to your callback in a file in which strict_types is declared, such as this:

<?php
declare(strict_types=1);

$ids = ['1', '2', '3'];

function strict_array_map($fn, $arr){
return array_map(fn (...$arguments) => $fn(...$arguments), $arr);
}

// Now TypeError is thrown correctly
strict_array_map(fn (int $id) => $id, $ids);

// Throws PHP Fatal error: Uncaught TypeError: Return value of {closure}() must be of the type int, string returned
strict_array_map(fn ($id): int => $id, $ids);

https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.strict

Enabling 'strict_types' globally in PHP 7

This is deliberately not possible, because the implementation adopted after an extremely long discussion of scalar type hints was this one: https://wiki.php.net/rfc/scalar_type_hints_v5

It introduces two modes for scalar type parameters, which both guarantee that the function receiving the parameters gets the types that it requires in its signature. However, it provides two modes for how calling code can achieve that:

  • in mode 0, it automatically validates and casts certain scalar types (e.g. int parameter will convert '123' to 123, but error on 'hello')
  • in mode 1, it requires the caller to do that validation and casting before-hand, and rejects any parameter not of the correct type (e.g. both '123' and 'hello' are rejected for an int parameter)

The choice of mode is per-file, and based on the caller of the function, because:

  • the setting needs to affect built-in functions as well as user-defined ones
  • all code that calls functions needs to be checked or updated to work correctly in mode 1, but most old code will run fine in mode 0
  • with a global setting, you could only use libraries which had been tested with both modes, or the same mode you prefer
  • files that don't declare their preferred mode need to continue to work similarly to PHP 5.x to allow existing code to run; that is only possible if the default is mode 0

From the point of view of someone writing a reusable library:

  • regardless of the setting, your functions are guaranteed to receive the parameter types requested
  • if you want to receive errors when you call functions with the wrong types, you can use mode 1 without forcing other applications and libraries to be on the same setting
  • if you want to have the automatic checks and casts of mode 0, you can do that, but still interact with other libraries and applications which run in mode 1
  • old libraries which were written before PHP 7.0 (or which needed to support both when it came out) will continue to work roughly as before, because the default mode 0 is similar to existing behaviour for built-in functions

It's therefore up to you to tell PHP which files have been written to use strict type mode, and which haven't; and the way to do this is using the declare statement.

Strict mode in PHP

Kind of. You can activate the E_NOTICE level in your error reporting. (List of constants here.)

Every instance of usage of an undeclared variable will throw an E_NOTICE.

The E_STRICT error level will also throw those notices, as well as other hints on how to optimize your code.

error_reporting(E_STRICT);

Terminating the script

If you are really serious, and want your script to terminate instead of just outputting a notice when encountering an undeclared variable, you could build a custom error handler.

A working example that handles only E_NOTICEs with "Undefined variable" in them and passes everything else on to the default PHP error handler:

<?php

error_reporting(E_STRICT);

function terminate_missing_variables($errno, $errstr, $errfile, $errline)
{
if (($errno == E_NOTICE) and (strstr($errstr, "Undefined variable")))
die ("$errstr in $errfile line $errline");

return false; // Let the PHP error handler handle all the rest
}

$old_error_handler = set_error_handler("terminate_missing_variables");

echo $test; // Will throw custom error

xxxx(); // Will throw standard PHP error

?>

strict_types doesn't work when included

It is not currently possible to globally declare strict typing in PHP. From the manual:

It is possible to enable strict mode on a per-file basis

And

Strict typing applies to function calls made from within the file with strict typing enabled, not to the functions declared within that file. If a file without strict typing enabled makes a call to a function that was defined in a file with strict typing, the caller's preference (weak typing) will be respected, and the value will be coerced.

My guess is that, in part, this was to maintain compatibility amongst packages. What if you enable if globally and some package you're using requires strict typing to be off?

Until further notice you'll have to declare it in every file you're going to be using it.



Related Topics



Leave a reply



Submit