BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles PHP 7 — New Features for Types

PHP 7 — New Features for Types

Leia em Português

Bookmarks

Key Takeaways

  • PHP 7.0 added scalar type declarations for strings, integers, floating-point numbers, and booleans 
  • PHP 7.0 added support for return type declarations 
  • PHP 7.1 added support for nullable parameter types and return types 
  • void is a valid return type as of PHP 7.1
  • PHP 7.1 added a new compound type called iterable 
  • PHP 7.4 adds support for typed properties, which is types for class properties
     

PHP 7.x brings several improvements and new features that touch all aspects of the language, including better support for object oriented programming, extensions to classes and interfaces, improvements to the type system, error handling, and more. In this series of articles, we discuss new features across the various PHP 7.x versions.

 

We have already explored some of the improvements introduced in PHP 7.x in two articles: PHP 7 — Getting Started and OOP Improvements and  PHP 7 — Classes and Interfaces Improvements. To set the background for this article on PHP’s type system,  PHP is a weakly typed language, which implies the data type of variables does not need to be declared.

In this article we explore new type-related features available in PHP 7.x.

Scalar Type Declarations

Type declarations are nothing new to PHP.  Type declarations are used to annotate function parameters and annotated parameters require an argument to be of the specified type. Support for type declarations for class, interface, and self was added in PHP 5.0.0, while support for type declarations for array was added in PHP 5.1.0, and upport for type declarations for callable was added in 5.4.0.   

PHP 7.0 added support for scalar type declarations for types string (strings), int (integers), float  (floating-point numbers), and bool (booleans).

To demonstrate type declarations with an example, create a script (typedeclr.php) in the document root directory and copy the following listing to the script:

<?php
class A {}
class B extends A {}
class C {}
function f(A $a) {
	echo get_class($a)."\n";
}
 
f(new A);
f(new B);
f(new C);
?>

Class B extends class A. Class C does not extend any class and defines a function f() with a parameter of type A. Then, the script invokes function passing instances of class A, B and C successively as arguments. Assuming the same setup as in the first article PHP 7 — Getting Started and OOP Improvements, run the script with url http://localhost:8000/typedeclr.php. While calling f() passing instances of classes A and B does not generate an error and outputs the  value defined in function, calling f() with an argument of type C generates a TypeError:

AB

Uncaught TypeError. Argument 1 passed to f() must be an instance of A, instance of C given

Now, let’s discuss scalar types. Scalar types come in two kinds, coercive (default) and strict. Create a script sumints.php and define a vararg function taking parameters of type int. Invoke the function with some of the arguments supplied as floating-point numbers and others as string, for example:

echo SumInts(1,'3',0,3.6);

The sumints.php script is listed below, including a commented-out declare directive that we will discuss later when we cover strict scalar types

<?php
   //declare(strict_types=1);
  function SumInts(int ...$ints) 
  {
  	return array_sum($ints);
  }
  echo SumInts(1,'3',0,3.6);
?>

If you run the script, it will output the sum of the args converted to integer values.  In other words, wrong type arguments get converted (coerced) to the expected type. A value of 9 is output for the sumints.php as shown in Figure 1.

Figure 1. Output from sumints.php

Floating-point numbers are converted to an integer by removing the fractional value. As an example 3.6 becomes 3 and does not get rounded off to 4. String value ‘3’ gets converted to 3.

Strict scalar type declarations work in a completely different way. First off, strict mode is available only for scalar type declarations and not for class, interface, callable, or array type declarations. Go back to the previous script and comment in the declare() directive to use strict scalar type declarations. Notice that the declare() directive must appear at the top of the file in which strict mode for scalar type declarations is to be used.

   declare(strict_types=1);

Run the script again and you will see a TypeError is raised. The TypeError indicates that the arguments must be of type int.

Uncaught TypeError: Argument 2 passed to SumInts() must be of the type int, string given

Strict mode does not apply to function calls from internal (built-in) functions.

When using coercive mode (the default mode) for scalar type declarations, a null value argument does not get coerced or converted to the expected type and a TypeError is thrown instead. To demonstrate this, create a script sumints_null.php in which a function expecting an integer value is supplied a null arg:

