What's new in PHP 8.0?

What's new in PHP 8.0?
Nikita Popov
What's new in PHP 8.0?
PHP 8.0
●
Just-In-Time (JIT) compiler
●
Many new language features
●
SemVer major version: Backwards incompatible
changes
Just-In-Time (JIT) Compiler
●
Compiles PHP code to x86 machine code to
improve performance
Just-In-Time (JIT) Compiler
●
Compiles PHP code to x86 machine code to
improve performance
●
Basic usage:
– Enable opcache
– opcache.jit_buffer_size=128M
Just-In-Time (JIT) Compiler
bench.php
micro_bench.php
PHP-Parser
amphp
Symfony Demo
With Preloading
0 0.5 1 1.5 2 2.5 3 3.5
Baseline: Opcache + No JIT
Just-In-Time (JIT) Compiler
bench.php
micro_bench.php
PHP-Parser
amphp
Symfony Demo
With Preloading
0 0.5 1 1.5 2 2.5 3 3.5
Baseline: Opcache + No JIT
Just-In-Time (JIT) Compiler
bench.php
micro_bench.php
PHP-Parser
amphp
Symfony Demo
With Preloading
0 0.5 1 1.5 2 2.5 3 3.5
Baseline: Opcache + No JIT
Just-In-Time (JIT) Compiler
bench.php
micro_bench.php
PHP-Parser
amphp
Symfony Demo
With Preloading
0 0.5 1 1.5 2 2.5 3 3.5
Baseline: Opcache + No JIT
Attributes
<?php
use SymfonyComponentRoutingAnnotationRoute;
class SomeController {
/**
* @Route("/path", name="action")
*/
public function someAction() {
}
}
Attributes
<?php
use SymfonyComponentRoutingAnnotationRoute;
class SomeController {
#[Route("/path", name: "action")]
public function someAction() {
}
}
Attributes
<?php
use SymfonyComponentRoutingAnnotationRoute;
class SomeController {
#[Route("/path", name: "action")]
public function someAction() {
}
}
Class name Constructor arguments
Attributes
<?php
namespace SymfonyComponentRoutingAnnotation;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD)]
class Route {
public function __construct(string $path, ...) {
// ...
}
}
Attributes
<?php
$rm = new ReflectionMethod(
SomeController::class, "someAction");
foreach ($rm->getAttributes() as $attr) {
var_dump($attr->getName());
// => "SymfonyComponentRoutingAnnotationRoute"
var_dump($attr->getArguments());
// => ["/path", "name" => "action"]
var_dump($attr->newInstance());
// object(SymfonyComponentRoutingAnnotationRoute)
}
Attributes
<?php
$rm = new ReflectionMethod(
SomeController::class, "someAction");
foreach ($rm->getAttributes() as $attr) {
var_dump($attr->getName());
// => "SymfonyComponentRoutingAnnotationRoute"
var_dump($attr->getArguments());
// => ["/path", "name" => "action"]
var_dump($attr->newInstance());
// object(SymfonyComponentRoutingAnnotationRoute)
}
Attribute validation happens HERE.
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;
}
}
Constructor Promotion
<?php
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
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 now allowed
Named Arguments
<?php
// Using positional arguments:
array_fill(0, 100, 50);
Named Arguments
<?php
// Using positional arguments:
array_fill(0, 100, 50);
// Using named arguments:
array_fill(start_index: 0, count: 100, value: 50);
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);
Named Arguments
<?php
// Using positional arguments:
htmlspecialchars(
$string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
Named Arguments
<?php
// Using positional arguments:
htmlspecialchars(
$string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
// Using named arguments:
htmlspecialchars($string, double_encode: false);
Named Arguments
<?php
use SymfonyComponentRoutingAnnotationRoute;
class SomeController {
#[Route("/path", name: "action")]
public function someAction() {
// ...
}
}
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);
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
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;
}
}
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;
}
}
Union Types
<?php
function strpos(
string $haystack, string $needle, int $offset = 0
): int|false {}
Union Types
<?php
function strpos(
string $haystack, string $needle, int $offset = 0
): int|false {}
Very common in standard library
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
Union Types
<?php declare(strict_types=0);
function test(int|float|bool $arg) {
var_dump($arg);
}
test(45); // int(45)
test(45.8); // float(45.8)
test("45"); // int(45)
test("45.8"); // float(45.8)
test(""); // bool(false)
test("X"); // bool(true)
test([]); // TypeError
Union Types
<?php declare(strict_types=1);
function test(int|float|bool $arg) {
var_dump($arg);
}
test(45); // int(45)
test(45.8); // float(45.8)
test("45"); // TypeError
test("45.8"); // TypeError
test(""); // TypeError
test("X"); // TypeError
test([]); // TypeError
Mixed Type
●
Distinguishes between:
– Type is missing because I didn't add one yet
– This function really does accept any value
Mixed Type
<?php
function var_dump(mixed $value, mixed ...$value): void {}
function serialize(mixed $value): string {}
Mixed Type
<?php
function var_dump(mixed $value, mixed ...$value): void {}
function serialize(mixed $value): string {}
// "mixed" is a common approximation for generic functions:
function array_reduce(
array $arg, callable $callback,
mixed $initial = null
): mixed {}
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
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() {}
}
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
Static Return Type
<?php
// Named constructor:
class TestParent {
public function createFromWhatever($whatever): static {
return new static($whatever);
}
}
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!
Static Return Type
<?php
// Fluent methods:
class Test {
public function doWhatever(): static {
// Do whatever.
return $this;
}
}
Static Return Type
<?php
// Wither pattern:
class Test {
public function withWhatever($whatever): static {
$clone = clone $this;
$clone->whatever = $whatever;
return $clone;
}
}
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);
}
Match Expression
<?php
$result = match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
default => throw new UnsupportedOperator($operator);
};
Match Expression
<?php
$result = match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
default => throw new UnsupportedOperator($operator);
};
Expression with a return value
Match Expression
<?php
$result = match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
default => throw new UnsupportedOperator($operator),
};
Expression with a return valueExpression with a return value
Each match clause is an expression
("throw" is an expression now)
Match Expression
<?php
function evalOp($operator, $a, $b) {
return match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
};
}
// Match is exhaustive:
evalOp('/', 10, 2); // UnhandledMatchError
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
Nullsafe Operator
<?php
$name = $session !== null
? $session->getUser()->name
: null;
// Same as:
$name = $session?->getUser()->name;
Nullsafe Operator
<?php
$name = $session?->getUser()?->name;
// Approximately same as:
$name = null;
if ($session !== null) {
$user = $session->getUser();
if ($user !== null) {
$name = $user->name;
}
}
Other Features
●
catch (Exception) without variable
●
$object::class
●
str_contains(), str_starts_with(), str_ends_with()
●
get_debug_type()
●
Stable sorting
●
WeakMap
Backwards Compatibility Breaks
●
Functionality deprecated before PHP 8.0 has
been removed!
●
Full list:
https://github.com/php/php-src/blob/PHP-8.0/UPGRADING
Number to String Comparison
<?php
$validValues = ["foo", "bar", "baz"];
$value = 0;
var_dump(in_array($value, $validValues));
// bool(true)
// ???
Number to String Comparison
<?php
0 == "foo";
// Before:
0 == (int)"foo";
// After:
(string)0 == "foo";
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
Resource To Object Migration
●
Long term goal: Convert all resources to objects
●
Objects are type-safe and have much better
internal support
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
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)
Resource To Object Migration
<?php
$image = imagecreatefrompng($path);
if (!is_resource($image)) {
throw new MalformedImageException;
}
Resource To Object Migration
<?php
$image = imagecreatefrompng($path);
if (!is_resource($image)) {
throw new MalformedImageException;
}
Now a GdImage object on success
Will always throw...
Resource To Object Migration
<?php
$image = imagecreatefrompng($path);
if (false === $image) {
throw new MalformedImageException;
}
Warning → Error exception
●
Many warnings converted to Error exceptions
– TypeError
– ValueError
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
Warning → Error exception
<?php
var_dump(strlen([]));
// Warning: strlen() expects parameter 1 to be string,
// array given
// NULL
function strlen(string $str): int|null {}
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 {}
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 {}
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 {}
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)
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!
PHP Stubs
●
PHP stub files specify function signatures for
internal functions/methods
●
Used to generate C code for function
registration
PHP Stubs
<?php
function array_search(
mixed $needle, array $haystack, bool $strict = false
): int|string|false {}
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()
PHP Stubs
●
Data available through Reflection:
– ReflectionFunction::getReturnType()
– ReflectionParameter::getType()
– ReflectionParameter::getDefaultValue()
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
}
}
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
}
}
Now allowed!
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
}
}
Now allowed!
A real return type would force all extending
classes to specify it.
PHP Stubs
●
Ties in with many other efforts:
– Union types critical for standard library signatures
– Warning -> Error exception promotions avoid
unnecessary return types
– Resource to object migration allows use as types
– Named arguments based on stubs default values
Thank You!
1 of 79

