class Status
{
const DRAFT = 'draft';
const PUBLISHED = 'published';
const ARCHIVED = 'archived';
}
function acceptStatus(string $status) {...}
enum Status
{
case Draft;
case Published;
case Archived;
}
function acceptStatus(Status $status) {...}
class BlogData
{
private Status $status;
public function __construct(Status $status)
{
$this->status = $status;
}
public function getStatus(): Status
{
return $this->status;
}
}
class BlogData
{
public readonly Status $status;
public function __construct(Status $status)
{
$this->status = $status;
}
}
Readonly properties cannot be changed after initialization, i.e. after a value is assigned to them.
They are a great way to model value objects and data-transfer objects.
$foo = [$this, 'foo'];
$fn = Closure::fromCallable('strlen');
$foo = $this->foo(...);
$fn = strlen(...);
It is now possible to get a reference to any function – this is called first-class callable syntax.
class Service
{
private Logger $logger;
public function __construct(
?Logger $logger = null,
) {
$this->logger = $logger ?? new NullLogger();
}
}
class Service
{
private Logger $logger;
public function __construct(
Logger $logger = new NullLogger(),
) {
$this->logger = $logger;
}
}
Objects can now be used as default parameter values, static variables, and global constants, as well as in attribute arguments.
This effectively makes it possible to use nested attributes.
class User
{
/**
* @Assert\All({
* @Assert\NotNull,
* @Assert\Length(min=5)
* })
*/
public string $name = '';
}
class User
{
#[\Assert\All(
new \Assert\NotNull,
new \Assert\Length(min: 5))
]
public string $name = '';
}
function count_and_iterate(Iterator $value) {
if (!($value instanceof Countable)) {
throw new TypeError('value must be Countable');
}
foreach ($value as $val) {
echo $val;
}
count($value);
}
function count_and_iterate(Iterator&Countable $value) {
foreach ($value as $val) {
echo $val;
}
count($value);
}
Use intersection types when a value needs to satisfy multiple type constraints at the same time.
It is not currently possible to mix intersection and union types together such as A&B|C
.
function redirect(string $uri) {
header('Location: ' . $uri);
exit();
}
function redirectToLoginPage() {
redirect('/login');
echo 'Hello'; // <- dead code
}
function redirect(string $uri): never {
header('Location: ' . $uri);
exit();
}
function redirectToLoginPage(): never {
redirect('/login');
echo 'Hello'; // <- dead code detected by static analysis
}
A function or method declared with the never
type indicates that it will not return a value and will either throw an exception or end the script’s execution with a call of die()
, exit()
, trigger_error()
, or something similar.
class Foo
{
public const XX = "foo";
}
class Bar extends Foo
{
public const XX = "bar"; // No error
}
class Foo
{
final public const XX = "foo";
}
class Bar extends Foo
{
public const XX = "bar"; // Fatal error
}
It is possible to declare final class constants, so that they cannot be overridden in child classes.
016 === 16; // false because `016` is octal for `14` and it's confusing
016 === 14; // true
0o16 === 16; // false — not confusing with explicit notation
0o16 === 14; // true
It is now possible to write octal numbers with the explicit 0o
prefix.
$httpClient->request('https://example.com/')
->then(function (Response $response) {
return $response->getBody()->buffer();
})
->then(function (string $responseBody) {
print json_decode($responseBody)['code'];
});
$response = $httpClient->request('https://example.com/');
print json_decode($response->getBody()->buffer())['code'];
Fibers are primitives for implementing lightweight cooperative concurrency. They are a means of creating code blocks that can be paused and resumed like Generators, but from anywhere in the stack. Fibers themselves don't magically provide concurrency, there still needs to be an event loop. However, they allow blocking and non-blocking implementations to share the same API.
Fibers allow getting rid of the boilerplate code previously seen with Promise::then()
or Generator based coroutines. Libraries will generally build further abstractions around Fibers, so there's no need to interact with them directly.
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];
$result = array_merge(['a' => 0], $arrayA, $arrayB);
// ['a' => 1, 'b' => 2]
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];
$result = ['a' => 0, ...$arrayA, ...$arrayB];
// ['a' => 1, 'b' => 2]
PHP supported unpacking inside arrays through the spread operator before, but only if the arrays had integer keys. Now it is possible to unpack arrays with string keys too.
#[ReturnTypeWillChange]
attribute.fsync
and fdatasync
functions.array_is_list
function.Serializable
interface deprecated.$GLOBALS
variable restrictions.file_info
resources migrated to existing finfo objects.IMAP\Connection
class objects.FTP\Connection
class objects.GdFont
class objects.LDAP\Connection
, LDAP\Result
, and LDAP\ResultEntry
objects.PgSql\Connection
, PgSql\Result
, and PgSql\Lob
objects.PSpell\Dictionary
, PSpell\Config
class objects.