<?php
  function SumInts(int ...$ints) 
  {
  	return array_sum($ints);
  }
  echo SumInts(1,'3',0,null);
?>

If you run the script, you will get a TypeError.

Uncaught TypeError: Argument 4 passed to SumInts() must be of the type int, null given

If you want to allow for a null argument, you must specify a null default value for the corresponding parameter as in the script listed below:

 <?php
  function SumInts(int $a=null) 
  {
  	return array_sum($a);
  }
  echo SumInts(null);
?>

With strict mode, argument values must be of the type defined in the function with one exception: integer values may be supplied to a function expecting a float.

To demonstrate this, create a script sum_floats.php in which a variadic function with float type parameters is defined. Invoke the function passing integers for some of its arguments:

<?php
declare(strict_types=1);
  function SumFloats(float ...$floats) : float
  {
  	return array_sum($floats);
  }
  echo SumFloats(1.1,2.5,3);
?>

Run the script to have the result, the float number 6.6, printed out. In this case, the int values get converted to float.

Our next example will show how to use strict scalar type declarations with float type.  Create a script sumfloats.php and define a function with two parameters of type float and return type int. Return type declaration is another new feature and is discussed in more detail later on, we will just assume they are ok for this example. The function returns a value by adding its two arguments. Invoke then the function with one argument of type int and the other of type string:

<?php
declare(strict_types=1);
 
  function SumFloats(float $a, float $b) : int
  {
  	return $a + $b;
  }
 
  echo SumFloats(1,'3');
 
?>

If you run the script, a TypeError is generated indicating that an argument passed to a function must be of type float while a string was given.

 Uncaught TypeError: Argument 2 passed to SumFloats() must be of the type float, string given

 If you remove the declare directive for strict mode and run the script again, coercive mode will give you an output value of 4.

Scalar type declarations may be used with strings.  Create a script reverse.php and add a function to it that takes a string parameter, reverses it, and returns the resulting string. Using a try/catch block, invoke the function with a floating-point value 3.5:

 <?php
  
  function Reverse(string $a) 
  {
  	return strrev($a);
  }
 
try{
  echo Reverse(3.5);
}catch (TypeError $e) {
	echo 'Error: '.$e->getMessage();
}
?>

If you run the script, you will get a value of 5.3 in coercive mode. If you add the directive declare(strict_types=1); for strict mode and run the script again, an Error is raised:

Error: Argument 1 passed to Reverse() must be of the type string, float given

Strict typing for scalar type declarations only applies to function calls from within the file in which strict typing is enabled and not to function calls from another file that does not declare strict typing. Similarly, if the file that makes the function call also has strict typing enabled, strict mode is used.

To demonstrate, create a PHP script reverse_2.php with strict mode enabled. Add a function (called Reverse) that reverses and returns  a string parameter value:

<?php
declare(strict_types=1);
  function Reverse(string $a) 
  {
  	return strrev($a);
  }?>

Create now another script reverse_1.php that requires the reverse_2.php script  and invokes its Reverse function with a floating point value:

<?php
require_once('reverse_2.php');
  echo Reverse(3.5);
?>

Run the reverse_1.php script. As strict mode is enabled in reverse_2.php, one might expect that a TypeError would be generated as the function that expects a string value is invoked with a floating point value. But  reverse_1.php uses weak typing and a floating point value is returned with an output of 5.3. On the contrary, if strict mode is enabled in reverse_1.php, strict typing is applied and a TypeError generated:

 Uncaught TypeError: Argument 1 passed to Reverse() must be of the type string, float given

Our next example deals with scalar type declarations for the boolean type. Create a script test.php with strict mode enabled and add a function that accepts a parameter of type bool and simply returns the unaltered parameter value. Invoke the function with a string value ‘true’:

<?php
declare(strict_types=1);
 function test(bool $param) {return $param;}
  echo test('true')
 ?>

Running the script in a browser  will give you a TypeError.

Uncaught TypeError: Argument 1 passed to test() must be of the type bool, string given

If the declare directive for strict mode is commented out or removed and the script is run again, coercive mode is applied. In this case, the string ‘true’ gets converted to the bool value true and the output will be 1.

 Now, we will go deeper about  the use of NULL values with scalar type strings.

