PHP 8.0 🐘

A new version, a new era

PHP 8.0 🐘

PHP Release Schedule (2020)

 

 

June 25th ~ July 23rd - Alphas (1, 2, 3)

August 4th ~ Feature Freeze (F.F ❄️)

August 6th ~ September 17th - Betas (1, 2, 3, 4)

October 1st ~ November 12th - Release Candidates (1, 2, 3, 4, 5)

26th November - General Availability (G.A. 🔥)

#php80

PHP 8.0 🐘

PHP SUPPORTED VERSIONS FOR 2021

 

PHP 7.2 - Won't be supported

PHP 7.3 - Security Fixes only

PHP 7.4 - Active Support

PHP 8.0 - Active Support

#php80

  • Gabriel Caruso 🇧🇷 ~> 🇳🇱

 

  • Backend Developer @ SurveyMonkey 

 

  • OSS Contributor 👨🏻‍💻

#php80

  • Performance Improvements
  • Some of the new Features
  • A couple of changes
  • Breaking Changes (BCs)
  • Internals Changes
  • Behind the scenes of PHP 8.0 development

Agenda

#php80

Performance improvements 📈

JIT (Just-in-time)

- Don't expect performance improvements like PHP 5.6 ~> PHP 7.0

 

- Heavy Processes will benefit from it, not simple and fast APIs

JIT talks

- https://youtu.be/g3RPYtwP1jk: Benoit Jacquemont (in French)

- https://youtu.be/JZAs1YTcRHM: Nickolas da Silva (in Portuguese)

- https://youtu.be/5H77TnoLLcU: Nikita Popov (in English)

- https://youtu.be/7UOWus-5yxg: Dmitry Stogov (in Russian)

 

Graviton2 Arm64 Amazon instances

- Since PHP 7.4, AWS has been helping PHP by providing performance improvements patches

 

- Blog post with all the changes and benchmarks

 

FEATURES 🔥

`match` Control Structure

`mixed` type

`?->` Operator

Constructor Property Promotion

`Stringable` Interface

Attributes

New `string_*` functions

Weak Maps

`static` return type

Validation for abstract trait methods

New `fdiv` function

Trailing commas in more places

DOM Living Standard APIs

New `preg_last_error_msg` function

Cryptographic Message Syntax (CMS) (RFC 5652) support

Array sorting functions are now stable

Union Types

Named Arguments

`PhpToken` class

New `get_resource_id` function

PHP 8.0 HAS A LOT OF NEW FUNCIONALITY

`match` Control Structure

Attributes

Union Types

Named Arguments

we are going to FOCUS ON

Union types

<?php declare(strict_types=1);

class Number
{
    /** @var int|float $number */
    private $number;
 
    /** @param int|float $number */
    public function setNumber($number)
    {
        $this->number = $number;
    }
 
    /** @return int|float */
    public function getNumber()
    {
        return $this->number;
    }
}

Union types

<?php declare(strict_types=1);

class Number
{
    /** @var int|float $number */
    private $number;
 
    /** @param int|float $number */
    public function setNumber($number)
    {
        $this->number = $number;
    }
 
    /** @return int|float */
    public function getNumber()
    {
        return $this->number;
    }
}

$number = new Number();

$number->setNumber('string');

echo $number->getNumber(); // 'string'

Union types

<?php declare(strict_types=1);

class Number
{
    /** @var int|float $number */
    private $number;
 
    /** @param int|float $number */
    public function setNumber($number)
    {
    	if (!is_int($number) && !is_float($number)) {
            throw new \InvalidArgumentException(
            	sprintf('Argument $number should be either an integer or float, %s given', gettype($number))
            );
        }
        
        $this->number = $number;
    }
 
    /** @return int|float */
    public function getNumber()
    {
        return $this->number;
    }
}

$number = new Number();

$number->setNumber('string'); // InvalidArgumentException: Argument $number should be either an integer or float, string given

Union types

<?php declare(strict_types=1);

class Number
{
    private int|float $number;
 
    public function setNumber(int|float $number): void
    {
        $this->number = $number;
    }
 
    public function getNumber(): int|float
    {
        return $this->number;
    }
}

Union types

<?php declare(strict_types=1);

class Number
{
    private int|float $number;
 
    public function setNumber(int|float $number): void
    {
        $this->number = $number;
    }
 
    public function getNumber(): int|float
    {
        return $this->number;
    }
}

