What's new in PHP 8.0?
如果无法正常显示,请先停止浏览器的去广告插件。
1. What's new in PHP 8.0?
Nikita Popov @ PhpConChina 2020
2.
3. PHP 8.0
● Planned release date: November 26th
● Large number of new features
● Backwards-compatibility breaks
4. Just-In-Time (JIT) Compiler
●
●
Compiles PHP code to x86 machine code
Performance improvement depends on type of
code
5. Just-In-Time (JIT) Compiler
●
●
Compiles PHP code to x86 machine code
Performance improvement depends on type of
code
– WordPress: ~5% improvement
– PHP-Parser: 2x faster
6. Just-In-Time (JIT) Compiler
● Compiles PHP code to x86 machine code
● Part of opcache:
– opcache.jit=on
– opcache.jit_buffer_size=128M
7. Attributes
<?php
/** @Entity */
class User {
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
private $id;
}
8. Attributes
<?php
use Doctrine\ORM\Attributes as ORM;
#[ORM\Entity]
class User {
#[ORM\Id]
#[ORM\Column("integer")]
#[ORM\GeneratedValue]
private $id;
}
9. Attributes
<?php
use Doctrine\ORM\Attributes as ORM;
Class name
#[ORM\Entity]
class User {
Constructor arguments
#[ORM\Id]
#[ORM\Column("integer")]
#[ORM\GeneratedValue]
private $id;
}
10. Attributes
<?php
namespace Doctrine\ORM\Attributes;
use Attribute;
#[Attribute]
class Column {
public function __construct(string $type) { … }
}
11. Attributes
<?php
namespace Doctrine\ORM\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_PROPERTY)]
class Column {
public function __construct(string $type) { … }
}
12. Attributes
<?php
$rc = new ReflectionProperty(User::class, "id");
foreach ($rc->getAttributes() as $attr) {
var_dump($attr->getName());
// => "Doctrine\ORM\Attributes\Column"
var_dump($attr->getArguments());
// => ["integer"]
}
var_dump($attr->newInstance());
// object(Doctrine\ORM\Attributes\Column)
13. Attributes
<?php
$rc = new ReflectionClass(User::class);
foreach ($rc->getAttributes() as $attr) {
var_dump($attr->getName());
// => "Doctrine\ORM\Attributes\Column"
var_dump($attr->getArguments());
// => ["integer"]
}
var_dump($attr->newInstance());
// object(Doctrine\ORM\Attributes\Column)
Attribute validation happens HERE.
14. Constructor Promotion
<?php
class Point {
public float $x;
public float $y;
public float $z;
}
public function __construct(
float $x = 0.0,
float $y = 0.0,
float $z = 0.0,
) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
15. Constructor Promotion
<?php
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
16. Constructor Promotion
<?php
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
Trailing comma in parameters lists
now allowed
17. Named Arguments
<?php
// Using positional arguments:
array_fill(0, 100, 50);
18. Named Arguments
<?php
// Using positional arguments:
array_fill(0, 100, 50);
// Using named arguments:
array_fill(start_index: 0, count: 100, value: 50);
19. Named Arguments
<?php
// Using positional arguments:
array_fill(0, 100, 50);
// Using named arguments:
array_fill(start_index: 0, count: 100, value: 50);
// Order does not matter!
array_fill(value: 50, count: 100, start_index: 0);
20. Named Arguments
<?php
// Using positional arguments:
htmlspecialchars(
$string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
21. Named Arguments
<?php
// Using positional arguments:
htmlspecialchars(
$string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
// Using named arguments:
htmlspecialchars($string, double_encode: false);
22. Named Arguments
<?php
// Using positional arguments:
htmlspecialchars(
$string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
// Using named arguments:
htmlspecialchars($string, double_encode: false);
Can combine named & positional.
But: Positional must come first.
Can skip optional arguments.
23. Named Arguments
<?php
use Symfony\Component\Routing\Annotation\Route;
class SomeController {
/**
* @Route("/path", name="action")
*/
public function someAction() {
// ...
}
}
24. Named Arguments
<?php
use Symfony\Component\Routing\Annotation\Route;
class SomeController {
#[Route("/path", name: "action")]
public function someAction() {
// ...
}
}
25. Named Arguments
<?php
class Point {
public function __construct(
public float $x,
public float $y,
public float $z,
) {}
}
new Point(x: 2.0, y: 3.1, z: 4.2);
26. Named Arguments
<?php
class Point {
public function __construct(
public float $x,
public float $y,
public float $z,
) {}
}
$array = ["x" => 2.0, "y" => 3.1, "z" => 4.2];
new Point(...$array);
27. Named Arguments
<?php
function acceptsAnything(...$args) {
var_dump($args);
}
acceptsAnything(1, 2, x: 3, y: 4);
// $args = [1, 2, "x" => 3, "y" => 4]
28. Named Arguments
<?php
class A {
public function method($name_a) {}
}
class B extends A {
public function method($name_b) {}
}
// Error: Unknown named parameter $name_a
(new B)->method(name_a: 42);
Names not the same
29. Union Types
<?php
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;
}
}
30. Union Types
<?php
class Number {
private int|float $number;
public function setNumber(int|float $number) {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
31. Union Types
<?php
function strpos(
string $haystack, string $needle, int $offset = 0
): int|false {}
32. Union Types
<?php
function strpos(
string $haystack, string $needle, int $offset = 0
): int|false {}
Very common in standard library
33. Union Types
<?php
function strpos(
string $haystack, string $needle, int $offset = 0
): int|false {}
function array_key_first(array $arg): int|string|null {}
?Type is a shorthand for Type|null now
34. Union Types
● Tricky interaction with "weak types"
● Type must be part of union, or...
●
Scalars are coerced to int, float, string, bool, in
order of preference
35. Union Types
<?php declare(strict_types=0);
function test(int|float|bool $arg) {
var_dump($arg);
}
test(45);
test(45.8);
test("45");
test("45.8");
test("");
test("X");
test([]);
//
//
//
//
//
//
//
int(45)
float(45.8)
int(45)
float(45.8)
bool(false)
bool(true)
TypeError
36. Union Types
<?php declare(strict_types=1);
function test(int|float|bool $arg) {
var_dump($arg);
}
test(45);
test(45.8);
test("45");
test("45.8");
test("");
test("X");
test([]);
//
//
//
//
//
//
//
int(45)
float(45.8)
TypeError
TypeError
TypeError
TypeError
TypeError
37. Mixed Type
●
Distinguishes between:
– Type is missing because I didn't add one yet
– This function really does accept any value
38. Mixed Type
<?php
function var_dump(mixed $value, mixed ...$value): void {}
function serialize(mixed $value): string {}
39. Mixed Type
<?php
// Mixed is a common approximation for generic functions:
function array_reduce<K, V, R>(
array<K, V> $arg,
callable(R, V): R $callback, R $initial = null
): R {}
40. Mixed Type
<?php
// Mixed is a common approximation for generic functions:
function array_reduce<K, V, R>(
array<K, V> $arg,
callable(R, V): R $callback, R $initial = null
): R {}
// Back down to earth:
function array_reduce(
array $arg, callable $callback,
mixed $initial = null
): mixed {}
41. Mixed Type
<?php
// For argument types:
// No type same as mixed type
class A {
public function method(mixed $arg) {}
}
class B extends A {
public function method($arg) {}
}
Allowed
42. Mixed Type
<?php
// For return types:
// No type effectively means mixed|void
class A {
public function method(): mixed {}
}
class B extends A {
public function method() {}
}
43. Mixed Type
<?php
// For return types:
// No type effectively means mixed|void
class A {
public function method(): mixed {}
}
class B extends A {
public function method() {}
}
Forbidden: Widening return type
44. Static Return Type
<?php
// Named constructor:
class TestParent {
public function createFromWhatever($whatever): static {
return new static($whatever);
}
}
45. Static Return Type
<?php
// Named constructor:
class TestParent {
public function createFromWhatever($whatever): static {
return new static($whatever);
}
}
class TestChild extends TestParent {}
// TestChild::createFromWhatever(...)
// must return TestChild, not TestParent!
46. Static Return Type
<?php
// Wither pattern:
class Test {
public function withWhatever($whatever): static {
$clone = clone $this;
$clone->whatever = $whatever;
return $clone;
}
}
47. Static Return Type
<?php
// Fluent methods:
class Test {
public function doWhatever(): static {
// Do whatever.
return $this;
}
}
48. Match Expression
<?php
switch ($operator) {
case '+':
$result = $a + $b;
break;
case '-':
$result = $a - $b;
break;
case '*':
$result = $a * $b;
break;
default:
throw new UnsupportedOperator($operator);
}
49. Match Expression
<?php
$result = match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
default => throw new UnsupportedOperator($operator);
};
50. Match Expression
<?php
Expression with a return value
$result = match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
default => throw new UnsupportedOperator($operator);
};
51. Match Expression
<?php
Expression with a return value
$result = match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
default => throw new UnsupportedOperator($operator),
};
Each match clause is an expression
("throw" is an expression now)
52. Match Expression
<?php
function evalOp($operator, $a, $b) {
return match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
};
}
// Match is exhaustive:
evalOp('/', 10, 2); // UnhandledMatchError
53. Match Expression
<?php
function evalOp($operator, $a, $b) {
return match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
};
}
// Match compares using ===, not ==.
evalOp(true, 10, 2); // UnhandledMatchError
54. Nullsafe Operator
<?php
$name = $session !== null
? $session->getUser()->name
: null;
// Same as:
$name = $session?->getUser()->name;
55. Nullsafe Operator
<?php
$name = $session?->getUser()?->name;
// Approximately same as:
$name = null;
if ($session !== null) {
$user = $session->getUser();
if ($user !== null) {
$name = $user->name;
}
}
56. Other Features
● catch (Exception) without variable
● $object::class
● str_contains(), str_starts_with(), str_ends_with()
● get_debug_type()
● Stable sorting
● WeakMap
57. Backwards Compatibility Breaks
●
●
Functionality deprecated before PHP 8.0 has
been removed!
Full list:
https://github.com/php/php-src/blob/master/UPGRADING
58. Number to String Comparison
<?php
$validValues = ["foo", "bar", "baz"];
$value = 0;
var_dump(in_array($value, $validValues));
// bool(true)
// ???
59. Number to String Comparison
<?php
0 == "foo";
// Before:
0 == (int)"foo";
// After:
(string)0 == "foo";
60. Number to String Comparison
Comparison
| Before | After
------------------------------
0 == "0"
| true
| true
0 == "0.0"
| true
| true
0 == "foo"
| true
| false
0 == ""
| true
| false
42 == "
42" | true
| true
42 == "42foo" | true
| false
61. Resource To Object Migration
●
●
Long term goal: Convert all resources to objects
Objects are type-safe and have much better
internal support
62. Resource To Object Migration
●
●
●
Long term goal: Convert all resources to objects
Objects are type-safe and have much better
internal support
Using "opaque objects"
–
Actual object-oriented APIs may be added later
63. Resource To Object Migration
● CurlHandle, CurlMultiHandle, CurlShareHandle
● EnchantBroker, EnchantDictionary
● GdImage
● InflateContext, DeflateContext
●
OpenSSLCertificate, OpenSSLCertificateSigningRequest,
OpenSSLAsymmetricKey
● Shmop
● Socket, AddressInfo
● SysvMessageQueue, SysvSemaphore, SysvSharedMemory
● XmlParser
● XmlWriter (already had an OO API)
64. Resource To Object Migration
<?php
$image = imagecreatefrompng($path);
if (!is_resource($image)) {
throw new MalformedImageException;
}
65. Resource To Object Migration
<?php
Now a GdImage object on success
$image = imagecreatefrompng($path);
if (!is_resource($image)) {
throw new MalformedImageException;
}
Will always throw...
66. Resource To Object Migration
<?php
$image = imagecreatefrompng($path);
if (false === $image) {
throw new MalformedImageException;
}
67. Warning → Error exception
●
Many warnings converted to Error exceptions
– TypeError
– ValueError
68. Warning → Error exception
●
●
Only allowed for error conditions that imply
programmer error
It makes no sense to "handle" the error, code
needs to be fixed instead
69. Warning → Error exception
<?php
var_dump(strlen([]));
// Warning: strlen() expects parameter 1 to be string,
// array given
// NULL
function strlen(string $str): int|null {}
70. Warning → Error exception
<?php
var_dump(strlen([]));
// Uncaught TypeError: strlen(): Argument #1 ($str)
// must be of type string, array given
function strlen(string $str): int {}
71. Warning → Error exception
<?php
var_dump(array_fill(0, -100, "foobar"));
// Warning: array_fill(): Number of elements can't
// be negative
// bool(false)
function array_fill(
int $start_index, int $num, mixed $value
): array|false {}
72. Warning → Error exception
<?php
var_dump(array_fill(0, -100, "foobar"));
// Uncaught ValueError: array_fill(): Argument #2 ($count)
// must be greater than or equal to 0
function array_fill(
int $start_index, int $count, mixed $value
): array {}
73. Warning → Error exception
<?php
var_dump(fopen("does_not_exist.txt", "r"));
// Warning: fopen(does_not_exist.txt):
// Failed to open stream: No such file or directory
// bool(false)
74. Warning → Error exception
<?php
var_dump(fopen("does_not_exist.txt", "r"));
// Warning: fopen(does_not_exist.txt):
// Failed to open stream: No such file or directory
// bool(false)
NOT going to change!
fopen() failure is an environment failure condition,
it does not imply programmer error!
75. PHP Stubs
●
●
PHP stub files specify function signatures for
internal functions/methods
Used to generate C code for function
registration
76. PHP Stubs
<?php
function array_search(
mixed $needle, array $haystack, bool $strict = false
): int|string|false {}
77. PHP Stubs
<?php
function array_search(
mixed $needle, array $haystack, bool $strict = false
): int|string|false {}
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(
arginfo_array_search, 0, 2,
MAY_BE_LONG|MAY_BE_STRING|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, needle, IS_MIXED, 0)
ZEND_ARG_TYPE_INFO(0, haystack, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(
0, strict, _IS_BOOL, 0, "false")
ZEND_END_ARG_INFO()
78. PHP Stubs
●
Data available through Reflection:
– ReflectionFunction::getReturnType()
– ReflectionParameter::getType()
– ReflectionParameter::getDefaultValue()
79. PHP Stubs
<?php
// Stub
class DateTime implements DateTimeInterface {
/** @return DateTime */
public function add(DateInterval $interval) {}
}
// Your code
class MyDateTime extends DateTime {
public function add(DateInterval $interval) {
// Do something
}
}
80. PHP Stubs
<?php
// Stub
class DateTime implements DateTimeInterface {
/** @return DateTime */
public function add(DateInterval $interval) {}
}
Now allowed!
// Your code
class MyDateTime extends DateTime {
public function add(DateInterval $interval) {
// Do something
}
}
81. PHP Stubs
<?php
A real return type would force all extending
classes to specify it.
// Stub
class DateTime implements DateTimeInterface {
/** @return DateTime */
public function add(DateInterval $interval) {}
}
Now allowed!
// Your code
class MyDateTime extends DateTime {
public function add(DateInterval $interval) {
// Do something
}
}
82. 3v4l.org
83. Travis CI
php:
- nightly
install:
- |
if [ $TRAVIS_PHP_VERSION = 'nightly' ]; then
composer install --ignore-platform-reqs;
else
composer install;
fi
Some libraries are not formally
compatible with PHP 8 (yet)
84. Thank You!