Create a script string_null.php containing two functions, each of which takes a string argument. One of the functions has the default argument value set to NULL and the other function has no default value set. Invoke each of the functions with a string value, no value, and null value:

<?php
function string_null_1(string $str) {
  var_dump($str);
}
function string_null_2(string $str = NULL) {
  var_dump($str);
}
string_null_1('a');     
string_null_2('b');   
string_null_1();
string_null_2();      
string_null_2(null); 
string_null_1(null);   
?>

If you run the script, string_null_1 will generate an error when invoked with no argument.

Uncaught ArgumentCountError: Too few arguments to function string_null_1(), 0 passed in

Comment out the function call that generates the preceding message and run the script again. You will see that the function calls providing string type arguments output a string, as expected. Calling string_null_2, which has a default value set to NULL, with no arguments outputs NULL. Calling string_null_2 with a NULL value as argument also outputs NULL, as expected. Calling string_null_1() passing NULL generates a TypeError, instead, since the NULL argument does not get converted to a string in coercive mode.

Return Type Declarations

As we briefly mentioned above, PHP 7.0 added support for return type declarations, which are similar to parameter type declarations. The same PHP types may be used with return type declarations as with parameter type declarations. To demonstrate the use of return type declarations, create a script reverse_return.php that declares a function with a parameter of type string and string return type.

<?php
  function Reverse(string $a) : string 
  {
  	return strrev($a);
  }
try{
  echo Reverse('hello');
}catch (TypeError $e) {
	echo 'Error: '.$e->getMessage();
}?>

Invoke the function with input value “hello” and the reversed string olleh gets output. The strict mode directive that is used for scalar type declarations is also applied to return type declarations. To demonstrate this, create a script reverse_return_strict.php and add the strict mode directive at the beginning. Add then a function that takes a string parameter and returns a string value.  Instead of returning a string value, make the actual value returned be an integer:

<?php
declare(strict_types=1);
  function Reverse(string $a) : string 
  {
  	return 5;
  }
 
try{
  echo Reverse("hello");
}catch (TypeError $e) {
	echo 'Error: '.$e->getMessage();
}
 
?>

Invoke the function with a string value. Running the script will generate an error:

Error: Return value of Reverse() must be of the type string, int returned

If you remove the strict mode declaration, thus switching to coercive mode, and run the script again, you will see the value ‘5’ is printed out. In this case, the int value returned is cast to a string due to weak typing.

In coercive mode, the return value gets converted to the expected return type if needed. Modify the script and declare the function return type to be an int, but actually return a string ‘5’, instead.

<?php
  	function Reverse(string $a) : int
  {
  	return '5';
  }
try{
  echo Reverse("hello");
}catch (TypeError $e) {
	echo 'Error: '.$e->getMessage();
}
 
 
?>

Invoke the function passing a string value. In this case, the int type value 5 is returned after being converted from the string value ‘5’. For the conversion to be made in coercive mode, the returned value has to be convertible to the declared return type. As an example of that, if you declare  an int return type and return the string ‘five’, an error is generated.

 Return value of Reverse() must be of the type int, string returned

While scalar type declarations are new in PHP 7, class type declarations are not supported yet. Class types may be used in return type declarations, though, as we shall demonstrate next.

Create a script return_obj.php and declare a class Catalog including some variables typical of a magazine catalog. Instantiate the class and set values for its variables. Declare then a function with return type Catalog taking a single parameter of type Catalog. In the body of this function, just return  the input argument itself:

function getCatalog(Catalog $catalog): Catalog {
	return  $catalog;
}

Invoke the function with a Catalog class object and then output the returned value.

var_dump(getCatalog($Catalog1));

The return_obj.php script is listed:

<?php
class Catalog
{
	public $title;
	public $edition;
}
 
$Catalog1 = new Catalog();
$Catalog1->title = 'Oracle Magazine';
$Catalog1->edition = 'January-February2018';
function getCatalog(Catalog $catalog): Catalog {
	return  $catalog;
}
var_dump(getCatalog($Catalog1));
?>