$number = new Number();

$number->setNumber('string'); // Number::setNumber(): Argument #1 ($number) must be of type int|float, string given
<?php declare(strict_types=1);

class Collection
{
    private array $items;
 
    public function setItem(string $index, array|bool|callable|int|float|null|object|string $value): void
    {
        $this->items[$index] = $value;
    }
 
    public function getItem(string $index): array|bool|callable|int|float|null|object|string
    {
        return $this->items[$index];
    }
}

Union types

mixed type

<?php declare(strict_types=1);

class Collection
{
    private array $items;
 
    public function setItem(string $index, mixed $value): void
    {
        $this->items[$index] = $value;
    }
 
    public function getItem(string $index): mixed
    {
        return $this->items[$index];
    }
}

match

<?php declare(strict_types=1);

$value = '2';

switch ($value) {
    case 0:
        $result = 'Foo';
        break;
    case 1:
        $result = 'Bar';
        break;
    case 2:
        $result = 'Baz';
        break;
}
 
echo $result; // 'Baz'

match

<?php declare(strict_types=1);

$value = '3';

switch ($value) {
    case 0:
        $result = 'Foo';
        break;
    case 1:
        $result = 'Bar';
        break;
    case 2:
        $result = 'Baz';
        break;
}
 
echo $result; // Warning: Undefined variable $result

match

<?php declare(strict_types=1);

$value = '3';

switch ($value) {
    case 0:
        $result = 'Foo';
        break;
    case 1:
        $result = 'Bar';
        break;
    case 2:
        $result = 'Baz';
        break;
    default:
        throw new \InvalidArgumentException(sprintf('No `case` for $value %s', $value));
}
 
echo $result; // InvalidArgumentException: No `case` for $value 3

match

<?php declare(strict_types=1);

$value = 2;

echo match ($value) {
    0 => 'Foo',
    1 => 'Bar',
    2 => 'Baz',
}; // Baz

match

<?php declare(strict_types=1);

$value = 3;

echo match ($value) {
    0 => 'Foo',
    1 => 'Bar',
    2 => 'Baz',
}; // UnhandledMatchError: Unhandled match value of type int

Nullsafe operator

<?php declare(strict_types=1);

$neighborhood = $address->getNeighborhood(); // returns either NULL or a Neighborhood object

$neighborhoodName = $neighborhood ? $neighborhood->getName() : null;

if ($neighborhoodName !== null) {
    // do something with $neighborhoodName
}

Nullsafe operator

<?php declare(strict_types=1);

$neighborhoodName = $address->getNeighborhood()?->getName();

if ($neighborhoodName !== null) {
    // do something with $neighborhoodName
}

named arguments

<?php declare(strict_types=1);

$items = [
    1, 2, 'foo',
];

$integers = array_filter($items, fn (mixed $item): bool => is_int($item));

var_dump($integers); // [1, 2]

named arguments

<?php declare(strict_types=1);

var_dump(
    array_fill(2, 3, 'PHP')
);

named arguments

<?php declare(strict_types=1);

var_dump(
    array_fill(2, 3, 'PHP')
); // [2 => 'PHP', 3 => 'PHP', 4 => 'PHP']

named arguments

<?php declare(strict_types=1);

var_dump(
    array_fill(start_index: 2, count: 3, value: 'PHP')
); // [2 => 'PHP', 3 => 'PHP', 4 => 'PHP']

named arguments

<?php declare(strict_types=1);

class Person
{
    public function __construct(string $name, int $age)
    {
        // ...
    }
}

new Person(name: 'Gabriel Caruso', age: 23);

named arguments

<?php declare(strict_types=1);

interface Person
{
    public function setName(string $name);
}
 
class MyPerson implements Person
{
    public function setName(string $fullName) {}
}
 
$person = (new MyPerson)->setName('Gabriel Caruso'); // fullName or name?

named arguments

<?php declare(strict_types=1);

interface Person
{
    public function setName(string $name);
}
 
class MyPerson implements Person
{
    public function setName(string $fullName) {}
}
 
$person = (new MyPerson)->setName(fullName: 'Gabriel Caruso'); // Named Arguments allows renaming

named arguments

<?php declare(strict_types=1);

class User
{
    public function __construct(
        bool $admin,
        bool $active,
        ?array $rules = [],
    ) {
        // Construct the object...
    }
}

$user = new User(true, false, null);

named arguments

<?php declare(strict_types=1);

