By classification, PHP is a dynamically typed and weakly typed language. These are two different concepts that often get mixed together. Dynamically typed languages do not require the explicit declaration of a variable before it is used. Weakly typed languages are those in which the variable is not of any specific data type, that is, its type can change through different value-type reassignments.
Let's take a look at the following example:
// dynamic typed (no specific type defined, directly assigning value)
$name = "Branko"; // string
$salary = 4200.00; // float
$age = 33; // int
// weak typed (variable value reassigned into different type)
$salary = 4200.00; // float
$salary = $salary + "USD"; // float
$salary = $salary . "USD"; // string
In the preceding code, we see three different variables being used, none of which are predefined with a certain type. We just have values declared into them. PHP then determines the type on the go. Even when the variable type is determined, it can still be changed by simply assigning another type of value to it. These are two very powerful concepts, which, when used wisely, can save us lines and lines of code.
However, these powerful features often indirectly encourage bad design. This is particularly noticeable when writing functions, either by forcing function designers into multiple data type checks, or forcing them into multiple function return types.
Let's take a look at the following example:
function addTab($tab) {
if (is_array($tab)) {
} elseif (is_object($tab)) {
} elseif (is_string($tab)) {
} else {
}
}
Given the type uncertainty of the input argument, the addTab function was forced to branch its logic. Similarly, the same function might decide to return different types of data, depending on the logic branch. Designs like these are usually a result of functions that simply try to do too much. The real problem is not even in the function, it is on the consumer side of things. If it happens that the developer using a function is not aware enough of the passing parameter type, unexpected results might occur.
To help us write more correct and self-documenting programs, PHP introduced type hinting.
PHP has supported function parameter type hinting from version 5.0, but only for objects, and from version 5.1 for arrays as well. With PHP 7, scalar types can be type-hinted as well, making it one of the more exciting features of the release. The following are the scalar type hints that are supported by PHP:
We can now write functions in either of the following ways:
- It can be function register($email, $age, $notify) { /* ... */}
- It can be function register($email, int $age, $notify) { /* ... */}
- It can be function register(string $email, int $age, bool $notify) { /* ... */}
However, simply hinting scalar types is not enough as type declarations are not enforced by default. PHP will simply attempt to convert to the specified type without complaint. By adding the declare(strict_types=1); directive as the first statement in a PHP file, we can enforce the strict type checking behavior. It is worth noting that this directive only affects the specific file it is used in, and does not affect other included files. The file-level directive was used to preserve the backward compatibility with numerous extensions and built-in PHP functions.
Let's take a look at the following example:
declare(strict_types=1);
function register(string $email, int $age, bool $notify) {
// body
}
register('user@mail.com', '33', true);
With strict types directive turned on, trying to pass an improper data type to a hinted scalar parameter would result in a \TypeError exception, as per the following output:
Fatal error: Uncaught TypeError: Argument 2 passed to register() must be of the type integer, string given, called in /test.php on line 11 and defined in /test.php:5 Stack trace: #0 /test.php(11): register('user@mail.co...', '33', true) #1 {main} thrown in /test.php on line 5.
Scalar type hints are a powerful new addition to the PHP language. They empower developers with an extra layer of protection during runtime, without really sacrificing the weak type system in general.