前書き
マジックメソッドは「ある動作がオブジェクトに対して行われた場合に、 PHP のデフォルトの動作を上書きする」事ができます。
https://www.php.net/manual/ja/language.oop5.magic.php
マジックメソッドを上手く使いこなすとコードの動作に対して色々と介入する事ができるので、やりたいことがより簡単に記述できるようになったり、或いは出来ないと思っていた事が出来るようになったりするので、とても便利です。
特に「ライブラリ(共通ルーチン / フレームワーク / ツール)」を作るような時には、機能にもよりますが「必須」に近い機能も多いので、しっかりと押さえておくとよいでしょう。
それでは、マジックメソッドをそれぞれ、見ていきましょう。
__construct() と __destruct()
https://www.php.net/manual/ja/language.oop5.decon.php
__construct()
はコンストラクタ、 __destruct()
はデストラクタの実装です。
コンストラクタは「オブジェクト(インスタンス)を作成するタイミング」で、デストラクタは「オブジェクト(インスタンス)が破棄されるタイミング」でそれぞれ動きます。
コンストラクタは一般的に「そのオブジェクト(インスタンス)に必要な初期化」を、デストラクタは「そのオブジェクト(インスタンス)で確保したリソースの解放」を行う事が多いです。
デストラクタの考え方については、RAII(Resource Acquisition Is Initialization)を調べてみるのもよいでしょう。デストラクタでリソースを解放しない場合、try-catchのfinallyで書く事が多いです。
コンストラクタは引数を持つ事ができます。
「引数があり、初期化をする」コンストラクタの場合、「コンストラクタのプロモーション」が便利な書き方になります。
コンストラクタではreturnをすることが出来ないため、エラーの場合は例外を投げます。
子クラスがコンストラクタを有している場合、親クラスのコンストラクタが暗黙の内にコールされることはありません。親クラスのコンストラクタの起動には、明示的に parent::__construct()
の記述が必要です。
子クラスでオーバライドした時、(他の通常のメソッドと異なり)シグネチャの互換性に関するルール は適用されません。
コンストラクタは通常 public
ですが、protected
や private
にする事もできます。protected
や private
にした場合、クラスの外側から new をする事はできなくなります。
「古いスタイルのコンストラクタ」は、PHP8以降は「警告も何も出ず、単に無視される」ので、古いコードの場合は気をつけるとよいでしょう。
デストラクタは引数を持つ事ができません。
また、デストラクタは「順不同」でcallされます。
子クラスがデストラクタを有している場合、親クラスのデストラクタが暗黙の内にコールされることはありません。親クラスのデストラクタの起動には、明示的に parent::__destruct()
の記述が必要です。
デストラクタ外のコードで exit()
がcallされた場合でもデストラクタは動きますが、デストラクタ内で exit()
がcallされると、処理はそこで停止します。
__call() と __callStatic()
https://www.php.net/manual/ja/language.oop5.overloading.php#object.call
__call()
と __callStatic()
は、アクセス不能メソッドが呼び出された時に起動します。
オブジェクト(インスタンス)のコンテキストから呼ばれると __call()
が、static メソッドのコンテキストから呼ばれると __callStatic()
が起動します。
__get() と __set()
https://www.php.net/manual/ja/language.oop5.overloading.php#object.set
アクセス不能プロパティへの読み込みや書き込みのタイミングで起動します。
読み込み時に __get()
、書き込み時に __set()
が起動します。
static なプロパティでは __get()
や __set()
は起動しません。
__isset() と __unset()
https://www.php.net/manual/ja/language.oop5.overloading.php#object.isset
アクセス不能プロパティに対して isset()
または empty()
を使うと、 __isset()
が起動します。
アクセス不能プロパティに対して unset()
を使うと、 __unset()
が起動します。
__clone()
https://www.php.net/manual/ja/language.oop5.cloning.php#object.clone
オブジェクト(インスタンス)を clone
する時に起動します。
これは「新規に作成された」側のオブジェクト(インスタンス)で起動されるので、プロパティに必要な編集を行う事などができます。
「外からcloneさせたくない」といった意図で、アクセス権を public 以外にする事もできます。コンストラクタが同じ仕様を持っているので、確認をしてみましょう。
__sleep() と __wakeup() 、__serialize() と __unserialize()
https://www.php.net/manual/ja/language.oop5.magic.php#object.sleep
https://www.php.net/manual/ja/language.oop5.magic.php#object.serialize
__sleep()
と __serialize()
は、インスタンスをserializeするタイミングで起動します。__wakeup()
と __unserialize()
は、インスタンスをunserializeするタイミングで起動します。
__sleep()
と __serialize()
は基本的に「どちらか片方のみが定義されている」事が期待されています。両方とも定義されている場合、__serialize()
のみが起動します。__wakeup()
と __unserialize()
は基本的に「どちらか片方のみが定義されている」事が期待されています。両方とも定義されている場合、__unserialize()
のみが起動します。
__toString()
https://www.php.net/manual/ja/language.oop5.magic.php#object.tostring
オブジェクト(インスタンス)が文字列にキャストされる時の挙動を決める事ができます。
Stringable インタフェースは、マジックメソッド __toString() が定義されているあらゆるクラスで 暗黙のうちに存在すると見なされます(が、「明示するほうがよい」とマニュアルに記載されています) https://www.php.net/manual/ja/class.stringable.php 。
__invoke()
https://www.php.net/manual/ja/language.oop5.magic.php#object.invoke
オブジェクト(インスタンス)が関数としてcallされる時の挙動を決める事ができます。
__set_state()
https://www.php.net/manual/ja/language.oop5.magic.php#object.set-state
var_export()
によって エクスポートされたクラスのためにコールされます。
このメソッドは「var_export()
がcallされた時」に呼ばれるメソッドではなく、「var_export()
によって作成される”有効な PHP コード”に含まれるメソッド名」になります。
__debugInfo()
https://www.php.net/manual/ja/language.oop5.magic.php#object.debuginfo
var_dump()
がcallされた時の出力内容を制御する事ができます。
このコラムに関連するコードはこちらになります
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/034_1.php
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/034_2.php
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/034_3.php
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/034_4.php