class User
{
    public function __construct(
        bool $admin,
        bool $active,
        ?array $rules = [],
    ) {
        // Construct the object...
    }
}

$user = new User(
    admin: true,
    active: false,
    rules: null
);

named arguments

<?php declare(strict_types=1);

function randomOrder(int $arg1, string $arg2, float $arg3) {}

randomOrder(arg2: 'PHP', arg3: 8.0, arg1: 1);

function optionals(string $arg1 = 'default', int $arg2 = 1) {}

optionals(arg2: 3);

function skipping(int $arg1, string $arg2, float $arg3) {}

skipping(1, 'PHP', arg3: 8.0); // This is allowed

skipping(1, arg2: 'PHP', 8.0); // Fatal error: Cannot use positional argument after named argument

attributes

<?php declare(strict_types=1);

/** @Entity */
final class User
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="integer")
     */
    private int $id;

    /** @ORM\Column() */
    private string $name;
}

Annotations

They are used to document your code, and only that.

attributes

Attributes change the behavior of your code. You can have your Entities' attributes, you can configure how properties will be (de)serialized, and even declare API routes with it

attributes

<?php declare(strict_types=1);

#[Entity]
final class User
{
    #[ORM\Id()]
    #[ORM\Column(type="integer")]
    private int $id;

    #[ORM\Column()]
    private string $name;
}

advantages of having native attributes in php

- A lot of libraries, projects and framework have their own implementation. This will be centralized now

- Caching, optimization and all the good stuff that PHP offers out of the box

- Annotations will be used for annotation only

ONE very IMPORTANT POINT ABOUT ATTRIBUTES in php 8.0 ⚠️

- Don't convert Annotations what were used as Attributes before checking if your library, or framework, supports it

- Take a look into the RFC, under the "Reflection" section to check how to support Attributes

CHANGES 🔩

throw is now an expression

<?php declare(strict_types=1);

$callable = fn() => throw new Exception(); // this was not possible before PHP 8.0

$value = $nullableValue ?? throw new InvalidArgumentException(); // `throw` is now considered an expression

catch's variable is no longer REQUIRED

<?php declare(strict_types=1);

try {
    $this->login($user);
} catch (PermissionException) { // $exception variable is no longer required
    throw AuthenticationException::failedLogin();
}

::class magic constant now supports objects

<?php declare(strict_types=1);

namespace Foo\Bar {
    class Baz {}
    
    $baz = new \Foo\Bar\Baz();

    var_dump(
        $baz::class, // "Foo\Bar\Baz"
    );
}

::class magic constant now supports objects

<?php declare(strict_types=1);

namespace Foo\Bar {
    class Baz {}
    
    $baz = new \Foo\Bar\Baz();

    var_dump(
        $baz::class, // "Foo\Bar\Baz"
        get_class($baz) // "Foo\Bar\Baz"
    );
}

PDO's default Error Mode is now PDO::ERRMODE_EXCEPTION

- Moved from `PDO::ERRMODE_SILENT` to `PDO::ERRMODE_EXCEPTION`

- This change will help new developers to understand their mistakes when dealing with PDO, avoiding common `call to fetch() on non-object` errors

- Common `SQL syntax` problems will now be exposed by PDO

deprecation of optional argument followed by a required one

<?php declare(strict_types=1);

function test(string $param = '', $param2) {
    // do something with $param and $param2
}

// Deprecated: Required parameter $param2 follows optional parameter $param

Backward Incompatible Changes 🚨

Removal of 7.x deprecated funcionalities 

- Use of case-insensitive constants / handling of true/false/null

- Declaration of case-insensitive constants

- PHP 4 constructors like

- Calling non-static methods statically

- Undocumented MBString function aliases

- Cast `(unset)`

- Check the linked PR for the full list...

magic method signatures checks

<?php declare(strict_types=1);

class Foo
{
    public function __toString(): bool
    {
        return true;
    }
}

// Declaration of Foo::__toString(): bool must be compatible with Stringable::__toString(): string

Stricter type checks for arithmetic/bitwise operators

<?php declare(strict_types=1);

$result = [] % [42]; // It will throw a "TypeError: Unsupported operand types"

$array = [2, 3];
$array++; // It will throw a "TypeError: Cannot increment array"

$object = new stdClass();
$object++; // It will throw a "TypeError: Cannot increment stdClass"

SANER STRING AND NUMBER COMPARISONS

