1
0
mirror of https://github.com/php/doc-ja.git synced 2026-04-28 02:23:18 +02:00
Files
archived-doc-ja/language/oop5/decon.xml
T
2025-08-28 19:57:49 +09:00

429 lines
18 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 82fc6a1c8670b96f1bd2b40932b6eb19929f4f6f Maintainer: hirokawa Status: ready -->
<!-- CREDITS: shimooka,mumumu -->
<sect1 xml:id="language.oop5.decon" xmlns="http://docbook.org/ns/docbook">
<title>コンストラクタとデストラクタ</title>
<sect2 xml:id="language.oop5.decon.constructor">
<title>コンストラクタ</title>
<methodsynopsis xml:id="object.construct">
<type>void</type><methodname>__construct</methodname>
<methodparam rep="repeat"><type>mixed</type><parameter>values</parameter><initializer>""</initializer></methodparam>
</methodsynopsis>
<para>
PHP では、開発者がクラスのコンストラクタメソッドを宣言することが
できます。コンストラクタメソッドを有するクラスは、新たにオブジェクトが
生成される度にこのメソッドをコールします。これにより、
そのオブジェクトを使用する前に必要な初期化を行うことができます。
</para>
<note>
<simpara>
子クラスがコンストラクタを有している場合、親クラスのコンストラクタが
暗黙の内にコールされることはありません。
親クラスのコンストラクタを実行するには、子クラスのコンストラクタの
中で <function>parent::__construct</function> をコールすることが
必要です。
子クラスでコンストラクタを定義していない場合は、親クラスのコンストラクタを継承します
(ただし、private 宣言されている場合は除く)。
これは、通常のクラスメソッドと同様です。
</simpara>
</note>
<example>
<title>継承とコンストラクタ</title>
<programlisting role="php">
<![CDATA[
<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}
class OtherSubClass extends BaseClass {
// BaseClass のコンストラクタを継承します
}
// In BaseClass constructor
$obj = new BaseClass();
// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();
// In BaseClass constructor
$obj = new OtherSubClass();
?>
]]>
</programlisting>
</example>
<para>
他のメソッドと異なり、<link linkend="object.construct">__construct()</link>
を子クラスでオーバライドしても、
<link linkend="language.oop.lsp">シグネチャの互換性に関するルール</link> は適用されません。
</para>
<para>
コンストラクタは、対応するオブジェクトを初期化する間に呼び出されるメソッドです。
よって、任意の数の引数を取ることが出来ます。
この引数は必須にすることもできますし、型宣言もできますし、デフォルト値を取ったりすることもできます。
コンストラクタの引数は、クラス名の後の括弧に、引数を置くことで指定することが出来ます。
</para>
<example>
<title>コンストラクタを引数と一緒に使う</title>
<programlisting role="php">
<![CDATA[
<?php
class Point {
protected int $x;
protected int $y;
public function __construct(int $x, int $y = 0) {
$this->x = $x;
$this->y = $y;
}
}
// 引数を両方渡す
$p1 = new Point(4, 5);
// 必須の引数のみを渡す。$y はデフォルト値0になります。
$p2 = new Point(4);
// 名前付き引数(PHP 8.0 以降):
$p3 = new Point(y: 5, x: 4);
?>
]]>
</programlisting>
</example>
<para>
クラスにコンストラクタが存在しない場合、あるいは、コンストラクタに必須の引数がない場合、
括弧は省略できます。
</para>
<sect3>
<title>古いスタイルのコンストラクタ</title>
<para>
PHP 8.0.0 より前のバージョンでは、グローバル名前空間にあるクラスは、
クラス名と同じ名前のメソッドが古いスタイルのコンストラクタとして解釈されます。
この文法は推奨されておらず、<constant>E_DEPRECATED</constant> が発生するものの、
まだこの関数をコンストラクタとして呼び出すことが出来ます。
<link linkend="object.construct">__construct()</link>
とクラス名と同じ名前のメソッドが両方定義されていた場合は、
<link linkend="object.construct">__construct()</link> がコンストラクタとして呼び出されます。
</para>
<para>
名前空間の中に存在するクラスについては、
PHP 8.0.0 以降では、
クラス名と同じ名前のメソッドはなんの意味も持ちません。
</para>
<para>新しいコードでは、常に <link linkend="object.construct">__construct()</link> を使うようにしましょう。
</para>
</sect3>
<sect3 xml:id="language.oop5.decon.constructor.promotion">
<title>コンストラクタのプロモーション</title>
<para>
PHP 8.0.0 以降では、コンストラクタの引数を
対応するオブジェクトのプロパティに昇格させることができます。
コンストラクタの引数をプロパティに代入し、それ以外の操作を行わないことはよくあることです。
コンストラクタのプロモーションは、こういった場合の短縮記法を提供します。
「コンストラクタを引数と一緒に使う」の例は、次のように書き直すことが出来ます。
</para>
<example>
<title>コンストラクタのプロモーションを使う</title>
<programlisting role="php">
<![CDATA[
<?php
class Point {
public function __construct(protected int $x, protected int $y = 0) {
}
}
]]>
</programlisting>
</example>
<para>
コンストラクタの引数に修飾子が含まれている場合、
PHP はそれをオブジェクトのプロパティ、かつコンストラクタの引数であると解釈します。
そして、その引数の値をプロパティに代入します。
コンストラクタの本体は空にすることもできますし、
他の文を含めることも出来ます。
引数の値が対応するプロパティに代入された後、
追加の文が実行されます。
</para>
<para>
全ての引数をプロパティに昇格させる必要はありません。
昇格させる引数と、させない引数を混ぜることもできます。
プロパティに昇格した引数は、コンストラクタの呼び出しコードになんの影響も与えません。
</para>
<note>
<para>
<link linkend="language.oop5.visibility">アクセス権</link>
の修飾子 (<literal>public</literal>,
<literal>protected</literal>, <literal>private</literal>)
を使うことが、プロパティの昇格を行わせるもっとも適したやり方ですが、
他にも (<literal>readonly</literal> のような) 単一の修飾子を使っても、
同じ昇格の効果が得られます。
</para>
</note>
<note>
<para>
PHP のエンジンが曖昧になってしまうため、
オブジェクトのプロパティは、<type>callable</type> 型にすることは出来ません。
そのため、昇格させる引数も <type>callable</type> 型にはできません。
しかし、それ以外の
<link linkend="language.types.declarations">型宣言</link> は許されています。
</para>
</note>
<note>
<para>
コンストラクタで昇格したプロパティはプロパティと関数のパラメータ両方に戻されるので、プロパティとパラメータの両方に対するあらゆる命名規則が適用されます。
</para>
</note>
<note>
<para>
昇格したコンストラクタの引数に指定された
<link linkend="language.attributes">アトリビュート</link> は、
プロパティと引数の両方にコピーされます。
昇格したコンストラクタの引数に指定されたデフォルト値は、
引数にだけコピーされます。プロパティにはコピーされません。
</para>
</note>
</sect3>
<sect3 xml:id="language.oop5.decon.constructor.new">
<title>初期化時の new キーワード</title>
<para>
PHP 8.1.0 以降では、
引数のデフォルト値の初期化時、
static 変数の初期化時、
グローバルな定数の初期化時に、
new を指定したオブジェクトが使えます。
同じものを、アトリビュートの引数や
<function>define</function> に渡せるようにもなっています。
</para>
<note>
<para>
この文脈で、動的なクラス名、文字列でないクラス名、
無名クラスを指定することはできません。
また、配列で引数を展開させることはできません。
未サポートの式を引数に指定することもできません。
</para>
</note>
<example>
<title>初期化時に new キーワードを使う</title>
<programlisting role="php" annotations="non-interactive">
<![CDATA[
<?php
// 以下は全て許可されます
static $x = new Foo;
const C = new Foo;
function test($param = new Foo) {}
#[AnAttribute(new Foo)]
class Test {
public function __construct(
public $prop = new Foo,
) {}
}
// 以下はいずれも許されません(コンパイルエラーになります)
function test(
$a = new (CLASS_NAME_CONSTANT)(), // 動的なクラス名
$b = new class {}, // 無名クラス
$c = new A(...[]), // 引数の展開
$d = new B($abc), // 定数式の展開はサポートされていません
) {}
?>
]]>
</programlisting>
</example>
</sect3>
<sect3 xml:id="language.oop5.decon.constructor.static">
<title>static な生成メソッド</title>
<para>
PHP は、クラスひとつにつき、コンストラクタをひとつだけサポートしています。
しかし、場合によっては異なる入力を使い、
異なるやり方でオブジェクトを生成させるのが望ましい場合もあります。
その場合におすすめなのが、staticメソッドをコンストラクタのラッパーとして使うことです。
</para>
<example>
<title>static な生成メソッドを使う</title>
<programlisting role="php">
<![CDATA[
<?php
$some_json_string = '{ "id": 1004, "name": "Elephpant" }';
$some_xml_string = "<animal><id>1005</id><name>Elephpant</name></animal>";
class Product {
private ?int $id;
private ?string $name;
private function __construct(?int $id = null, ?string $name = null) {
$this->id = $id;
$this->name = $name;
}
public static function fromBasicData(int $id, string $name): static {
$new = new static($id, $name);
return $new;
}
public static function fromJson(string $json): static {
$data = json_decode($json, true);
return new static($data['id'], $data['name']);
}
public static function fromXml(string $xml): static {
$data = simplexml_load_string($xml);
$new = new static();
$new->id = (int) $data->id;
$new->name = $data->name;
return $new;
}
}
$p1 = Product::fromBasicData(5, 'Widget');
$p2 = Product::fromJson($some_json_string);
$p3 = Product::fromXml($some_xml_string);
var_dump($p1, $p2, $p3);
]]>
</programlisting>
</example>
<para>
コンストラクタは外部から呼ばれることを防ぐため、private または protected にしておきます。
この場合、staticメソッドだけがクラスをインスタンス化するのに使えます。
なぜなら、staticメソッドは、同じオブジェクトのインスタンスでなくとも、
private なメソッドにアクセスできる同じクラスのメソッド定義として存在するからです。
コンストラクタを private にすることはオプションです。使い方によっては意味がないかもしれません...
</para>
<para>
上の public なstaticメソッドは、オブジェクトをインスタンス化する3つの異なるやり方を示しています。
</para>
<simplelist>
<member><code>fromBasicData()</code> は、必要となる必須の引数を取り、
コンストラクタを呼ぶことでオブジェクトを生成し、その結果を返します。</member>
<member><code>fromJson()</code> は、JSON文字列を受け取り、
コンストラクタで必要なフォーマットに変換するための前処理を行います。
その上で、新しいオブジェクトを返します。</member>
<member><code>fromXml()</code> は、XML文字列を受け取り、前処理をします。
そして生のオブジェクトを生成します。コンストラクタは呼び出されていますが、
全ての引数はオプションなので、その実行はスキップされます。
結果を返す前に、オブジェクトのプロパティに直接値を代入しています。</member>
</simplelist>
<para>
上の3つの場合全てで、<code>static</code> キーワードはコードが存在するクラスの名前として解釈されます。
この場合は <code>Product</code> です。
</para>
</sect3>
</sect2>
<sect2 xml:id="language.oop5.decon.destructor">
<title>デストラクタ</title>
<methodsynopsis xml:id="object.destruct">
<type>void</type><methodname>__destruct</methodname>
<void />
</methodsynopsis>
<para>
PHP には、C++ のような他のオブジェクト指向言語に似たデストラクタの概念があります。
デストラクタメソッドは、
特定のオブジェクトを参照するリファレンスがひとつもなくなったときにコールされます。
あるいは、スクリプトの終了時にも順不同でコールされます。
</para>
<example>
<title>デストラクタの例</title>
<programlisting role="php">
<![CDATA[
<?php
class MyDestructableClass
{
function __construct() {
print "In constructor\n";
}
function __destruct() {
print "Destroying " . __CLASS__ . "\n";
}
}
$obj = new MyDestructableClass();
]]>
</programlisting>
</example>
<para>
コンストラクタと同様、親クラスのデストラクタがエンジンにより暗黙のうちに
コールされるということはありません。親クラスのデストラクタを実行するには、
デストラクタの中で明示的に <function>parent::__destruct</function>
をコールする必要があります。
また、コンストラクタと同様、子クラスでデストラクタを定義していない場合は
親クラスのデストラクタを継承します。
</para>
<para>
<function>exit</function>
でスクリプトの実行を止めた場合にもデストラクタはコールされます。
デストラクタの内部で <function>exit</function> をコールすると、
それ以降のシャットダウンルーチンを実行しません。
</para>
<note>
<para>
スクリプトのシャットダウン時にデストラクタがコールされた場合は、
HTTP ヘッダはすでに送信されています。スクリプトのシャットダウン時の作業ディレクトリは、
SAPI によっては (たとえば Apache など) 異なります。
</para>
<para>
デストラクタが自身のオブジェクトへの新しい参照を作成した場合、参照カウントが再びゼロになったときや
シャットダウンシーケンス中に、再度呼び出されることはありません。
</para>
<para>
PHP 8.4.0 以降、 <link linkend="features.gc.collecting-cycles">ガベージサイクルの収集</link>
<link linkend="language.fibers">ファイバー</link>の実行中に発生した場合、
回収がスケジュールされたオブジェクトのデストラクタは、 <literal>gc_destructor_fiber</literal>と呼ばれる
別のファイバー内で実行されます。
このファイバーが中断された場合、残りのデストラクタを実行するために新しいファイバーが作成されます。
中断された <literal>gc_destructor_fiber</literal> はガベージコレクタによって参照されなくなり、
他の参照がなければ回収される可能性があります。
デストラクタが中断されたオブジェクトは、デストラクタが復帰するかファイバー自体が回収されるまで回収されません。
</para>
</note>
<note>
<para>
デストラクタの中から (スクリプトの終了処理時に)
例外をスローしようとすると、致命的なエラーを引き起こします。
</para>
</note>
</sect2>
</sect1>
<!-- 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
-->