More Related Content

What's hot(20)

JavaScript PromisesJavaScript Promises
JavaScript Promises
L&T Technology Services Limited4.6K views
My Top 5 APEX JavaScript API'sMy Top 5 APEX JavaScript API's
My Top 5 APEX JavaScript API's
Roel Hartman7.3K views
Oops concepts in phpOops concepts in php
Oops concepts in php
CPD INDIA3.6K views
Javascript this keywordJavascript this keyword
Javascript this keyword
Pham Huy Tung2.3K views
Express node jsExpress node js
Express node js
Yashprit Singh2.5K views
JavaScriptJavaScript
JavaScript
Sunil OS519K views
Bca 2nd sem u-4 operator overloadingBca 2nd sem u-4 operator overloading
Bca 2nd sem u-4 operator overloading
Rai University1.7K views
Unary operator overloadingUnary operator overloading
Unary operator overloading
Md. Ashraful Islam1K views
PHP -  Introduction to  Object Oriented Programming with PHPPHP -  Introduction to  Object Oriented Programming with PHP
PHP - Introduction to Object Oriented Programming with PHP
Vibrant Technologies & Computers2.8K views
Phpunit testingPhpunit testing
Phpunit testing
Nikunj Bhatnagar1K views
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1
Philip Schwarz3.2K views
PHP POWERPOINT SLIDESPHP POWERPOINT SLIDES
PHP POWERPOINT SLIDES
Ismail Mukiibi3.9K views
Php Tutorials for BeginnersPhp Tutorials for Beginners
Php Tutorials for Beginners
Vineet Kumar Saini5.3K views
PHP -  Introduction to PHP AJAXPHP -  Introduction to PHP AJAX
PHP - Introduction to PHP AJAX
Vibrant Technologies & Computers1.2K views
Clean code in JavaScriptClean code in JavaScript
Clean code in JavaScript
Mathieu Breton198 views
Nodejs functions & modulesNodejs functions & modules
Nodejs functions & modules
monikadeshmane140 views
JavaScript PromisesJavaScript Promises
JavaScript Promises
Derek Willian Stavis1.1K views