<?php declare(strict_types=1);

var_dump(
    0 == "0", // continues to be true
    0 == "0.0", // continues to be true
    0 == "foo", // was true, now is false
    0 == "", // was true, now is false
    42 == "   42", // continues to be true
    42 == "42foo", // was true, now is false
);

Internals Changes 👀

REFLECTION API IMPROVEMENTS

<?php declare(strict_types=1);

$reflectionFunction = new ReflectionFunction('array_filter');

foreach ($reflectionFunction->getParameters() as $parameter) {
    echo sprintf('Parameter $%s, type %s', $parameter->getName(), $parameter->getType() ?? '*NO TYPE DEFINED*') . PHP_EOL; 
}

echo 'array_filter return type: ' . $reflectionFunction->getReturnType() ?? '*NO TYPE DEFINED*';

REFLECTION API IMPROVEMENTS

<?php declare(strict_types=1);

$reflectionFunction = new ReflectionFunction('array_filter');

foreach ($reflectionFunction->getParameters() as $parameter) {
    echo sprintf('Parameter $%s, type %s', $parameter->getName(), $parameter->getType() ?? '*NO TYPE DEFINED*') . PHP_EOL; 
}

echo 'array_filter return type: ' . $reflectionFunction->getReturnType() ?? '*NO TYPE DEFINED*';

// Before PHP 8.0

// Parameter $arg, type *NO TYPE DEFINED*
// Parameter $callback, type *NO TYPE DEFINED*
// Parameter $use_keys, type *NO TYPE DEFINED*
// array_filter return type: *NO TYPE DEFINED*

REFLECTION API IMPROVEMENTS

<?php declare(strict_types=1);

$reflectionFunction = new ReflectionFunction('array_filter');

foreach ($reflectionFunction->getParameters() as $parameter) {
    echo sprintf('Parameter $%s, type %s', $parameter->getName(), $parameter->getType() ?? '*NO TYPE DEFINED*') . PHP_EOL; 
}

echo 'array_filter return type: ' . $reflectionFunction->getReturnType() ?? '*NO TYPE DEFINED*';

// After PHP 8.0

// Parameter $array, type array
// Parameter $callback, type ?callable
// Parameter $use_keys, type int
// array_filter return type: array

Which projects will be benefited from these improvements?

  • phpstan/phpstan

  • vimeo/psalm

  • roave/better-reflection

  • jetbrains/phpstorm-stubs

PHP INI ENTRIES CHANGES

- Default `error_reporting` level is `E_ALL`. Previously it was excluding `E_NOTICE` and `E_DEPRECATED`

- `zend.exception_string_param_max_len` is configurable, improving `Throwable::getTraceAsString()`

- `assert.exception` default value is `true`, throwing an Exception instead of just raising a Warning

Behind the scenes of PHP 8.0 development 🎪

Who is behind PHP 8.0?

release managers

Gabriel Caruso (his first time)

Sara Golemon (PHP 7.2 manager)

release managers

- Responsible for taking care and coordinate all the Releases from Alpha 1 until G.A

- Responsible for releasing a new patch version every four weeks after G.A.

- (Not official, but) Release Managers are the one spreading the word of the new version in conferences, meetups and the WWW

contributors

Dmitry Stogov (JIT)

Nikita Popov (JetBrains)

contributors

Christoph M. Becker (Microsoft)

contributors

Matteo Beccati  (PDO/Postgres)

Tyson Andre (Phan)

new contributors

new contributors

known contributors

Benjamin Eberlei (Doctrine)

Nicolas Grekas (Symfony)

this talk was just the tip of the iceberg 🗻

you can check all the references for this talk, with blog posts, youtube videos and more.

Thank you ♥️

Please, leave a feedback for this presentation 👉🏻

Questions? 🤔

PHP 8.0: A new version, a new era

By Gabriel Caruso (Caruso)

PHP 8.0: A new version, a new era

PHP 8 is coming, and you might be wondering: what are the new features? Have they fixed that weird bug? Is my application getting any faster? Can I support both PHP 7 and 8 versions in my library or framework? In this talk, we are going to spend some time looking at everything that has changed in PHP 8, both internally and externally, either new features and changes of behavior (Break Compatibility, you know them). As one of the Release Managers, Gabriel Caruso is going to tell you all about it, giving you the resources to get the latest major version of PHP, that is scheduled for November 2020, up and running!

  • 5,748