Ray Lee | 李宗叡
Learn or Die
Published in
10 min readJan 27, 2023

--

# Constructor property promotion

現在可以直接在 construct 的 argument 中定義 property, 省略 assign 的步驟

// Before PHP 8.0
class Client
{
private string $url;

public function __construct(string $url)
{
$this->url = $url;
}
}
// PHP 8.0
class Client
{
public function __construct(
private string $url,
) {}
}

# Union Types

現在可以把 return type 定義的很清楚

// Before PHP 8.0
class PostService
{
public function all(): mixed
{
if (! Auth::check()) {
return [];
}

return Post::query()->get();
}
}
// PHP 8.0
class PostService
{
public function all(): array|Collection
{
if (! Auth::check()) {
return [];
}

return Post::query()->get();
}
}

# Named Arguments

PHP 8 之前, method 的 argument 一定要照順序, 如果中間有一個是 nullable, 那就要在帶入的時後特別給 null Named argument 解決了這個問題, 不用照順序, 如果 default = null 的 arg, 帶入時也不用特別給 null 了

// Before PHP 8.0
class ProcessImage
{
public static function handle(string $path, int $height, int $width, string $type, int $quality, int $compression): void
{
// logic for handling image processing
}
}

ProcessImage::handle('/path/to/image.jpg', 500, 300, 'jpg', 100, 5);
// PHP 8.0
class ProcessImage
{
public static function handle(string $path, int $height, int $width, string $type, int $quality, int $compression): void
{
// logic for handling image processing
}
}

ProcessImage::handle(
path: '/path/to/image.jpg',
height: 500,
width: 300,
type: 'jpg',
quality: 100,
compression: 5,
);

# Match Expressions

可用來取代 switch, 比較簡短, 至於可讀性見仁見智, 這個比較主觀

// Before PHP 8.0
switch (string $method) {
case 'GET':
$method = 'GET';
break;
case 'POST':
$method = 'POST';
break;
default:
throw new Exception("$method is not supported yet.");
}

// PHP 8.0
match (string $method) {
'GET' => $method = 'GET',
'POST' => $method = 'POST',
default => throw new Exception(
message: "$method is not supported yet.",
),
};

# Using ::class on objects

以前要 pass 一個 class string 到 argument, 都要特別使用 get_class, 現在不用了

// Before PHP 8.0
$commandBus->dispatch(get_class($event), $payload);

// PHP 8.0
$commandBus->dispatch(
event: $event::class,
payload: $payload,
);

# None capturing catch blocks

try catch 時, 如果 exception 沒用到, 那 PHP 8 之後就不用帶入了, 這個我自己是比較少用到

// Before PHP 8.0
try {
$response = $this->sendRequest();
} catch (RequestException $exception) {
Log::error('API request failed to send.');
}

// PHP 8.0
try {
$response = $this->sendRequest();
} catch (RequestException) {
Log::error('API request failed to send.');
}

# Enums

PHP 現在也支援 Enum 了, 使用情境可參照以下範例

// Before PHP 8.1
class Method
{
public const GET = 'GET';
public const POST = 'POST';
public const PUT = 'PUT';
public const PATCH = 'PATCH';
public const DELETE = 'DELETE';
}
// PHP 8.1
enum Method: string
{
case GET = 'GET';
case POST = 'POST';
case PUT = 'PUT';
case PATCH = 'PATCH';
case DELETE = 'DELETE';
}
// Before PHP 8.1
trait SendsRequests
{
public function send(string $method, string $uri, array $options = []): Response
{
if (! in_array($method, ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'])) {
throw new InvalidArgumentException(
message: "Method [$method] is not supported.",
);
}

return $this->buildRequest()->send(
method: $method,
uri: $uri,
options: $options,
);
}
}
// PHP 8.1
trait SendsRequests
{
public function send(Method $method, string $uri, array $options = []): Response
{
return $this->buildRequest()->send(
method: $method->value,
uri: $uri,
options: $options,
);
}
}

# Unpacking arrays

可以 unpack array, 就像 js 中的的 ...object, 詳見以下範例:

// Before PHP 8.1
final class CreateNewClient implements CreateNewClientContract
{
public function handle(DataObjectContract $client, int $account): Model|Client
{
return Client::query()->create(
attributes: array_merge(
$client->toArray(),
[
'account_id' => $account,
],
),
);
}
}

// PHP 8.1
final class CreateNewClient implements CreateNewClientContract
{
public function handle(DataObjectContract $client, int $account): Model|Client
{
return Client::query()->create(
attributes: [
...$client->toArray(),
'account_id' => $account,
],
);
}
}

# New in constructors

可以在 constructor argument 中, 使用 new 假如今天我們需要 pass arg 到一個 class, 這個 arg 基本上是一個 instance, 但也有可能是 null 如果是 null 的話, 就必須要在 constructor 當中 initialize 這個 instance, 8.1 之後, 可以在 arg 中使用 new, 讓寫法更加精簡, 詳見以下範例:

// Before PHP 8.1
class BuyerWorkflow
{
public function __construct(
private null|WorkflowStepContract $step = null
) {
$this->step = new InitialBuyerStep();
}
}

// PHP 8.1
class BuyerWorkflow
{
public function __construct(
private WorkflowStepContract $step = new InitialBuyerStep(),
) {}
}

# Read-only Properties

過去, 我們要避免 class 中的 property 被改動, 通常我們必須要將它定義為 private, 但這樣同時我們也必須要另外定義 getter 來取得 property 的值 現在多了一個 read-only 選項, 我們可以將 property 定為 public, 同時它可以是 read-only 詳見以下範例:

// Before PHP 8.1
class Post
{
public function __construct() {
protected string $title,
protected string $content,
}

public function getTitle(): string
{
return $this->title;
}

public function getContent(): string
{
return $this->content;
}
}

// PHP 8.1
class Post
{
public function __construct() {
public readonly string $title,
public readonly string $content,
}
}

# 參考連結

https://laravel-news.com/modern-php-features-explained

--

--

Ray Lee | 李宗叡
Learn or Die

It's Ray. I do both backend and frontend, but more focus on backend. I like coding, and would like to see the whole picture of a product.