PHP 8.0 🐘
A new version, a new era
#php80
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
#php80
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
#php80
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();
}
#php80
::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"
);
}
#php80
::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"
);
}
#php80
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
#php80
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
#php80
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...
#php80
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
#php80
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"
#php80
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
);
#php80
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*';
#php80
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*
#php80
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
#php80
Which projects will be benefited from these improvements?
-
phpstan/phpstan
-
vimeo/psalm
-
roave/better-reflection
-
jetbrains/phpstorm-stubs
#php80
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
#php80
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)
#php80
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)
#php80
contributors
Christoph M. Becker (Microsoft)
#php80
contributors
Matteo Beccati (PDO/Postgres)
#php80
new contributors
#php80
new contributors
#php80
known contributors
Benjamin Eberlei (Doctrine)
#php80
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!
- 6,173