What is New in PHP Type Hinting Support in PHP 8

Recommend this page to a friend!
  Blog PHP Classes blog   RSS 1.0 feed RSS 2.0 feed   Blog What is New in PHP Ty...   Post a comment Post a comment   See comments See comments (0)   Trackbacks (0)  

Author:

Updated on: 2021-01-26

Posted on: 2021-01-26

Viewers: 563 (January 2021 until February 2021)

Last month viewers: 219 (February 2021)

Categories: PHP Tutorials

Type hinting is a feature that PHP provides to declare types of class variables, function parameters and return values, so you can detect and fix programming mistakes as early as possible in your developments.

Read this article to learn about PHP version 7.4 type hinting support. You can also read about the new type hinting features of PHP version 8.0.

The article also presents a list of the kinds of type hinting that are available in each PHP version.

In the end you can learn more about type hinting in practice with code examples.




In this article you will learn about:

  1. What type hinting means
  2. Why using type hinting
  3. What value types are supported by PHP
  4. The different kinds of type hinting
  5. Type hinting kinds sorted by PHP version
  6. Type hinting in practice
  7. New features in PHP version 8.0 regarding the type hinting
  8. Enhancing the type hinting by using phpDoc Annotations

What type hinting means

Type-hinting: Type declaration or type assignment.

Type hinting means that a certain value type can be assigned to a function argument, a class variable or the return value of a function or class method. PHP does not yet provide a type hinting support for global and local variables, not even in version 8.

Type hinting serves two basic purposes in the code:

  • Improvement of data and code integrity: With type hinting, it is more difficult to abuse of functions by passing ore returning values of the wrong types. Additionally, the need to use explicit type checks in the code (is_numeric (), is_bool (), is_array (), ...) can be reduced to a minimum.
  • Increase the readability of the code: It becomes clearer the types of values a function accepts and what it returns. So that will make you more productive when you are working in a team, as well it can make your own old code more transparent.

Why using type hinting

PHP itself is a typeless programming language, i.e. PHP determines which type (text, number, etc.) has a variable, a class property, an argument or the return value of a function and sets this accordingly.

Starting with PHP version 5.0, (optional) type hinting support was gradually introduced.
From the 'typeless' programming language of version 4, PHP has evolved from version to version, from as a 'weakly typed' language up to the almost completely typed programming language introduced with version 8.

Which kind of type hinting was introduced with which PHP version is summarized in section Type hinting kinds sorted by PHP version.

Note: Type hints are still an optional part of PHP, so they are not absolutely necessary for any particular code to be run.

For example, think about a function that expects an integer as an argument:

Without type hinting PHP tries to convert any value that is passed into an integer before the value is used in the function, regardless of whether it is a string, a boolean value or something else. Then it executes the function code without an error message.

Depending on the error reporting level set in PHP configuration, at most you get Notice or Warning messages in the output, but the execution of the code is not stopped.

In many cases, passing non-integer values leads to a conversion to the integer value  0, unless it was passed a string that begins with numeric characters. But even then, the result is probably not what is expected.

Since the error reporting PHP options is often disabled on production systems, such errors often go unnoticed. Often it leads to unpredictable behavior of the PHP application in other parts of the code.

If type hinting is used and it points that this function expects an integer, an error message is generated when the function is called with a wrong value, and the source of the error is immediately exposed. The error message refers to the actual line of code in which the wrong argument is passed.

Without type hinting, a notice or warning message would possibly be generated elsewhere in the code. And although the origin of the error is the wrong argument, this is usually makes it difficult to identify the original part of the code that caused the error message and/or the line of code the message is shown for.

In addition, such errors can be detected by modern development environment tools, like for instance IDEs (e.g. PHPStorm, Eclipse IDE, Zend Studio, etc...) and are displayed during the coding process.

This can lead to a significantly higher code quality and reliability, as well it can reduce the testing efforts. Additionally, this may result in fewer 'coding - testing - troubleshooting' cycles, which in most cases means faster project times.

The increase in effort for coding with type hinting is paid back by a higher code quality and  especially in the last phase of a project it may lead to shorter test and bug fixing times.

Even more extensive tests can be carried out with static analysis tools (e.g. PHP CodeSniffer, PHPLint, PHPStan, Scrutinizer, etc.) in order to avoid errors, as early as possible, during the coding phase.

Of course these tools depend on the presence of type hints, so they already report the lack of type hints when you use them.

Another advantage of type-hinting is greater transparency when working in a team. It becomes clearer to every programmer on the team, what kind of values a class property should contain, what kind of arguments a function expects, or what type of value a function returns. This also applies, of course, to your own code that you may have written in the past.