Running the script will output the Catalog class object field values, as shown in Figure 18.

 object(Catalog)#1 (2) { ["title"]=> string(15) "Oracle Magazine" ["edition"]=> string(20) "January-February2018" }

As we discussed in the first article in this series, Getting started and OOP improvements, class inheritance in PHP 7.2 supports one-level return type covariance and contravariance to no type. As you remember, covariance for return type makes it possible to provide a narrower return type, while contravariance for parameter types makes it possible to provide a more generic parameter type. PHP 7.4 added full support for covariance and contravariance.

To demonstrate this, create a script return_type_inheritance.php. Add a class Catalog with a function declaring a  parameter of type string and no return type. Create another class CatalogMagazine that extends the Catalog class. The return type of the function is string and the parameter type is omitted, relying on PHP 7 parameter type widening support. Instantiate each class and invoke the function to output the value returned:

<?php
class Catalog
{
	public function printItem(string $string)
	{
    	return 'Catalog: ' . $string . PHP_EOL;
	}
}
class CatalogMagazine extends Catalog
{
	public function printItem($string) : string
	{
        return 'CatalogMagazine: ' . $string . PHP_EOL;
	}
}
$catalog = new Catalog();
$catalogMagazine = new CatalogMagazine();
echo $catalog->printItem('Catalog'); 
echo $catalogMagazine->printItem('CatalogMagazine'); 
?>

Running the script will output the return value of the functions  defined in each class object:

Catalog: Catalog CatalogMagazine: CatalogMagazine

Nullable Types

It is not uncommon for null values to be passed as function parameters or returned from a function. The null type in PHP has only one value and it is case-insensitive NULL.

PHP 7.1 has added support for nullable parameter types and nullable return types. Prefix a parameter or return type with a question mark ? to make it nullable. To demonstrate the use of the nullable type create a script hello-nullable.php. Define a function hello() specifying a nullable return type.  Nullable does not imply the function has to return null, though, so let’s return an actual value:

function hello(): ?string
{
	return 'Hello';
}

Define now another function with nullable return type that actually returns NULL:

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

Finally, define a function with a nullable parameter type:

<?php
function hello(): ?string
{
	return 'Hello';
}
echo hello();
echo "<br/>";
function hello_return_null(): ?string
{
	return null;
}
echo hello_return_null();
echo "<br/>";
function hello_null_arg(?string $name)
{
	return 'Hello';
}
echo hello_null_arg(null);
echo "<br/>";
?>

Running the script will produce the following output:

Hello

Hello

The second function call produces no output because echo converts its argument to a to string and null does not have any corresponding string value. If var_dump() is used instead of echo a NULL value should get the following output:.

Hello

NULL

Hello

Next, we shall demonstrate with an example that if the return type of a function is a class type, null  cannot be returned.  Create a script return_null.php and copy the following listing:

<?php
class Catalog
{
	public $title;
	public $edition;
}
$Catalog1 = new Catalog();
$Catalog1->title = 'Oracle Magazine';
$Catalog1->edition = 'January-February2018';
function getCatalog(?Catalog $catalog): ?Catalog {
	return  $catalog;
 
}
function getCatalog_2(?Catalog $catalog): ?Catalog {
	return  null;
}
function getCatalog_3(?Catalog $catalog): Catalog {
	return  $catalog;
}
var_dump(getCatalog(null));
var_dump(getCatalog_2(null));
var_dump(getCatalog_3(null));
?>

The script declares a class Catalog with two fields. The script creates an instance of the class and sets its field values to initialize it. All three functions in the script declare a nullable parameter of type Catalog. Two of the functions have nullable return type Catalog and one function has non-nullable return type Catalog. Each of the functions is invoked with a null argument and the call is enclosed in var_dump().

As the output indicates, if the return type is nullable and a null argument is passed, the return value is null whether the actual argument or null is returned. But if the return type is a class type such as Catalog and null is returned, an error is generated indicating that return value must be an instance of  the class type, in the example Catalog:

 NULL NULL

Uncaught TypeError: Return value of getCatalog_3() must be an instance of Catalog, null returned

Using void as Return Type for Functions

As mentioned, support for return type declarations was added in PHP 7.0, while PHP 7.1 introduced the void return type. In a function returning void, the return statement should either be empty or omitted altogether. NULL is not to be confused with void type.  NULL is a value for the null type, while void implies the absence of a value.