Similar to What's new in PHP 8.0?

PHP BasicsPHP Basics
PHP BasicsSaraswathi Murugan
208 views61 slides
Introduction to PHPIntroduction to PHP
Introduction to PHPBradley Holt
42.6K views77 slides

Similar to What's new in PHP 8.0?(20)

What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?
Nikita Popov10K views
Web Technology_10.pptWeb Technology_10.ppt
Web Technology_10.ppt
Aftabali7022404 views
PHP BasicsPHP Basics
PHP Basics
Saraswathi Murugan208 views
Introduction to PHPIntroduction to PHP
Introduction to PHP
Bradley Holt42.6K views
PHP Conference Asia 2016PHP Conference Asia 2016
PHP Conference Asia 2016
Britta Alex358 views
Giới thiệu PHP 7Giới thiệu PHP 7
Giới thiệu PHP 7
ZendVN787 views
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
jsmith92492 views
Object Oriented PHP - PART-2Object Oriented PHP - PART-2
Object Oriented PHP - PART-2
Jalpesh Vasa175 views
Functions in PHP.pptxFunctions in PHP.pptx
Functions in PHP.pptx
Japneet92 views
PHP BasicPHP Basic
PHP Basic
Yoeung Vibol476 views
php user defined functionsphp user defined functions
php user defined functions
vishnupriyapm4155 views
Php7 hhvm and coPhp7 hhvm and co
Php7 hhvm and co
Pierre Joye1.6K views
SymfonyCon 2017 php7 performancesSymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performances
julien pauli2.8K views
Mastering Namespaces in PHPMastering Namespaces in PHP
Mastering Namespaces in PHP
Nick Belhomme16.7K views
Learn PHP Basics Learn PHP Basics
Learn PHP Basics
McSoftsis368 views

More from Nikita Popov(9)

