New language features in PHP 7

In this article, we present new language features of PHP 7. The article focuses on the features of the core language. For our examples, we use PHP CLI. PHP tutorial is a concise tutorial of PHP language on ZetCode. In the PHP filesystem functions article, we cover numerous PHP functions related to filesystem.

Between years 2014 and 2015, a new major PHP version was developed, which was numbered PHP 7. After some debate among developers, version 6 was skipped in order to avoid confusion with PHP Unicode experiment, which was referred to as PHP 6 in the press. First, we are going to show how to install PHP 7 from the sources.

Installing PHP

We download PHP from the http://php.net/downloads.php page. We have downloaded php-7.0.4.tar.bz2 file.

$ bunzip2 php-7.0.4.tar.bz2
$ tar -xf php-7.0.4.tar
$ cd php-7.0.4/

We decompress the file and move into the build directory.

$ sudo apt-get install libreadline-dev
$ sudo apt-get install libicu-dev

We need to install readline development library for PHP interactive shell and the development files for international components for Unicode.

$ ./configure --with-readline --enable-intl

We run the configure script with readline and internationalization support enabled.

$ make
$ sudo make install

We build and install PHP. It is possible that we need to install some additional packages. For instance, the author had to install libxml2-dev library.

$ sudo cp php.ini-development /usr/local/lib/php.ini
$ sudo ln -s /usr/local/lib/php.ini /etc

We copy the php.ini file and make a link to it in the /etc directory.

$ php -a
Interactive shell

php > echo PHP_VERSION;
7.0.4

We run PHP in interactive mode and get the version of PHP.

Integer division

The division operator (/) returns a float point value. The divint() function performs integer division. The first parameter is the dividend, the second parameter is the divisor.

integer_division.php
<?php

    echo 7/3, "\n";
    echo intdiv(7, 3), "\n";
    
?>

The example prints values of a floating point and integer divisions.

$ php integer_division.php 
2.3333333333333
2

This is the output of the integer_division.php program.

The spaceship operator

The spaceship operator (<=>), also known as combined comparison operator, can be used to make chained comparisons more concise. In behaviour, it is similar to to strcmp() or version_compare().

$a <=> $b

This expression evaluates to -1 if $a is smaller than $b, 0 if $a equals $b, and 1 if $a is greater than $b.

($a < $b) ? -1 : (($a > $b) ? 1 : 0)

The expression is rewritten without the spaceship operator.

spaceship_operator.php
<?php

    echo 1 <=> 1; // 0
    echo 1 <=> 2; // -1
    echo 2 <=> 1; // 1
    
    echo "\n";
    
    echo "a" <=> "a"; // 0
    echo "a" <=> "b"; // -1
    echo "b" <=> "a"; // 1
    
    echo "\n";
    
    echo [] <=> []; // 0
    echo [1, 2, 3] <=> [1, 2, 3]; // 0
    echo [1, 2, 3] <=> []; // 1
    echo [1, 2, 3] <=> [1, 2, 1]; // 1
    echo [1, 2, 3] <=> [1, 2, 4]; // -1  
    
    echo "\n";
    
?>

The example uses the spaceship operator on numbers, strings, and arrays.

echo 1 <=> 1; // 0

Numbers are compared by their size.

echo "a" <=> "b"; // -1

Strings are compared lexically; e.i. by their alphabetical order.

echo [1, 2, 3] <=> [1, 2, 1]; // 1

In case of arrays, corresponding array elements are compared with one another.

$ php spaceship_operator.php 
0-11
0-11
0011-1

This is the output of the spaceship_operator.php program.

The null coalescing operator

The null coalescing operator (??) is a syntactic sugar for the common case of having to use a ternary operator in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.

php > $mycolour = $colour ?? "blue";
php > echo $mycolour;
blue

Since the $colour variable is not defined, the ?? operator returns its second operand.

php > $mycolour = isset($colour) ? $colour : "blue";
php > echo $mycolour;
blue

In previous versions of PHP, we would need to used the isset() function.

Scalar type declarations

Scalar type declarations are type hints for function parameters. There are two types of declarations: coercive (default) and strict. The following types for parameters can now be enforced string, int, float, and bool.

The types are enforced with the declare() function.

scalar_type_declarations.php
<?php

// declare(strict_types=1);

function add(int $a, int $b) {

    return $a + $b;
}

$r = add(4, 6);
echo "$r\n";

$r = add("4", "6");
echo "$r\n";
    
?>