To  demonstrate the use of a void return type, create a script hello-void.php and copy the following listing into that file:

<?php
function hello(): void
{
	echo 'Hello';
	return;
}
echo hello();
echo "<br/>";
?>

The script declares a function called hello() with return type void. The function outputs a ‘Hello’ string and has an empty return statement. If the return type is void the function must not return a value. Run the script, “Hello” is output.

Next, we demonstrate that a function with return type void is not allowed to return a value, not even NULL. Create a script return_void.php and declare a class Catalog containing a function with return type void that returns NULL. Invoke the function passing an instance of Catalog as an argument. The return_void.php script is listed here:

<?php
class Catalog
{
	public $title;
	public $edition;
}
$Catalog1 = new Catalog();
$Catalog1->title = 'Oracle Magazine';
$Catalog1->edition = 'January-February2018';
function getCatalog(Catalog $catalog): void {
	return NULL;
}
var_dump(getCatalog($Catalog1));
?>

Running the script will output an error message.

A void function must not return a value (did you mean "return;" instead of "return null;"?)

Modify the script slightly so that the function has an empty return statement, which is valid for the void return type. Invoke the function with an instance of Catalog as an arg.

function getCatalog(Catalog $catalog): void {
	return;
}
var_dump(getCatalog($Catalog1));

In this case, a value of NULL is returned since we used an empty return statement, as var_dump makes clear.

New iterable type

The iterable is a new compound type in PHP 7.1. Pseudo types are keywords used for types or  values that an argument may have. The iterable type may be used as parameter type or return type. It accepts an array or an object that implements the Traversable interface, both of which can be iterated over using foreach.

First, we shall demonstrate how to use iterable with an array. Create a script iter.php in which declare a function with parameter type iterable.  iterable type parameters may declare a default value that is NULL or an array. Within the function iterate over the iterable using foreach() and output its values. Create an array and invoke the function using the array as its argument. The iter.php is listed.

<?php
$catalog = ['Oracle Magazine','Java Magazine','PHP Magazine'];
function iterator(iterable $iter)
{
	foreach ($iter as $val) {
    	echo $val;
    	echo "<br/>";
	}
}
iterator($catalog);
?>

Run the script to output the array values:

Oracle Magazine

Java Magazine

PHP Magazine

An iterable also accepts a class object that implements the Traversable interface. To demonstrate this, create a script  iter_traversable.php and declare a class that implements the IteratorAggregate interface, which further extends the Traversable interface. Declare three class properties.

public $journal1 = "Oracle Magazine";
	public $journal2 = "Java Magazine";
	public $journal3 = "PHP Magazine"

Add a class constructor that implements the interface function abstract public Traversable getIterator ( void ). Let’s call this class catalogData. In the same script, also declare a function with parameter type iterable. Within this function, use foreach to iterate over the iterable parameter and output its values:

function iterator(iterable $iter)
{
foreach($iter as $key => $value) {
    var_dump($key, $value);
	echo "\n";
}
}

Finally, invoke the function with an instance of the class catalogData as an argument:

$obj = new catalogData;
iterator($obj);
The iter_traversable.php script is listed:
<?php
class catalogData implements IteratorAggregate {
public $journal1 = "Oracle Magazine";
	public $journal2 = "Java Magazine";
	public $journal3 = "PHP Magazine";
	public function __construct() {
     	
	}
 
	public function getIterator() {
    	return new ArrayIterator($this);
	}
}
$obj = new catalogData;
function iterator(iterable $iter)
{
foreach($iter as $key => $value) {
    var_dump($key, $value);
	echo "\n";
}
}
iterator($obj);
?>

Now, run the script to output the key/value pairs in the iterable:

string(8) "journal1" string(15) "Oracle Magazine" string(8) "journal2" string(13) "Java Magazine" string(8) "journal3" string(12) "PHP Magazine"

The iterable type supports  full  contravariance (for parameter type) and covariance (for return type). Covariance and Contravariance are discussed in detail in an earlier article PHP 7 — Getting Started and OOP Improvements. Full contravariance implies that parameter types array and Traversable may be widened to iterable. Full covariance implies that return type iterable may be narrowed to array or Traversable.  Next, we shall demonstrate covariance and contravariance with iterable using an example. Create a script iter_extend_ret_type.php and declare a class containing an iterator function with an array parameter and nullable iterable return type. Within the class, iterate over the array argument using foreach and output its values. Declare a class that extends the first one and overrides the iterator function with a function that has an iterable parameter and nullable array return type. Create an instance of the extending class and invoke its iterator function. The script is listed here:

<?php
class ClassA{
function iterator(array $arr)  : ?iterable
{
	foreach ($arr as $val) {
    	echo  $val;
    	echo "<br/>";
    	return null;
	}
}
}
class ClassB extends ClassA{
function iterator(iterable $iter)  : ?array
{
	foreach ($iter as $val) {
    	echo  $val;
    	echo "<br/>";
 
   	
	}
return null;
}
}
$catalog = ['Oracle Magazine','Java Magazine','PHP Magazine'];
$classB=new ClassB();
$classB->iterator($catalog);
?>

Run the script, which will output:

Oracle Magazine

Java Magazine

PHP Magazine

Functions with return type iterable may also be used as generators. As an example, create a script iterable_gen.php and create and invoke a generator function with return type iterable.  Yield some values in the generator function and return an array. Subsequently, output the values yielded by the generator function.  Also output the generator function return value using getReturn() function to obtain the return value:

<?php
function generator(): iterable {
	yield 'a';
	yield 'b';
	yield 'c';
	return ['a','b','c'];
}
$gen = generator();
foreach ($gen as $value) {
	echo "$value\n";
}
var_dump($gen->getReturn());
?>

Run the script to output the values yielded by the generator function.

a b c array(3) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> string(1) "c" }

Typed Properties

Version 7 has made several improvements to PHP's type system. PHP 7.4 adds support for typed properties, using which you can declare types for class properties. Class properties do nor require you to explicitly declare getter/setter methods for them. Some of their salient characteristics are:

  • Types may be declared on static properties as well.
  • References to typed properties are supported.
  • The typed properties are affected by the strict_types directive just as the parameter types and return types are.
  • Types may be used with var notation.
  • Default values for typed properties may be declared.
  • The type of multiple properties may be declared in a single declaration.
  • Implicit int to float cast is made.
  • The type for a nullable property may be declared.
  • A class property of type callable or void cannot be declared.

As an example, consider the following class called  Catalog that illustrates several of the preceding characteristics:

<?php
declare(strict_types=1);
class Catalog {
    public int $catalogid,$journalid;
    public ?string $journal=null;
    public static string $edition="January-February 2020";
    var bool $flag;
    public float $f=1;
public function __construct(int $catalogid,int $journalid,string $journal,bool $flag)
    {
        $this->catalogid = $catalogid;
        $this->journalid = $journalid;
        $this->journal = $journal;
        $this->flag = $flag;
    }
}
$c = new Catalog(123,345,"PHP Magazine",true);
echo "Catalogid: ".$c->catalogid."\n";
echo "Journalid: ".$c->journalid."\n";
echo "Journal: ".$c->journal."\n";
echo "Flag: ".$c->flag."\n";
echo "Edition: ".Catalog::$edition."\n";
?>

Run now the script, whose output is shown in Figure 2.

Figure 2. Output illustrating use of Typed properties

To demonstrate the use of properties along with strict-type mode, consider the following script that sets strict_types to 1 and declares a class property called $catalogid of type int. Then try to assign the $catalogid property a string value:

<?php
declare(strict_types=1);
class Catalog {
    public int $catalogid;
}
$c = new Catalog();
$c->catalogid = "123"; 
?>

When the script is run, the following TypeError is thrown:

Uncaught TypeError: Typed property Catalog::$catalogid must be int, string used

If you define, no TypeError is thrown. The important distinction to note here is that the assigned value must satisfy the strict_types mode at the write-location of the property. In other terms, the strict_types mode at the property declaration location is not relevant. If strict_types mode is set to 1 at the write-location, the assigned value must be of the declared type. To demonstrate the use of different modes, we shall use two different files a.php and b.php with strict_types mode set to 1 and 0 respectively. The scripts also demonstrate the following characteristics of references when used with typed properties:

  • An uninitialized-nullable property may be accessed by reference
  • An uninitialized non-nullable property cannot be accessed by reference
  • An array may be accessed by reference.