In summary, type-hinting helps us:

  • to find runtime errors
  • with the faster discovery of coding errors
  • while coding when using a modern development environment
  • in the static code analysis - and thus in the improvement of the code with the help of appropriate tools
  • to improve the transparency of the code

What data types are supported by PHP

To use type hinting correctly, we first have to look at the value types that PHP supports.

  • Scalar types
    A scalar variable is a variable that stores a single value. Often these are also referred to as elementary value types.
    PHP distinguishes between the following types:

  • Compound types
    In contrast to the scalar types, a variable of a compound value type that can contain several values of scalar or further compound data types. PHP distinguishes between the following types:

  • Special types

    • resource:
      This is not a data type in the actual sense but a special variant of the data type int.
      Attention: For technical reasons this type is not supported for type hinting!!!
    • null:
      Again, this is not a value type in the strict sense, but a special value that can be assigned to every variable, which is equivalent to 'no value assigned'.
    • self:
      The keyword self is also not a distinct value type. However, this always denotes the name of the class in the scope of which it is used. self is allowed for type assignment inside of classes.

The different kinds of type hinting

To assign a value type, the four scalar, the four compound data types and the names of all classes known in the current script are available.

If you want to specify a variable that can be of a certain value type or hold the value null, the respective data type have to be used with prefixed '?'. Assigning a default value of null on the type reference of function arguments has the same effect.

function myFunction(string $strName)          // string as argument is expected
function myFunction(int $iCount)              // int value as argument is expected
function myFunction(float $fltPrice)          // float value as argument is expected
function myFunction(bool $bValidate)          // bool value as argument is expected
function myFunction(array $aOptions)          // array as argument is expected
function myFunction(MyClass $oMyClass)        // instance of MyClass as argument is expected

// nullable type hint with preceeding '?'...
function myFunction(?string $strName)         // string or null as argument is expected
// ... or assigning default value of null
function myFunction(string $strName = null)   // no value, string or null as argument is expected

If the value type is not unique, like for instance if both a string and an int are allowed, this can not be indicated by a type hint in PHP prior to version 8.0.

In order to receive support from the IDE or analysis tools for these cases, a specification can be made in the comment (see also section Enhancing the Type-hinting by using phpDoc Annotations).

As already mentioned in the section about the value types above, the value type resource has a special meaning regarding type hinting. No type hint can be specified for parameters or return values of this value type. To ensure type safety, is_resource() should be used in the code.

Local and global variables

Up to and including version PHP 8, no type-hinting is provided for local and global variables. Both PHP and the various IDE's and analysis tools determine the type of a variable according to the value that was assigned for last in the code to those variables.

Function arguments / Arguments of class methods

Probably the most important kind of type hinting consists in specifying the value types of the arguments of a function or class method.

This helps to assure that the correct values are passed. When incorrect types are passed as argument values, an error is triggered at the line of code where the wrong type value is used, besides the line of code where the value is passed to the function.

Return values

The data type is specified after the function definition, separated by a colon. A function that has no return value is identified with the keyword void.

function getAge(string $strName) : int    // function returns value of type int
function initData() : void                // function doesn't return any value
function getParams() : ?array             // function returns array or null

Class properties

In order to determine the type of value of class properties, type hinting is specified in the declaration before the name and after the visibility of a property.

In contrast to the declaration of function arguments, a property can only be assigned a initial value of null if the data type is marked as 'nullable' by a preceeding '?'. The data type callable is not permitted for class properties!

class MyClass
{
    public string $strString;
    protected int $iValue;
    private array $aArray1;
    private array $aArray2 = null;      // null assignment not allowed !!!
    private ?array $aArray3 = null;     // valid assignment for 'nullable' array
}

The keyword self can also be used in classes for properties, method arguments and return values.

class MyClass
{
    protected self $oNextSibling;
    public function setNextSibibling(self $oSibbling) : void
    {
        ....
    }
}

Type hinting kinds sorted by the PHP version when they were introduced

Version 5.0

As of version 5.0, functions that expect an object of a certain class as an argument can be preceeded with the corresponding class name. If the function expects an object of its own class, the keyword self is also permitted. Scalar value types are not yet supported!

Version 5.1

  • It became possible to assign the default value null.
  • The value type array became supported.

Version 5.4

  • The callable value type became supported for function arguments.

Version 7.0

  • The scalar value types (string, int, float, bool) are supported now.
  • The value type of the return value of functions and class methods can be specified.

Version 7.1

  • By prefixing the data type with a '?' it can be specified that a parameter or return value can also have the value null in addition to the specified value type.
  • Functions that do not return any value can be marked with return type void.
  • Introduction of the iterable value type.

Version 7.2

  • Introduction of the object value type.

Version 7.4

  • Support of type declarations for class properties.

Version 8.0

  • Union Types.
  • Constructor Property Promotion.