In the coercive mode, the example prints 10 for both cases. If we enable the strict mode by removing the comment, we receive the following error message: Fatal error: Uncaught TypeError: Argument 1 passed to add() must be of the type integer, string given.

Return type declarations

The return type declarations specify the type of the value that is returned from a function. The same types are available for return type declarations as are available for argument type declarations.

return_type_declaration.php
<?php

declare(strict_types=1);

function add( $a, $b) : int {

    return $a + $b;
}

$r = add(4, 6.0);
echo "$r\n";
    
?>

The function has an int return type declared. We pass a floating point value to the function which results in a float return value. The program ends with this error: Fatal error: Uncaught TypeError: Return value of add() must be of the type integer, float returned.

Unicode codepoint escape syntax

The syntax takes a codepoint in hexadecimal form and outputs that codepoint in UTF-8 to a double-quoted string or a heredoc. Any valid codepoint is accepted, with leading 0's being optional. (Code points are numerical values that represent a character.)

unicode_codepoints.php
<?php

    echo "\u{13C7}", "\n";
    echo "\u{1307}", "\n";
        
?>

The example prints a Cherokee letter que and ethiopic syllable jwa.

$ php unicode_codepoints.php 
Ꮗ
ጇ

This is the output of the unicode_codepoints.php program.

IntlChar class

The new IntlChar class provides access to a number of utility methods that can be used to access information about Unicode characters.

intlcharex.php
<?php

    echo IntlChar::charName('Ꮗ'), "\n";
    echo IntlChar::charName('ጇ'), "\n";
    
?>

The example uses the IntlChar::charName() function to retrieve the name of two Unicode characters.

$ php intlcharex.php 
CHEROKEE LETTER QUE
ETHIOPIC SYLLABLE JWA

This is the output of the intlcharex.php script.

Anonymous classes

Anonymous classes are classes that do not have a name. They are useful when simple, one-off objects need to be created.

anonymous_class.php
<?php

    $c = new class {
    
        public function say() {
            echo "This is an anonymous class\n";
        }        
    };
    
    $c->say();
    
?>

We assign an object to the $c variable; the object is created from a class that does not have a name.

$ php anonymous_class.php 
This is an anonymous class

This is the output of the anonymous_class.php script.

Expectations

Expectations are backwards compatible enhancement to the older assert() function. It is possible to have zero-cost assertions in production code and to provide the ability to throw custom exceptions when the assertion fails.

The zend.assertions configuration setting can have one of three values:

The assert.exception sets whether an exception is thrown when an assertion fails. This is switched off by default to remain compatible with the old assert() function.

expectations.php
<?php

abstract class Grades {

    const A = 0;
    const B = 1;
    const C = 2;
    const D = 3;
    const E = 4;
    const FX = 5;
}

const G = 6;

//$grade = Grades::A;
$grade = G;

switch ($grade) {

    case Grades::A:
        echo "Outstanding\n";
    break;
 
    case Grades::B:
        echo "Superior\n";
    break;
 
    case Grades::C:
        echo "Good\n";
    break;
 
    case Grades::D:
        echo "Satisfactory\n";
    break;
    
    case Grades::E:
        echo "Low pass\n";
    break;
    
    case Grades::FX:
        echo "Failure\n";
    break;    
 
    default:
        assert (false, "Unrecognized grade passed through switch: {$grade}");
}

echo "code continues\n";

?>

We have a system of college grades.

//$grade = Grades::A;
$grade = G;

A wrong value is given to the $grade variable.

default:
    assert (false, "Unrecognized grade passed through switch: {$grade}");

We place an assertion at a location we assume will not be reached.

$ php expectations.php 

Warning: assert(): Unrecognized grade passed through switch: 6 
failed in /home/janbodnar/prog/php7/expectations.php on line 44
code continues

The script contains the above warning, but it continues.

ini_set('assert.exception', 1);

It is possible to enable exceptions by setting the assert.exception configuration. This time the script fails with Fatal error: Uncaught AssertionError: Unrecognized suit passed through switch: 6.

The levels paramter of dirname()

The dirname() function returns the parent's directory path. The new levels parameter tells how many parent directories to go up.

dirlevels.php
<?php

    echo dirname('/usr/local/bin').PHP_EOL;
    echo dirname('/usr/local/bin', 1).PHP_EOL;
    echo dirname('/usr/local/bin', 2).PHP_EOL;
    echo dirname('/usr/local/bin', 3).PHP_EOL;
    
?>

In the script, we utilize the new optional parameter of the dirname() function.

$ php dirlevels.php 
/usr/local
/usr/local
/usr
/