In a.php, set strict_types mode to 1 and declare a class A containing a typed class property called $a of  type int. Make sure the a.php script also declare an array, a nullable property and a non-nullable property:

<?php
declare(strict_types=1);
class A {
    public int $a;
    public array $array = [3, 1, 2];
    public ?int $x;
    public int $y; 
}

In a second file, b.php, include a.php and set strict_types mode to 0.  Instantiate class A, then use var_dump to output this instance’s $a property value, which is shown in the following listing after the comment //. Then, access the array type property through its reference, and finally, sort and output it.  

Now access the int type variable $a through its reference and assign it string value “1”.  Output the  variable $a value using var_dump and you will see no TypeError is thrown. This is because th value stored in $a is cast to its type, int.

Also notice you can access the uninitialized nullable property $x through its reference. In such a case, it will be transparently initialized to NULL. On the contrary, if you try to access the  uninitialized non-nullable property $y,  you will get a TypeError.

<?php 
declare(strict_types=0);
include 'a.php';
$a = new A;
$a->a = "1";
var_dump($a->a); // int(1)
sort($a->array);
var_dump($a->array);// array(3) { [0]=> int(1) [1]=> //int(2) [2]=> int(3) }
$x =& $a->x; // Initialized to null
$y =& $a->y; //TypeError Uncaught Error: Cannot access //uninitialized non-nullable property A::$y by reference

Next, we shall discuss another important characteristic of typed properties that has to do with type invariance.

  • The type of a non-private property cannot be changed in a derived class. A non-private property type cannot be added or removed either.
  • The type of a private property can be changed in a derived class. Private property types may be added or removed.

To demonstrate type invariance, declare a class called A that contains a private variable $a of type int, a public variable $b of type string, a nullable public variable $c of type int, and a public variable $d of type string. Also declare a void type property called $v to verify the void type cannot be used with properties.

<?php class A {
    private int $a;
    public string $b;
    public ?int $c;
    public string $d;
    public void $v;//Property A::$v cannot have type void
}

Declare now a class B that extends class A and changes the types of its class properties. Change the type of the private property $a from int to string and make it public, which is supported. If you try to change the type of the public property $b from string to int; the type of the public property $c nullable int to int; or to omit the type of the public property $d, you will get errors. The error messages output are shown below as comments.

class B extends A {
    public string $a;  
public  int $b;   // Type of B::$b must be string (as in class A)
public int $c;    // Type of B::$c must be ?int (as in class A)
public  $d;    // Type of B::$d must be string (as in class A)
}

Summary

In this third article in our series on PHP 7.x, we discussed new features in PHP’s type system.

PHP 7.0 added support for scalar type declarations for string,int,float, and bool types. PHP also 7.0 added support for return type declarations to be used in the return statement.

PHP 7.1 added support for the null type, which has only one value, NULL,  and for a return type called void, representing the absence of any value. PHP 7.1 also added support for a new type called iterable. While the void type may be used only as return type, iterable can be used as a parameter or a return type.

Typed properties were added in PHP 7.4, which enable to explicitly declare types for class properties.

In the next article in the series we shall explore new features for PHP functions.

About the Author

Deepak Vohra is a Sun Certified Java Programmer and Sun Certified Web Component Developer. Deepak has published Java and Java EE related technical articles in  WebLogic Developer's Journal, XML Journal, ONJava, java.net, IBM developerWorks, Java Developer’s Journal, Oracle Magazine, and devx. Deepak has published five books on Docker and is a Docker Mentor. Deepak has also published several articles on PHP and a book Ruby on Rails for PHP and Java Developers. 

 

PHP 7.x brings several improvements and new features that touch all aspects of the language, including better support for object oriented programming, extensions to classes and interfaces, improvements to the type system, error handling, and more. In this series of articles, we discuss new features across the various PHP 7.x versions.

 

Rate this Article

Adoption
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Community comments

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

BT