Type hinting in practice

A consistent type check is only carried out if this is activated in the PHP code directly at the beginning of the file by the following instruction:

declare(strict_types=1);

The strict type check must be activated explicitly for each PHP file. Unfortunately there is no global configuration option to set the strict checks on directory or web space level.

A very simple example follows to show how a code snippet behaves at runtime under the various conditions:

Whether 'notice' or 'warning' messages are issued depends on the setting of the error reporting level set in the PHP configuration!

Without type hinting

// function without type hints
function myFunc($a, $b)
{
    return $a * $b;
}

echo myFunc(2, 3);
echo myFunc(2, 3.1);
echo myFunc(2, '3');
echo myFunc(2, '3text');    // causes 'Notice' in line 4
echo myFunc(2, 'text3');    // causes 'Warning' in line 4

All calls of the function myFunc() are run. The first three calls all produces an output of 6.
The fourth call also outputs 6, since PHP converts strings that start with numeric characters up to the first occurrence of a non-numeric character to its numeric value. However, following 'notice' is generated:

Notice: A non well formed numeric value encountered in Example.php on line 4

With the fifth call, the result is 0 and it causes a 'warning':

Warning: A non-numeric value encountered in Example.php on line 4

Please note that line 4, in which the arithmetic operation takes place, is output as the triggering code line.

In order to localize the actual 'culprit' (i.e. the calls to the function with an invalid argument in line 9 or 10), the call stack of the PHP message needs to be examined more closely!

Using type hinting without the 'strict_types' setting

// function with type hints
function myFunc(int $a, int $b)
{
    return $a * $b;
}

echo myFunc(2, 3);
echo myFunc(2, 3.1);
echo myFunc(2, '3');
echo myFunc(2, '3text');    // causes 'Notice' in line 4
echo myFunc(2, 'text3');    // causes 'Fatal error' in line 11 (this line)

The result of the first four calls are identical with the previous example without type hinting.

With the fifth call, however, the execution of the PHP script is halted by triggering a 'fatal error'.

Fatal error: Uncaught TypeError: Argument 2 passed to myFunc() must be of the type int, string given, called in Example.php on line 11 and defined in Example.php on line 2

But take a closer look at the message: The line of code that is causing the error is no longer line 4, but line 11. That is where the invalid function call takes place. Line 2, from where the function called function is also shown to give further information to help finding the problem better.

Type hinting with 'strict_types' checking

declare(strict_types=1);

// function without type hints
function myFunc(int $a, int $b)
{
    return $a * $b;
}

echo myFunc(2, 3);
echo myFunc(2, 3.1);               // causes 'Fatal error' in line 10 (this line)
echo myFunc(2, '3');               // ... 
echo myFunc(2, '3text');           // ...    
echo myFunc(2, 'text3');           // ...

Activating the strict_types check, all invalid calls of the myFunc() function will make PHP trigger a 'fatal error'!

In which cases type hinting is not possible to use?

It is not possible to use type hinting if:

  1. A parameter or return value can have different value types. This can be the case when:
    • The return type usually is int , but in case of an error the function returns the bool value false (e.g. strpos(...)).
    • An argument is declared intentionally to supports multiple value types, e.g. a function function getEvent($date) accepts as $date parameter either a unix timestamp (i.e. an int), a DateTime object or a string containing a valid date.
  2. For functions that expect or return a resource.

The first case was solved with the introduction of Unions in PHP 8.0, the second case still has to be handled by using type checking with specific code using  the functionis_resource.

What should be considered when using 'nullable' values

In general, each type assignment can be marked as nullable, which means that the respective value can either be of the specified data type or contains null. However, this option should be used with caution, because it may mean that an important advantage that you would gain using type hinting is lost.

Due to the type hinting, especially with arrays and objects, there is no longer need for time consuming type checking in the code, which is required when using nullable values.

Check the following scenario:

function getOptions() : ?array 
{
    ...
    if (...) {
        return null;
    }
    ...
}

$aOptions = getOptions();
foreach ($aOptions as $option) {
    ...
}

This code example would result in a warning when executing the foreach statement if getOptions() returns null:

Warning: Invalid argument supplied for foreach() in Example.php

For type safety we should check $aOptions for validity before we use it.

In the following implementation without a 'nullable' return value, however, the validity check of $aOptions can be omitted:

function getOptions() : array 
{
    ...
    if (...) {
        return [];
    }
    ...
}

$aOptions = getOptions();
foreach ($aOptions as $option) {
    ...
}

Therefore, it is important to consider for each case whether the value null should be expected and processed accordingly.

In the code snippet above, for example, the value null could indicate that the options were not correctly initialized, whereas an empty array simply means that there are currently no options available... but even then, an incorrect initialization should lead to an error or appropriate treatment within the getOptions() function and not outside when processing the return value.

