こんにちは。よっしーです(^^)
今日は、PHPのAttributes(属性)についてご紹介します。
背景
PHP8.2を利用したAPIを開発しているときにAttributes(属性)を利用したので、そのときの調査内容を備忘としてのこしました。
こちらのサイトを参考にしています。
概要
属性(Attributes)は、コード内の宣言に構造化された、機械可読なメタデータ情報を追加する能力を提供します。クラス、メソッド、関数、パラメータ、プロパティ、クラス定数は、属性の対象となります。属性によって定義されたメタデータは、実行時にReflection APIを使用して検査できます。したがって、属性はコードに直接埋め込まれた設定言語と考えることができます。
属性を使用することで、機能の一般的な実装とアプリケーション内での具体的な使用を切り離すことができます。これは、インターフェースとその実装と似ていますが、インターフェースと実装はコードに関するものであり、属性は追加情報と設定に注釈を付けることに関連しています。インターフェースはクラスに実装できますが、属性はメソッド、関数、パラメータ、プロパティ、クラス定数にも宣言できます。そのため、属性はインターフェースよりも柔軟です。
属性の使用の単純な例は、オプションのメソッドを持つインターフェースに属性を使用するように変換することです。アプリケーション内の操作を表すActionHandlerインターフェースを想像してみましょう。アクションハンドラのいくつかの実装にはセットアップが必要で、他の実装には必要ない場合があります。ActionHandlerを実装するすべてのクラスにsetUp()メソッドの実装を要求する代わりに、属性を使用できます。このアプローチの利点の一つは、属性を複数回使用できることです。
サンプルコード
<?php
interface ActionHandler
{
public function execute();
}
#[Attribute]
class SetUp {}
class CopyFile implements ActionHandler
{
public string $fileName;
public string $targetDirectory;
#[SetUp]
public function fileExists()
{
if (!file_exists($this->fileName)) {
throw new RuntimeException("File does not exist");
}
}
#[SetUp]
public function targetDirectoryExists()
{
if (!file_exists($this->targetDirectory)) {
mkdir($this->targetDirectory);
} elseif (!is_dir($this->targetDirectory)) {
throw new RuntimeException("Target directory $this->targetDirectory is not a directory");
}
}
public function execute()
{
copy($this->fileName, $this->targetDirectory . '/' . basename($this->fileName));
}
}
function executeAction(ActionHandler $actionHandler)
{
$reflection = new ReflectionObject($actionHandler);
foreach ($reflection->getMethods() as $method) {
$attributes = $method->getAttributes(SetUp::class);
if (count($attributes) > 0) {
$methodName = $method->getName();
$actionHandler->$methodName();
}
}
$actionHandler->execute();
}
$copyAction = new CopyFile();
$copyAction->fileName = "/tmp/foo.jpg";
$copyAction->targetDirectory = "/home/user";
executeAction($copyAction);
このコードは、PHP 8の属性(Attributes)を使用して、インターフェース、属性、クラスを組み合わせた例です。以下はコードの解説です。
interface ActionHandler
: ActionHandlerという名前のインターフェースを定義しています。このインターフェースにはexecute
メソッドが含まれています。#[Attribute] class SetUp {}
:SetUp
という属性クラスを定義しています。この属性は#[SetUp]
のようにメソッドに注釈として使用できるようになります。class CopyFile implements ActionHandler
:ActionHandler
インターフェースを実装したCopyFile
クラスを定義しています。このクラスはファイルのコピーを実行するアクションを表します。クラス内にはfileName
とtargetDirectory
の2つのプロパティがあります。#[SetUp] public function fileExists()
:fileExists
メソッドに#[SetUp]
属性が付いています。このメソッドはファイルの存在を確認し、存在しない場合にRuntimeException
をスローします。#[SetUp] public function targetDirectoryExists()
: 同様に、targetDirectoryExists
メソッドにも#[SetUp]
属性が付いています。このメソッドはターゲットディレクトリの存在を確認し、存在しない場合には新たにディレクトリを作成し、既存の場合にはディレクトリであることを確認します。public function execute()
:execute
メソッドはActionHandler
インターフェースで必要なメソッドで、ファイルのコピーを実行します。function executeAction(ActionHandler $actionHandler)
: アクションを実行するための関数を定義しています。この関数は、与えられたアクションハンドラ($actionHandler
)に対して、SetUp
属性が付いているメソッドを実行し、それからexecute
メソッドを実行します。これにより、事前のセットアップが行われ、アクションを実行できます。$copyAction = new CopyFile()
:CopyFile
クラスのインスタンスを作成し、$copyAction
に代入しています。$copyAction->fileName
および$copyAction->targetDirectory
にファイル名とターゲットディレクトリを設定しています。executeAction($copyAction)
:executeAction
関数を呼び出して、アクションを実行します。この際、SetUp
属性が付いているメソッド(fileExists
およびtargetDirectoryExists
)が事前に実行され、その後にファイルのコピーが行われます。
このコードは、属性を使用してアクションの事前セットアップを実現し、アクションの実行を行う方法を示しています。属性を利用することで、アクションハンドラの実装をより柔軟に構築できます。
おわりに
今日は、PHPのAttributes(属性)についてご紹介しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント