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!