Summary

As can be seen in the example shown above, the behavior changes even with very simple program code by using type hinting and activating the strict type check.

In order to assure predictable behavior of the code, strict type checking should generally be switched on and, wherever possible, a clear type declaration should be made.

Although this other post was written originally considering PHP version 7.1, I think it is still worth reading it and dealing with the statements made.

New features in PHP version 8.0 regarding the type hinting

Union Types 2.0

The most important evolution of PHP 8.0 regarding type hinting is the introduction of the Union data type.

Before PHP 8, union types could only be specified in phpdoc annotation comments. Now this can be made directly as type hint by specifying the allowed value types in a list separated by the pipe symbol '|'.

Union types supports all available value types whereby the following limitations have to be considered:

  • The keyword void must not be part of a union - either a function delivers a result or it doesn't!
  • The null value is only supported as part of a union, it is not allowed to use it as a standalone type.
  • The nullable type notation ('?datatype') is still allowed (corresponds to 'datatype|null'). However, this notation may not be used within a union. '?string|int' is therefore not allowed - declare such combinations as 'string|int|null'.
  • Since many functions return a certain data type and return the boolean value false in case of an error or another special condition, this pseudo-type is also supported. (e.g. strpos(), strstr(), substr(), ...).

You can find further information on the PHP documentation.

Constructor Property Promotion

Although this change does not directly concern the topic of type hinting, it should not be left unmentioned here, because it can be used to simplify the declaration of the properties and the constructor of a class.

This innovation affects class properties whose value should be set when the constructor is called:

// Before PHP 8.0 
class MyClass 
{
protected int $a; protected int $b; public function __construct(int $a, int $b = 0.0) { $this->a = $a; $this->b = $b; } }

Instead of having to write each property four times at different places, this can now be shortened in PHP 8.0 as follows:

// Using PHP 8.0 and property promotion
class MyClass 
{ public function __construct(protected int $a, protected int $b = 0.0) { } }

The following restrictions have to be considered:

  • Redeclaration of a property is forbidden
    class MyClass
    {
      protected int $a;
      public function __construct(protected int $a) {}
    }
  • The value type callable must not be used, since it is not a valid type for a property.
  • The default value of null must not be assigned to a non-nullable property.
    class MyClass
    {
      public function __construct(protected int $a = null) {}
    }

You can find a more detailed description in the PHP documentation.

Consistent type errors for internal functions

Sometimes a different type checking for internal and user-defined functions can be specified more consistently using PHP 8.0. See the PHP documentation for more information.

Enhancing the type hinting by using phpDoc Annotations

Not only the most style guides recommend the use of phpDoc-compatible comments in the source code, most IDEs also support phpDoc and display the comments stored in this way when using a function, which is often very helpful during coding.

Most static analysis tools also use phpDoc annotations to do a more detailed type check.

Note: phpDoc annotations do NOT affect PHP type checking at runtime!!

At this point you will not find a detailed description of the phpDoc annotations but only the special features that should be in mind in connection with the type hinting are mentioned here.

Value types not supported by PHP

The value types Integer, Numeric, Double and Boolean accepted by phpDoc should not be used, as they are not valid PHP value types.

Declaration if there are multiple value types that are allowed

If an argument or return value accepts different value types, before PHP 8.0 it was only possible to define more precise types using phpDoc annotations.

The value type behind the @param, @return and @ var annotations can be declared as mixed.

This only indicates that several different data types can occur but no further type check is carried out. If several data types are specified separated with the pipe symbol '|', only these types are permitted. There is also the option of defining null or false as permitted values.

/**
 * Function taking string param and date of following formats:
 * - unix timestamp (int)
 * - DateTime
 * - well formed date string
 * @param string $strParam
 * @param int|DateTime|string $date
 * @return int|false any valid int or false in case of an error
 */
public function myFunction(string $strParam, $date)
{
    ....
}

Values of the type resource

In phpDoc itself, the data type resource is known, but unfortunately the treatment of values of this type is not uniform in the various IDEs and analysis tools.


The value type resource is sometimes recognized and attempts are made to carry out appropriate checks.


In any case, the main aspect to be seen here is that resource values are correctly documented in order to ensure code transparency.




You need to be a registered user or login to post a comment

Login Immediately with your account on:

FacebookGmail
HotmailStackOverflow
GitHubYahoo


Comments:

No comments were submitted yet.



  Blog PHP Classes blog   RSS 1.0 feed RSS 2.0 feed   Blog What is New in PHP Ty...   Post a comment Post a comment   See comments See comments (0)   Trackbacks (0)  

For more information send a message to info at phpclasses dot org.