This is the output of the dirlevels.php script.

Array constants in define()

Since PHP 5.6 it is possible to define constant arrays with the const keyword. In PHP 7, a constant array can be created with define() too.

array_constants.php
<?php

    const POSSIBLE_GRADES = ['A', 'B', 'C', 'D', 'E', 'FX'];
    define('ALLOWED_IMAGE_EXTENSIONS', ['jpg', 'jpeg', 'gif', 'png']);
       
    print_r(POSSIBLE_GRADES);
    print_r(ALLOWED_IMAGE_EXTENSIONS);
    
?>

The code example creates two array constants; one with the const keyword and one with the define() function.

$ php array_constants.php 
Array
(
    [0] => A
    [1] => B
    [2] => C
    [3] => D
    [4] => E
    [5] => FX
)
Array
(
    [0] => jpg
    [1] => jpeg
    [2] => gif
    [3] => png
)

This is the output of the array_constants.php script.

New use syntax

The use keyword allows to group multiple declarations in one statement.

myfile.php
<?php

    namespace ZetCode;
    
    class Night {
    
        function say() {
        
            echo "This is Night class\n";
        }
    }
    
    class Day {
    
        function say() {
        
            echo "This is Day class\n";
        }
    }    
    
    function f1() {
    
        echo "This is f1()\n";
    }
    
    function f2() {
    
        echo "This is f2()\n";
    }    
            
?>

In the myfile.php we have two classes and two functions defined in a ZetCode namespace.

usegroup.php
<?php

    include 'myfile.php';

    use ZetCode\{Day, Night};
    use function ZetCode\{f1, f2};
    
    $d = new Day();
    $d->say();
    
    $n = new Night();
    $n->say();    
    
    f1();
    f2();
            
?>

In the usegroup.php we use the classes and functions from ZetCode namespace.

use ZetCode\{Day, Night};
use function ZetCode\{f1, f2};

The new syntax of use allows to import several declarations in one statement.

use ZetCode\Day;
use ZetCode\Night;
use function ZetCode\f1;
use function ZetCode\f2;

This is the syntax for previous versions of PHP.

$ php usegroup.php 
This is Day class
This is Night class
This is f1()
This is f2()

This is the output.

Closure call() method

PHP 5.4 added the ability to bind closures to an object's scope. This process is simplyfied in PHP 7 with the call() method.

closure_call.php
<?php

    class Greeting {
    
        private $word = "Hello";
    }
    
    $f = function($whom) { 
    
        echo "$this->word $whom\n";
    };
    
    $obj = new Greeting();

    $f->call($obj, 'Tom');
    $f->call($obj, 'Jane'); 
    
?>

With a closure, we access a private variable of a class.

$ php closure_call.php 
Hello Tom
Hello Jane

This is the output of the closure_call.php script.

Generator return expressions

A return statement can be used within a generator to enable for a final expression to be returned. This value can be retrieved with the getReturn() method, which may only be used once the generator has finishing yielding values.

generator_return.php
<?php

    srand();
    
    function random_numbers($k) {
    
        for ($i=0; $i<$k; $i++) {
        
            $r = rand(1, 10);
    
            yield $r;            
        }
        
        return -1;
    }

    $rns = random_numbers(10);
    
    foreach ($rns as $r) {
    
        echo "$r\n";
    }
    
    echo $rns->getReturn() . PHP_EOL;
            
?>

Our generator returns random values. At the end of the iteration, the generator returns -1.

$ php generator_return.php 
7
5
6
7
1
1
9
6
5
8
-1

This is a sample output of the generator_return.php script.

Generator delegation via yield from

Generator delegation allows to yield values from another generator, Traversable object, or array by using the yield from keyword. Generator delegation promotes cleaner code and reusability.

generator_delegation.php
<?php

    function f1() {
    
        yield from f2();
    
        yield "f1() 1";
        yield "f1() 2";
        
        yield from [3, 4];
        
        yield "f1() 3";
        yield "f1() end";
    }

    function f2() {
    
        yield "f2() 1";
        yield "f2() 2";
        yield "f2() 3";
        yield "f2() end";
    }

    $f = f1();
    
    foreach ($f as $val) {
        echo "$val\n";
    }
            
?>

In the example, we yield values another generator and an array.

