1
0
mirror of https://github.com/php/doc-ja.git synced 2026-03-23 22:52:11 +01:00
Files
archived-doc-ja/language/attributes.xml
2025-08-10 06:17:41 +09:00

396 lines
12 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 0f14761ba340c6e49797706ac3f0cf1147d97253 Maintainer: mumumu Status: ready -->
<chapter xml:id="language.attributes" xmlns="http://docbook.org/ns/docbook">
<title>アトリビュート</title>
<sect1 xml:id="language.attributes.overview">
<title>アトリビュートの概要</title>
<?phpdoc print-version-for="attributes"?>
<para>
PHP のアトリビュートは、クラス、メソッド、関数、パラメータ、プロパティ、定数に、
構造化され、かつマシンが読み取り可能なメタデータを提供します。
これらは <link linkend="book.reflection">リフレクション API</link> を介して実行時に検査でき、
コードを変更することなく動的な振る舞いを可能にします。
アトリビュートは、宣言的な方法でコードにメタデータの注釈を付ける方法を提供します。
</para>
<para>
アトリビュートは、機能の実装と利用を分離できるようにします。
インターフェイスがメソッドを強制することで構造を定義するのに対し、
アトリビュートはメソッド、関数、プロパティ、定数を含む、
複数の要素にわたってメタデータを提供します。
インターフェイスがメソッドの実装を強制するのとは異なり、
アトリビュートはコードの構造を変更することなく注釈を付けます。
</para>
<para>
アトリビュートは、強制された構造の代わりにメタデータを提供することで、
オプションのインターフェイスメソッドを補完または置き換えることができます。
アプリケーションでの操作を表す <literal>ActionHandler</literal> インターフェイスを考えてみましょう。
一部の実装ではセットアップ手順が必要な場合がありますが、必要ない場合もあります。
<literal>ActionHandler</literal> を実装するすべてのクラスに
<literal>setUp()</literal> メソッドの定義を強制する代わりに、
アトリビュートを使用してセットアップ要件を示すことができます。
このアプローチは柔軟性を高め、必要に応じてアトリビュートを複数回適用できます。
</para>
<example>
<title>アトリビュートを使い、インターフェイスのオプションのメソッドを実装する</title>
<programlisting role="php">
<![CDATA[
<?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);
]]>
</programlisting>
</example>
</sect1>
<sect1 xml:id="language.attributes.syntax">
<title>アトリビュートの文法</title>
<para>
アトリビュートの文法は、いくつかの重要な要素で構成されています。
アトリビュートの宣言は <literal>#[</literal> で始まり、<literal>]</literal> で終わります。
その内部には、1つ以上のアトリビュートをカンマで区切って並べることができます。
アトリビュート名は、<link linkend="language.namespaces.basics">名前空間の基礎</link> で説明されているように、
非修飾名、修飾名、または完全修飾名にすることができます。
アトリビュートへの引数はオプションで、括弧 <literal>()</literal> で囲みます。
引数にはリテラル値または定数式のみを指定できます。
位置引数と名前付き引数の両方の構文がサポートされています。
</para>
<para>
アトリビュート名とその引数はクラスに解決され、
リフレクション API を介してアトリビュートのインスタンスが要求されると、
引数がそのコンストラクタに渡されます。
したがって、各アトリビュートに対してクラスを導入することをお勧めします。
</para>
<example>
<title>アトリビュートの文法</title>
<programlisting role="php">
<![CDATA[
<?php
// a.php
namespace MyExample;
use Attribute;
#[Attribute]
class MyAttribute
{
const VALUE = 'value';
private $value;
public function __construct($value = null)
{
$this->value = $value;
}
}
// b.php
namespace Another;
use MyExample\MyAttribute;
#[MyAttribute]
#[\MyExample\MyAttribute]
#[MyAttribute(1234)]
#[MyAttribute(value: 1234)]
#[MyAttribute(MyAttribute::VALUE)]
#[MyAttribute(array("key" => "value"))]
#[MyAttribute(100 + 200)]
class Thing
{
}
#[MyAttribute(1234), MyAttribute(5678)]
class AnotherThing
{
}
]]>
</programlisting>
</example>
</sect1>
<sect1 xml:id="language.attributes.reflection">
<title>リフレクションAPI を使ってアトリビュートを読み取る</title>
<para>
クラス、メソッド、関数、パラメータ、プロパティ、クラス定数からアトリビュートにアクセスするには、
リフレクション API が提供する <function>getAttributes</function> メソッドを使用します。
このメソッドは <classname>ReflectionAttribute</classname> インスタンスの配列を返します。
これらのインスタンスは、アトリビュート名、引数を問い合わせることができ、
表現されたアトリビュートのインスタンスをインスタンス化するために使用できます。
</para>
<para>
リフレクションされたアトリビュートの表現を実際のインスタンスから分離することで、
存在しないアトリビュートクラス、型指定ミス、値の不足など、
エラー処理をより詳細に制御できます。
アトリビュートクラスのオブジェクトは <function>ReflectionAttribute::newInstance</function>
を呼び出した後にのみインスタンス化され、その時点で引数の検証が行われます。
</para>
<example>
<title>リフレクションAPIを使い、アトリビュートを読み取る</title>
<programlisting role="php">
<![CDATA[
<?php
#[Attribute]
class MyAttribute
{
public $value;
public function __construct($value)
{
$this->value = $value;
}
}
#[MyAttribute(value: 1234)]
class Thing
{
}
function dumpAttributeData($reflection) {
$attributes = $reflection->getAttributes();
foreach ($attributes as $attribute) {
var_dump($attribute->getName());
var_dump($attribute->getArguments());
var_dump($attribute->newInstance());
}
}
dumpAttributeData(new ReflectionClass(Thing::class));
/*
string(11) "MyAttribute"
array(1) {
["value"]=>
int(1234)
}
object(MyAttribute)#3 (1) {
["value"]=>
int(1234)
}
*/
]]>
</programlisting>
</example>
<para>
リフレクションインスタンスのすべてのアトリビュートをループする代わりに、
アトリビュートクラス名を引数として渡すことで、
特定のアトリビュートのクラスだけを取得できます。
</para>
<example>
<title>リフレクションAPIを使い、特定のアトリビュートを読み取る</title>
<programlisting role="php">
<![CDATA[
<?php
function dumpMyAttributeData($reflection) {
$attributes = $reflection->getAttributes(MyAttribute::class);
foreach ($attributes as $attribute) {
var_dump($attribute->getName());
var_dump($attribute->getArguments());
var_dump($attribute->newInstance());
}
}
dumpMyAttributeData(new ReflectionClass(Thing::class));
]]>
</programlisting>
</example>
</sect1>
<sect1 xml:id="language.attributes.classes">
<title>アトリビュートクラスを宣言する</title>
<para>
アトリビュートごとに個別のクラスを定義することをお勧めします。
最も単純なケースでは、<literal>#[Attribute]</literal> 宣言を持つ空のクラスで十分です。
このアトリビュートは、<literal>use</literal> 文を使用してグローバル名前空間からインポートできます。
</para>
<example>
<title>単純なアトリビュートクラスの例</title>
<programlisting role="php">
<![CDATA[
<?php
namespace Example;
use Attribute;
#[Attribute]
class MyAttribute
{
}
]]>
</programlisting>
</example>
<para>
アトリビュートを適用できる宣言の種類を制限するには、
<literal>#[Attribute]</literal> 宣言の最初の引数としてビットマスクを渡します。
</para>
<example>
<title>アトリビュートを使える場所を限定するために、ターゲットとなるクラスを作る</title>
<programlisting role="php">
<![CDATA[
<?php
namespace Example;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION)]
class MyAttribute
{
}
]]>
</programlisting>
<para>
<classname>MyAttribute</classname> を別の型で宣言すると、
<function>ReflectionAttribute::newInstance</function> の呼び出し時に例外がスローされます。
</para>
</example>
<para>ビットマスクには、以下が指定できます:</para>
<simplelist>
<member><constant>Attribute::TARGET_CLASS</constant></member>
<member><constant>Attribute::TARGET_FUNCTION</constant></member>
<member><constant>Attribute::TARGET_METHOD</constant></member>
<member><constant>Attribute::TARGET_PROPERTY</constant></member>
<member><constant>Attribute::TARGET_CLASS_CONSTANT</constant></member>
<member><constant>Attribute::TARGET_PARAMETER</constant></member>
<member><constant>Attribute::TARGET_ALL</constant></member>
</simplelist>
<para>
デフォルトでは、アトリビュートは宣言ごとに1回しか使用できません。
アトリビュートを繰り返し可能にするには、
<literal>#[Attribute]</literal> 宣言のビットマスクで
<constant>Attribute::IS_REPEATABLE</constant> フラグを指定します。
</para>
<example>
<title>宣言時にアトリビュートを複数回使えるように、IS_REPEATABLE を使う</title>
<programlisting role="php">
<![CDATA[
<?php
namespace Example;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::IS_REPEATABLE)]
class MyAttribute
{
}
]]>
</programlisting>
</example>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->