New features
coming in PHP 7.4


by Enrico Zimuel
Principal Software Engineer @ Elastic


University of Turin, Dec. 2, 2019

About me

PHP 7.4

Release date

  • PHP 7.4 Alpha1, Jun 13 2019
  • PHP 7.4 Beta1, Jul 25 2019
  • PHP 7.4 RC1, Sep 5 2019
  • PHP 7.4 RC2, Sep 19 2019
  • PHP 7.4 RC3, Oct 03 2019
  • PHP 7.4 RC4, Oct 17 2019
  • PHP 7.4 RC5, Oct 31 2019
  • PHP 7.4 RC6, Nov 14 2019
  • PHP 7.4 GA, Nov 28 2019

New features (some)

  • Typed properties
  • Preloading
  • FFI (Foreign Function Interface)
  • Null Coalescing Assignment Operator
  • Arrow Functions (Short Closures)
  • Spread Operator in Array Expression

Typed properties

Param and return types

PHP 7 introduced param and return types


declare(strict_types=1);

class User
{
    protected $name;

    public function setName(string $name): void
    {
        $this->name = $name;
    }
    public function getName(): string
    {
        return $this->name;
    }
    //...
}

Typed properties in 7.4


class User
{
    public string $name;
}

$user = new User();
$user->name = 42;
// Fatal error: Typed property User::$name must be string

NULL by default


class User
{
    public $name;
}

$user = new User();
var_dump($user->name);
// NULL

Typed property: NULL


class User
{
    public string $name;
}

$user = new User();
echo $user->name;
// Fatal error: Typed property User::$name must not
// be accessed before initialization

Initialization


class User
{
    public ?string $name = null;
}

$user = new User();
var_dump($user->name);
// NULL

The Billion Dollar Mistake

Tony Hoare introduced Null references in ALGOL back in 1965 "simply because it was so easy to implement".

The idea was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler.

References


class User
{
    public string $name = "foo";
}

$user = new User();
$name =& $user->name;
$name = 42;
// Fatal error: Cannot assign int to reference held
// by property User::$name of type string	

Typed property example


class Example {
    public int $scalarType;
    protected ClassName $classType;
    private ?ClassName $nullableClassType;

    public static iterable $staticProp;

    public string $str = "foo";
    public ?string $nullableStr = null;

    public float $x, $y;
}

It supports all types, except void and callable

Why not callable?


Class Test {
    public callable $cb;
	
    public function __construct() {
        // $this->cb is callable here
        $this->cb = [$this, 'foo'];
    }
	
    private function foo() {}
}

$obj = new Test;
// $obj->cb is NOT callable here!!!
($obj->cb)();

strict_types off


declare(strict_types=0);

class Test {
    public int $val;
}

$test = new Test;
$test->val = "42";
var_dump($test->val); // int(42)

$test->val = "42.7";
var_dump($test->val); // int(42)

$test->val = "test";
// Fatal error: Uncaught TypeError:
// Typed property Test::$val must be int

Invariant

Property types (public and protected) are invariant


class A {
    private bool $a;
    private string $b;
    protected int $c;
    public ?int $d;
}

class B extends A {
    public string $a;  // ok, A::$a is private
    private bool $b;   // ok, A::$b is private
    protected ?int $c; // ILLEGAL
    public int $d;     // ILLEGAL
}

Preloading

Opcache

Preloading

"On server startup – before any application code is run – we may load a certain set of PHP files into memory – and make their contents “permanently available” to all subsequent requests that will be served by that server. All the functions and classes defined in these files will be available to requests out of the box, exactly like internal entities."
Dmitry Stogov

Opcache.preload

  • Preloading is controlled by a specific php.ini directive: opcache.preload
  • This directive specifies a PHP script to be compiled and executed at server start-up
  • Each file to cache, should be added in this script using opcache_compile_file()

Preloading in composer


$composer = require '/path/to/vendor/composer/autoload_classmap.php';
$preload  = '/tmp/preload.php';

$content = "<?php\n";
foreach (array_unique($composer) as $class => $file) {
    $content .= sprintf("opcache_compile_file('%s');\n", $file);
}
file_put_contents($preload, $content);

opcache.preload="/tmp/preload.php"

Benchmark

