(PHP 8)
アトリビュートを使うと、 コンピューターが解析できる構造化されたメタデータの情報を、 コードの宣言時に埋め込むことができます。 つまり、クラス、メソッド、関数、プロパティ、クラス定数にアトリビュートを指定することができます。 アトリビュートで定義されたメタデータは、 実行時に リフレクションAPI を使って調べることが出来ます。 よって、アトリビュートは、 コードに直接埋め込むことが出来る、 設定のための言語とみなすことができます。
アトリビュートを使うと、機能の抽象的な実装と、アプリケーションでの具体的な利用を分離できます。 この点でアトリビュートは、インターフェイスとその実装と比較できます。 インターフェイスとその実装はコードに関する情報ですが、 アトリビュートはコードの追加情報と設定に注釈を付けるものです。 インターフェイスはクラスによって実装できますが、 アトリビュートはメソッドや関数、引数、プロパティ、クラス定数で宣言できます。 よって、アトリビュートはインターフェイスより柔軟です。
アトリビュートの使い方の簡単な例として、
必須でないメソッドを持つインターフェイスを、
アトリビュートを使うように変換するコードを示します。
アプリケーションの操作を表現する ActionHandler
があるとします。
この ActionHandler
の実装は、セットアップが必須なものもありますが、
そうでないものもあります。
ActionHandler
を実装する全てのクラスで
setUp()
メソッドを必須とする代わりに、
アトリビュートを使うようにしてみましょう。
このアプローチの利点のひとつは、
アトリビュートを複数回使えることです。
例1 アトリビュートを使い、インターフェイスのオプションのメソッドを実装する
<?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);