La nuova major release di PHP
Github:Â https://github.com/matiux
Slack GRUSP:Â matiux
Email:Â m.galacci@gmail.com
Linkedin: Matteo Galacci
Enrico Zimuel
Â
Â
Â
*Disabled by default. Use declare(strict_types=1); in each file
More info here
<?php
// Coercive mode
function sumOfInts(int ...$ints)
{
return array_sum($ints);
}
var_dump(sumOfInts(2, '3', 4.1));
//int(9)
<?php
// strict mode
declare(strict_types=1);
function sumOfInts(int ...$ints)
{
return array_sum($ints);
}
var_dump(sumOfInts(2, '3', 4.1));
Fatal errors: Uncaught TypeError: Argument 2 passed to sumOfInts() must be of the type integer, string given
Â
Next TypeError: Argument 3 passed to sumOfInts() must be of the type integer, float given, called in
<?php
function arraysSum(array ...$arrays): array
{
return array_map(function(array $array): int {
return array_sum($array);
}, $arrays);
}
print_r(arraysSum([1,2,3], [4,5,6], [7,8,9]));
Array
(
[0] => 6
[1] => 15
[2] => 24
)
<?php
interface Logger {
public function log(string $msg);
}
class Application {
private $logger;
public function getLogger(): Logger {
return $this->logger;
}
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
}
$app = new Application();
$app->setLogger(new class implements Logger {
public function log(string $msg) {
echo $msg;
}
});
var_dump($app->getLogger());
<?php
// Fetches the value of $_GET['user'] and returns 'nobody'
// if it does not exist.
$username = $_GET['user'] ?? 'nobody';
// This is equivalent to:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
// Coalescing can be chained: this will return the first
// defined value out of $_GET['user'], $_POST['user'], and
// 'nobody'.
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';
<?php
function testReturn(?string $string): ?string
{
return $string;
}
echo testReturn(null); // null
echo testReturn('foo'); // foo
<?php
function testReturn(string $string): void
{
echo $string;
}
echo testReturn('foo'); // foo
<?php
class ConstDemo
{
const PUBLIC_CONST_A = 1;
public const PUBLIC_CONST_B = 2;
protected const PROTECTED_CONST = 3;
private const PRIVATE_CONST = 4;
}
<?php
try {
// some code
} catch (FirstException | SecondException $e) {
// handle first and second exceptions
}
<?php
$data = [
[1, 'Tom'],
[2, 'Fred'],
];
// list() style
list($id1, $name1) = $data[0];
// [] style
[$id1, $name1] = $data[0];
// list() style
foreach ($data as list($id, $name)) {
// logic here with $id and $name
}
// [] style
foreach ($data as [$id, $name]) {
// logic here with $id and $name
}
<?php
$data = [
["id" => 1, "name" => 'Tom'],
["id" => 2, "name" => 'Fred'],
];
// list() style
list("id" => $id1, "name" => $name1) = $data[0];
// [] style
["id" => $id1, "name" => $name1] = $data[0];
// list() style
foreach ($data as list("id" => $id, "name" => $name)) {
// logic here with $id and $name
}
// [] style
foreach ($data as ["id" => $id, "name" => $name]) {
// logic here with $id and $name
}
You can now specify keys in list(), or its new shorthand [] syntax. This enables destructuring of arrays with non-integer or non-sequential keys.
<?php
function test(object $obj) : object
{
return new SplQueue();
}
test(new StdClass());
<?php
abstract class A
{
abstract function test(string $s);
}
abstract class B extends A
{
// overridden - still maintaining contravariance
// for parameters and covariance for return
abstract function test($s) : int;
}
<?php
class User {
public int $id;
public string $name;
}
array_map(function (User $user) {
return $user->id;
}, $users)
Also called "short closures".
array_map(fn (User $user) => $user->id, $users)
array_map(function (User $user) {
return $user->id;
}, $users)
Also called "short closures".
array_map(fn (User $user) => $user->id, $users)
class ParentType {}
class ChildType extends ParentType {}
class A
{
public function covariantReturnTypes(): ParentType
{
/* … */
}
}
class B extends A
{
public function covariantReturnTypes(): ChildType
{
/* … */
}
}
Covariance
class ParentType {}
class ChildType extends ParentType {}
class A
{
public function contraVariantArguments(ChildType $type)
{
/* … */
}
}
class B extends A
{
public function contraVariantArguments(ParentType $type)
{
/* … */
}
}
Contravariance
A foreign function interface (FFI) is a mechanism by which a program written in one programming language can call routines or make use of services written in another.
Â
Currently, accessing FFI data structures is significantly (about 2 times) slower than accessing native PHP arrays and objects. Therefore, it makes no sense to use the FFI extension for speed; however, it may make sense to use it to reduce memory consumption.
It was already possible to extend the PHP core. Phalcon framework is an example
Another lower-level feature is preloading. It's is an amazing addition to PHP's core, which can result in some significant performance improvements.
In short: if you're using a framework, its files have to be loaded and linked on every request. Preloading allows the server to load PHP files in memory on startup, and have them permanently available to all subsequent requests.
The performance gain comes of course with a cost: if the source of preloaded files are changed, the server has to be restarted.
$data['date'] = $data['date'] ?? new DateTime();
$data['date'] ??= new DateTime();
PHP 8, the new major PHP version, is expected to be released on December 3, 2020. That means there will be no PHP 7.5 version.
A Just-In-Time (JIT) compiler is a feature of the run-time interpreter, that instead of interpreting bytecode every time a method is invoked, will compile the bytecode into machine code, and then invoke this object code instead
Ciclo di vita
<?php
for ($i=0; $i<100; $i++) {
echo $i;
}
Example
Opcode
L0 (2): ASSIGN CV0($i) int(0)
L1 (2): JMP L4
L2 (3): ECHO CV0($i)
L3 (2): PRE_INC CV0($i)
L4 (2): T1 = IS_SMALLER CV0($i) int(100)
L5 (2): JMPNZ T1 L2
L6 (5): RETURN int(1)
Linguaggio semplice simile ad assembly
Opcache
Preloading in PHP 7.4
All’avvio del server, prima dell'esecuzione di ogni script si può caricare in memoria dei file PHP rendendo così il loro contenuto già pronto per le richieste successive che verranno fatto al server.
Classi e funzioni definite nei file in Preloading saranno disponibili nativamente, proprio come le entità interne.
Il Preloading è controllato dalla direttiva opcache.preload che specifica uno script PHP da compilare ed eseguire all'avvio del server. Questo script verrà usato per precaricare file aggiuntivi, includendoli oppure tramite la funzione opcache_compile_file().Â
JIT traduce le parti calde del codice intermedio in codice macchina
JIT
Previously we said that JIT can decide which portions to translate into machine code and which to leave in Opcode. How do you decide? Through a CRTO configuration, a number of 4 decimal places where each number represents a specific configuration.
CRTO = 1235
Â
https://php.watch/articles/jit-in-depth
[C]PU-specific Optimization Flags
0: Disable CPU-specific optimization.
1: Enable use of AVX, if the CPU supports it.
[T]rigger
0: Compile all functions on script load.
1: Compile all functions on first execution.
2: Profile first request and compile the hottest functions afterwards.
3: Profile on the fly and compile hot functions.
4: Currently unused.
5: Use tracing JIT. Profile on the fly and compile traces for hot code segments.
[O]ptimization Level
0: No JIT.
1: Minimal JIT (call standard VM handlers).
2: Inline VM handlers.
3: Use type inference.
4: Use call graph.
5: Optimize whole script.
[R]egister Allocation
0: Don't perform register allocation.
1: Perform block-local register allocation.
2: Perform global register allocation.
CRTO
https://php.watch/articles/jit-in-depth#jit-flags
JIT TRIGGER
* The option 4 under Triggers (T=4) did not make it to the final version of JIT implementation. It was trigger JIT on functions declared with @jit DocBlock comment attribute. This is now unused.
CRTO default value: opcache.jit=tracing
In addition to these values you can specify the numerical value of CRTO, for example:
opcache.jit = 1254, equivalent to tracing.
The difference between function and tracing is that the function JIT will only try to optimise code within the scope of a single function, while the tracing JIT can look at the whole stack trace to identify and optimise hot code.
https://stitcher.io/blog/php-8-jit-setup
<?php
class Number {
/**
* @var int|float $number
*/
private $number;
/**
* @param int|float $number
*/
public function setNumber($number): void {
$this->number = $number;
}
/**
* @return int|float
*/
public function getNumber() {
return $this->number;
}
}
<?php
class Number {
private int|float $number;
public function setNumber(int|float $number): void {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
<?php
class Service
{
private Dep1 $dep1;
private array $params;
public function __construct(
Dep1 $dep1,
array $params
) {
$this->dep1 = $dep1;
$this->params = $params;
}
public function params(): array
{
return $this->params;
}
}
<?php
class Service
{
public function __construct(
private Dep1 $dep1,
private array $params
) {}
public function params(): array
{
return $this->params;
}
}
<?php
class A {
private function __construct(
private int $a
){}
public function create(int $a): self {
return new self($a);
}
}
class B extends A {
}
$b = new B();
$result = $b->create(2);
var_dump($result);
// object(A) {
// ["a"]=> 2
// }
Useful in fluent interfaces
<?php
class A {
private function __construct(
private int $a
) {}
public function create($a): static {
return new static($a);
}
}
class B extends A {
}
$b = new B();
$result = $b->create(2);
var_dump($result);
// object(B) {
// ["a"]=> 2
// }
<?php
// PHP 5
// Foo\Bar::class
$object = new stdClass;
var_dump($object::class); // "stdClass"
$object = null;
var_dump($object::class); // TypeError
Con PHP 8, $object::class === get_class($object)
Â
Se $object non è un oggetto viene generata un'eccezione TypeError
<?php
class Foo
{
public function __toString(): string
{
return 'foo';
}
}
function bar(string | Stringable $stringable): string
{
return (string) $stringable;
}
var_dump(bar(new Foo())); //string(3) "foo"
var_dump(bar('abc')); //string(3) "abc"
Stringable interface is automatically added to classes that implement the __toString() method.
Â
Ogni volta che una classe implementa __toString(), implementa automaticamente l'interfaccia Stringable e non quindi  necessario implementarla manualmente.
First of all, custom attributes are simple classes, annotated themselves with the #[Attribute] attribute
<?php
declare(strict_types=1);
namespace PHP80\Attribute\ExampleA;
use Attribute;
#[Attribute]
class ListensTo
{
/** @var class-string */
public string $event;
public function __construct(string $event)
{
$this->event = $event;
}
}
PHP's existing Doctrine-esque is widely used, but Attributes in PHP 8 uses the #[ and ] brace syntax. This was debated and changed from the initial <<Attr>> implementation to @@Attr to the final #[Attr] syntax.
Most uses of attributes/annotations in PHP today revolve around registration.
Attributes allow to add structured, machine-readable metadata information on declarations in code: Â Classes, methods, functions, parameters, properties and class constants can be the target of an attribute.
The metadata defined by attributes can then be inspected at runtime using the Reflection APIs.
Attributes and Annotations provide the same functionality. The word "Annotations" is already being used widely in PHP libraries and frameworks, so the name Attributes help to minimize the confusion with Annotations.
(e.g. Doctrine annotation)
Be careful not to use annotations as another tool that takes development away from the Domain.
Attributes could therefore be thought of as a configuration language embedded directly into code.
https://php.watch/articles/php-attributes
https://www.php.net/manual/en/language.attributes.overview.php
https://stitcher.io/blog/attributes-in-php-8
https://kinsta.com/it/blog/php-8/#attributes
https://platform.sh/blog/2020/php-8-0-feature-focus-attributes/
They shouldn't — and can't — be used for, for example, argument input validation.
Keep in mind the goal of attributes: they are meant to add meta data to classes and methods, nothing more.
#[ArrayShape], #[ExpectedValues], #[NoReturn], #[Pure], #[Deprecated], #[Immutable]
https://blog.jetbrains.com/phpstorm/2020/10/phpstorm-2020-3-eap-4/
https://psalm.dev/articles/php-8-attributes
https://psalm.dev/articles/php-8-support
Tip: as well as supporting all the new features outlined below, Psalm 4 can also tell you if your PHP 7 code might break in PHP 8 – just run it with --php-version=8.0
PhpStorm Attributes:
Psalm Attributes:
array_slice (
array $array,
int $offset,
int|null $length = null,
bool $preserve_keys = false
): array
<?php
$input = array("a", "b", "c", "d", "e");
// array(1) { [3]=> string(1) "d" }
array_slice($input, -2, 1, true);
<?php
$input = ['a', 'b', 'c', 'd', 'e'];
// array(1) { [3]=> string(1) "d" }
array_slice(
array: $input,
offset: -2,
length: 1,
preserve_keys: true
);
<?php
// PHP < 8
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
<?php
// PHP >= 8
$result = $repository?->getUser(5)?->name;
Arrays starting with a negative index
<?php
// PHP <8.0
$a = array_fill(-5, 4, true);
var_dump($a);
// array(4) {
// [-5]=>
// bool(true)
// [0]=>
// bool(true)
// [1]=>
// bool(true)
// [2]=>
// bool(true)
// }
<?php
// PHP >=8.0
$a = array_fill(-5, 4, true);
var_dump($a);
// array(4) {
// [-5]=>
// bool(true)
// [-4]=>
// bool(true)
// [-3]=>
// bool(true)
// [-2]=>
// bool(true)
// }
throw can now be used as an expression. That allows usages like:
<?php
// Arrow function - PHP 7.4
$fn = fn() => throw new Exception('Exception in arrow function');
// Null coalescing operator - PHP 7.0 other than ??= PHP 7.4
$user = $session->user ?? throw new Exception('Must have user');
<?php
$foo = 1;
$a = 1;
$b = 2;
$res = 0;
switch ($foo) {
case $a:
$res = $a;
break;
default:
$res = $b;
break;
}
<?php
$foo = 1;
$a = 'a';
$b = 'b';
$res = match ($foo) {
$a => $a,
default => $b
};
<?php
$result = match ($x) {
foo() => ...,
$this->bar() => ..., // bar() isn't called if foo() === $x
$this->baz => beep(), // beep() isn't called unless $x === $this->baz
$a, $b => throw new Exception()
// etc.
};
Allow trailing comma in parameter list
<?php
// PHP >= 7.3
function functionWithLongSignature(
string $parameter1,
string $parameter2
) {
}
functionWithLongSignature(
'a',
'b',
);
<?php
// PHP >= 8.0
function functionWithLongSignature(
string $parameter1,
string $parameter2, // <-- This comma is now allowed.
) {
}
functionWithLongSignature(
parameter1: 'a',
parameter2: 'b',
);
See the full list here: Standard library
<?php
// PHP < 8.0
if (strpos('string with lots of words', 'words') !== false) {}
// PHP >= 8.0 - case-sensitive
$string = 'Welcome to the real world';
if (str_contains(haystack: $string, needle: 'real')) {} // True
if (str_starts_with(haystack: $string, needle: 'Welcome')) {} // True
if (str_ends_with(haystack: $string, needle: 'world')) {} // True
Â
PHP RFC official page
PHP RFC Watch
Brent Roose, New in PHP 8
Eli White, What’s in PHP Eight?, php[architect], Vol.19 - Issue 5, May 2020
Arkadiusz Kondas, Compiling PHP 8 from source with JIT support
NÃckolas Da Silva, Understanding PHP 8's JIT
Benoit Jacquemont, PHP 8 et Just In Time Compilation, PHPForum 2019
Nikita Popov, PHP 7 Virtual Machine
Anthony Ferrara, A PHP Compiler, aka The FFI Rabbit Hole
James Titcumb, Climbing the Abstract Syntax Tree, PHP UK 2018