I found this benchmark of Ben Morel:

  • Medium PHP project: 90 libraries, autoload of 380 classes in composer
  • Preloading only "hot" classes: using opcache_get_status() (878 files)
  • Prealod of all the classes (14,541 files)

Results

Strategy Req/sec
No prealoding 596 rq/s
Preload hot classes 695 rq/s
Preload everything 675 rq/s

+13% when preloading everything
+16% when preloading "hot" classes

Preloading cons

  • Restart the web server to update opcache.preload
  • Not compatible with host running multiple applications, or multiple versions of applications

Foreign Function Interface

FFI

  • Enables the execution of C code directly from PHP
  • Provides a FFI class to manage the bridge between C and PHP
  • A C function can be executed as a PHP method of FFI class

Example 1


$ffi = FFI::cdef(
    "int printf(const char *format, ...);",
    "libc.so.6"
);
// esegue la funzione C printf()
$ffi->printf("Hello %s!\n", "world");

Example 2


$ffi = FFI::cdef("
    typedef unsigned int time_t;
    typedef unsigned int suseconds_t;
    struct timeval {
        time_t      tv_sec;
        suseconds_t tv_usec;
    };
    struct timezone {
        int tz_minuteswest;
        int tz_dsttime;
    };
    int gettimeofday(struct timeval *tv, struct timezone *tz);
", "libc.so.6");

$tv = $ffi->new("struct timeval");
$tz = $ffi->new("struct timezone");
$ffi->gettimeofday(FFI::addr($tv), FFI::addr($tz));

Example 2 (cont)


var_dump($tv);
var_dump($tz);
/*
object(FFI\CData:struct timeval)#2 (2) {
  ["tv_sec"]=>
  int(1568643992)
  ["tv_usec"]=>
  int(0)
}
object(FFI\CData:struct timezone)#3 (2) {
  ["tz_minuteswest"]=>
  int(0)
  ["tz_dsttime"]=>
  int(0)
}
*/

PHP TensorFlow

Dmitry Stogov has developed an experimental binding of TensorFlow library in PHP using FFI:
dstogov/php-tensorflow

Null Coalescing

Null Coalescing


$this->request->data['comments']['user_id'] =
    $this->request->data['comments']['user_id'] ?? 'value';

// with null coalescing assignment
$this->request->data['comments']['user_id'] ??= 'value';

Arrow Functions

Arrow Functions


$y = 1;
$fn1 = function ($x) use ($y) {
    return $x + $y;
};
echo $fn1(3); // 4

// with arrow functions
$fn2 = fn($x) => $x + $y;
echo $fn2(3); // 4

Syntax


fn(parameter_list) => expr

NOTE: fn is a reserved word in PHP 7.4

Spread operator in array

Spread operator


$x = ['c', 'd'];
$y = ['a', 'b', ...$x];
var_dump($y); // ['a', 'b', 'c', 'd']

Spread operator

It also works on Traversable objects


$arr1 = [...new ArrayIterator(['a', 'b', 'c'])];
var_dump($arr1); // ['a', 'b', 'c']

$arr2 = new ArrayIterator(['a', 'b']);
$arr3 = new ArrayIterator(['c']);
$arr4 = [...$arr2, ...$arr3 ];
var_dump($arr4); // ['a', 'b', 'c']

Performance of PHP 7.4

Benchmark PHP 7.x

  • I compared the execution time of PHP7.4RC1 with PHP 7.1, 7.2 and 7.3
  • I used Zend\bench.php script as benchmark
  • I took the total execution time
  • Full experiment report here

Results

Results

PHP ver. Exec. time (in sec)
7.1.32 0.455
7.2.22 0.437
7.3.9 0.373
7.4RC1 0.334

PHP 7.4 is the fastest!

  • 7.4RC1 is 26% faster than 7.1.32;
  • 7.4RC1 is 22% faster than 7.2.22;
  • 7.4RC1 is 10% faster than 7.3.9;

PHP 8.0

The future

  • PHP 8.0 will be released on Dec 2020
  • Big new feature: JIT compiler

PHP to bytecode

Source: Dmitry's talk at PHP Russia 2019

Bytecode to native code

Source: Dmitry's talk at PHP Russia 2019

References

Thanks!

Follow me: @ezimuel



Creative Commons License
This work is licensed under a
Creative Commons Attribution-ShareAlike 3.0 Unported License.
I used reveal.js to make this presentation.