What's new in PHP 8.0?

  • 1. What's new in PHP 8.0? Nikita Popov
  • 3. PHP 8.0 ● Just-In-Time (JIT) compiler ● Many new language features ● SemVer major version: Backwards incompatible changes
  • 4. Just-In-Time (JIT) Compiler ● Compiles PHP code to x86 machine code to improve performance
  • 5. Just-In-Time (JIT) Compiler ● Compiles PHP code to x86 machine code to improve performance ● Basic usage: – Enable opcache – opcache.jit_buffer_size=128M
  • 6. Just-In-Time (JIT) Compiler bench.php micro_bench.php PHP-Parser amphp Symfony Demo With Preloading 0 0.5 1 1.5 2 2.5 3 3.5 Baseline: Opcache + No JIT
  • 7. Just-In-Time (JIT) Compiler bench.php micro_bench.php PHP-Parser amphp Symfony Demo With Preloading 0 0.5 1 1.5 2 2.5 3 3.5 Baseline: Opcache + No JIT
  • 8. Just-In-Time (JIT) Compiler bench.php micro_bench.php PHP-Parser amphp Symfony Demo With Preloading 0 0.5 1 1.5 2 2.5 3 3.5 Baseline: Opcache + No JIT
  • 9. Just-In-Time (JIT) Compiler bench.php micro_bench.php PHP-Parser amphp Symfony Demo With Preloading 0 0.5 1 1.5 2 2.5 3 3.5 Baseline: Opcache + No JIT
  • 10. Attributes <?php use SymfonyComponentRoutingAnnotationRoute; class SomeController { /** * @Route("/path", name="action") */ public function someAction() { } }
  • 11. Attributes <?php use SymfonyComponentRoutingAnnotationRoute; class SomeController { #[Route("/path", name: "action")] public function someAction() { } }
  • 12. Attributes <?php use SymfonyComponentRoutingAnnotationRoute; class SomeController { #[Route("/path", name: "action")] public function someAction() { } } Class name Constructor arguments
  • 14. Attributes <?php $rm = new ReflectionMethod( SomeController::class, "someAction"); foreach ($rm->getAttributes() as $attr) { var_dump($attr->getName()); // => "SymfonyComponentRoutingAnnotationRoute" var_dump($attr->getArguments()); // => ["/path", "name" => "action"] var_dump($attr->newInstance()); // object(SymfonyComponentRoutingAnnotationRoute) }
  • 15. Attributes <?php $rm = new ReflectionMethod( SomeController::class, "someAction"); foreach ($rm->getAttributes() as $attr) { var_dump($attr->getName()); // => "SymfonyComponentRoutingAnnotationRoute" var_dump($attr->getArguments()); // => ["/path", "name" => "action"] var_dump($attr->newInstance()); // object(SymfonyComponentRoutingAnnotationRoute) } Attribute validation happens HERE.
  • 16. 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; } }
  • 17. Constructor Promotion <?php class Point { public function __construct( public float $x = 0.0, public float $y = 0.0, public float $z = 0.0, ) {} }
  • 18. 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 now allowed
  • 19. Named Arguments <?php // Using positional arguments: array_fill(0, 100, 50);
  • 20. Named Arguments <?php // Using positional arguments: array_fill(0, 100, 50); // Using named arguments: array_fill(start_index: 0, count: 100, value: 50);
  • 21. 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);
  • 22. Named Arguments <?php // Using positional arguments: htmlspecialchars( $string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
  • 23. Named Arguments <?php // Using positional arguments: htmlspecialchars( $string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false); // Using named arguments: htmlspecialchars($string, double_encode: false);
  • 24. Named Arguments <?php use SymfonyComponentRoutingAnnotationRoute; 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 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
  • 27. 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; } }
  • 28. 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; } }
  • 29. Union Types <?php function strpos( string $haystack, string $needle, int $offset = 0 ): int|false {}
  • 30. Union Types <?php function strpos( string $haystack, string $needle, int $offset = 0 ): int|false {} Very common in standard library
  • 31. 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
  • 32. Union Types <?php declare(strict_types=0); function test(int|float|bool $arg) { var_dump($arg); } test(45); // int(45) test(45.8); // float(45.8) test("45"); // int(45) test("45.8"); // float(45.8) test(""); // bool(false) test("X"); // bool(true) test([]); // TypeError
  • 33. Union Types <?php declare(strict_types=1); function test(int|float|bool $arg) { var_dump($arg); } test(45); // int(45) test(45.8); // float(45.8) test("45"); // TypeError test("45.8"); // TypeError test(""); // TypeError test("X"); // TypeError test([]); // TypeError
  • 34. Mixed Type ● Distinguishes between: – Type is missing because I didn't add one yet – This function really does accept any value
  • 35. Mixed Type <?php function var_dump(mixed $value, mixed ...$value): void {} function serialize(mixed $value): string {}
  • 36. Mixed Type <?php function var_dump(mixed $value, mixed ...$value): void {} function serialize(mixed $value): string {} // "mixed" is a common approximation for generic functions: function array_reduce( array $arg, callable $callback, mixed $initial = null ): mixed {}
  • 37. 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
  • 38. 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() {} }
  • 39. 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
  • 40. Static Return Type <?php // Named constructor: class TestParent { public function createFromWhatever($whatever): static { return new static($whatever); } }
  • 41. 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!
  • 42. Static Return Type <?php // Fluent methods: class Test { public function doWhatever(): static { // Do whatever. return $this; } }
  • 43. Static Return Type <?php // Wither pattern: class Test { public function withWhatever($whatever): static { $clone = clone $this; $clone->whatever = $whatever; return $clone; } }
  • 44. 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); }
  • 45. Match Expression <?php $result = match ($operator) { '+' => $a + $b, '-' => $a - $b, '*' => $a * $b, default => throw new UnsupportedOperator($operator); };
  • 46. Match Expression <?php $result = match ($operator) { '+' => $a + $b, '-' => $a - $b, '*' => $a * $b, default => throw new UnsupportedOperator($operator); }; Expression with a return value
  • 47. Match Expression <?php $result = match ($operator) { '+' => $a + $b, '-' => $a - $b, '*' => $a * $b, default => throw new UnsupportedOperator($operator), }; Expression with a return valueExpression with a return value Each match clause is an expression ("throw" is an expression now)
  • 48. Match Expression <?php function evalOp($operator, $a, $b) { return match ($operator) { '+' => $a + $b, '-' => $a - $b, '*' => $a * $b, }; } // Match is exhaustive: evalOp('/', 10, 2); // UnhandledMatchError
  • 49. 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
  • 50. Nullsafe Operator <?php $name = $session !== null ? $session->getUser()->name : null; // Same as: $name = $session?->getUser()->name;
  • 51. Nullsafe Operator <?php $name = $session?->getUser()?->name; // Approximately same as: $name = null; if ($session !== null) { $user = $session->getUser(); if ($user !== null) { $name = $user->name; } }
  • 52. Other Features ● catch (Exception) without variable ● $object::class ● str_contains(), str_starts_with(), str_ends_with() ● get_debug_type() ● Stable sorting ● WeakMap
  • 53. Backwards Compatibility Breaks ● Functionality deprecated before PHP 8.0 has been removed! ● Full list: https://github.com/php/php-src/blob/PHP-8.0/UPGRADING
  • 54. Number to String Comparison <?php $validValues = ["foo", "bar", "baz"]; $value = 0; var_dump(in_array($value, $validValues)); // bool(true) // ???
  • 55. Number to String Comparison <?php 0 == "foo"; // Before: 0 == (int)"foo"; // After: (string)0 == "foo";
  • 56. 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
  • 57. Resource To Object Migration ● Long term goal: Convert all resources to objects ● Objects are type-safe and have much better internal support
  • 58. 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
  • 59. 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)
  • 60. Resource To Object Migration <?php $image = imagecreatefrompng($path); if (!is_resource($image)) { throw new MalformedImageException; }
  • 61. Resource To Object Migration <?php $image = imagecreatefrompng($path); if (!is_resource($image)) { throw new MalformedImageException; } Now a GdImage object on success Will always throw...
  • 62. Resource To Object Migration <?php $image = imagecreatefrompng($path); if (false === $image) { throw new MalformedImageException; }
  • 63. Warning → Error exception ● Many warnings converted to Error exceptions – TypeError – ValueError
  • 64. 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
  • 65. Warning → Error exception <?php var_dump(strlen([])); // Warning: strlen() expects parameter 1 to be string, // array given // NULL function strlen(string $str): int|null {}
  • 66. 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 {}
  • 67. 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 {}
  • 68. 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 {}
  • 69. 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)
  • 70. 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!
  • 71. PHP Stubs ● PHP stub files specify function signatures for internal functions/methods ● Used to generate C code for function registration
  • 72. PHP Stubs <?php function array_search( mixed $needle, array $haystack, bool $strict = false ): int|string|false {}
  • 73. 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()
  • 74. PHP Stubs ● Data available through Reflection: – ReflectionFunction::getReturnType() – ReflectionParameter::getType() – ReflectionParameter::getDefaultValue()
  • 75. 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 } }
  • 76. 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 } } Now allowed!
  • 77. 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 } } Now allowed! A real return type would force all extending classes to specify it.
  • 78. PHP Stubs ● Ties in with many other efforts: – Union types critical for standard library signatures – Warning -> Error exception promotions avoid unnecessary return types – Resource to object migration allows use as types – Named arguments based on stubs default values