function f1() {

    yield from f2();
...    

Values are yield from f2() generator.

yield from [3, 4];

It is also possible to yield values from an array.

$ php generator_delegation.php 
f2() 1
f2() 2
f2() 3
f2() end
f1() 1
f1() 2
3
4
f1() 3
f1() end

This is the output of the generator_delegation.php script.

Uniform variable syntax

New uniform variable syntax allows combinations of operators that were previously disallowed; old operations can be achieved in terser code. The new syntax always follows a left-to-right evaluation order. The uniform variable syntax breaks backward compatibility in some expressions where old evaluation is used.

uniform_variable_syntax.php
<?php

    function e() {
        
        echo "This is e() \n";
    };
    
    function f() {
        
        echo "This is f() \n";
        return e;
    };    

    function g() {
    
        echo "This is g()\n";
        return f;
    };
    
    g();
    echo "***********\n";
    g()();
    echo "***********\n";
    g()()();
            
?>

The example uses g()() and g()()(), which were not possible in earlier PHP versions.

g();

Here the g() function is executed.

g()();

This statement executes the g() function and the return of the g() function.

g()()();

The g() function is executed, the return of the g() function is executed, and the return of the return is executed.

$ php uniform_variable_syntax.php 
This is g()
***********
This is g()
This is f() 
***********
This is g()
This is f() 
This is e() 

This is the output of the uniform_variable_syntax.php script.

The following is another example of a new syntax introduced with the uniform variable syntax.

uniform_variable_syntax2.php
<?php

    class A {
    
        function f() {
             
            echo "f() of A\n";
        }    
    }

    class B {
    
        function g() {
            echo "g() of B\n";
            
            return new A();
        }
    }
    
    $b = new B();
    $b->g()::f();
            
?>

The $b->g()::f() executes an f() method of a class A returned by method g().

$ php uniform_variable_syntax2.php 
g() of B
f() of A

This is the output of the uniform_variable_syntax2.php script.

In the third example, we have an immediately-invoked function expression, which is a term from the JavaScript language.

uniform_variable_syntax3.php
<?php

    $v = (function() {
 
        return 11 - 5;
    })();
 
    echo "$v\n";
            
?>

Immediately-invoked function expression is an anonymous function that is executed right after it is created.

$ php uniform_variable_syntax3.php 
6

This is the output of the uniform_variable_syntax3.php script.

Changes in exceptions and errors

In PHP 7, an exception is thrown when a fatal and recoverable error (E_ERROR and E_RECOVERABLE_ERROR) occurs, rather than halting script execution. These fatal and recoverable errors are now instances of a new and separate exception class: Error. Some errors throw a more concrete error: TypeError, ParseError, ArithmeticError, and AssertionError.

Other types of errors such as warnings and notices remain unchanged in PHP 7. Only fatal and recoverable errors throw exceptions.

A new Throwable interface is introduced to unite the two exception branches: Exception and Error; they both implement the Throwable.

null_error.php
<?php

    class A {
    
        public function say() { 
        
            echo "This is class A\n"; 
        }
    }
    
    try {
    
        $a = new A();
        $a->say();
        
        $a = null;
        $a->say();
    
    } catch (Error $e) {
    
        echo "Error occured" . PHP_EOL;
        echo $e->getMessage() . PHP_EOL ;
        echo "File: " . $e->getFile() . PHP_EOL;
        echo "Line: " . $e->getLine(). PHP_EOL;
    }
    
    echo "Script continues\n";
            
?>

In the example, we call a method on null object. We catch the exception and handle it; the script then continues.

$ php null_error.php 
This is class A
Error occured
Call to a member function say() on null
File: /home/janbodnar/prog/php7/null_error.php
Line: 17

This is the output of the null_error.php script.

DivisionByZeroError is an ArithmeticError which is thrown from intdiv() and the modulo (%) operator when the denominator is zero. The (\) division operator issues still a Warning: Division by zero warning.

arithmetic_error.php
<?php

    $a = 4;
    $b = 0;
    
    try {
    
        $c = intdiv($a , $b);
    
    } catch (DivisionByZeroError $e) {
    
        echo "Error occured\n";
        echo $e->getMessage() . PHP_EOL ;
        echo "File: " . $e->getFile() . PHP_EOL;
        echo "Line: " . $e->getLine(). PHP_EOL;        
    }

    echo "$c \n";
    
    echo "Script continues\n";
            
?>

In the example, we catch and report a DivisionByZeroError in a intdiv() function.

$ php arithmetic_error.php 
Error occured
Division by zero
File: /home/janbodnar/prog/php7/arithmetic_error.php
Line: 8
 
Script continues

This is the output of the arithmetic_error.php script.

Sources

The following sources were used to create this article:

In this article, we have presented new features of PHP 7 language. You may want to look at PHP tutorial now.