クラスの基礎
class
各クラスの定義は、classキーワードで始まり、クラス名が続きます。
そしてその後に波括弧のペアが続き、
その中にはクラスのプロパティとメソッドの定義を記述します。
クラス名には、PHP の予約語
以外でラベルとして有効なあらゆる名前を使用することができます。
PHP 8.4.0 以降では、アンダースコア (_)
1文字のみのクラス名は非推奨となりました。
有効なクラス名は、先頭が文字あるいはアンダースコアで始まり、
その後に任意の数の文字/数字/アンダースコアが続くものです。
正規表現で表すと、
^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$
のようになります。
クラスの中には、
定数 や
変数
("プロパティ" といいます) そして関数 ("メソッド" といいます)
を含めることができます。
簡単なクラス定義
var;
}
}
?>
]]>
メソッドがオブジェクトのコンテキストからコールされる場合は、
疑似変数 $this が利用可能です。
$this は、呼び出し元オブジェクトの値です。
static でないメソッドを static メソッドとしてコールすると、
Error がスローされます。
PHP 8.0.0 より前のバージョンでは、推奨されない警告が発生し、
$this が未定義になっていました。
$this 疑似変数の例
foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>
]]>
&example.outputs.7;
&example.outputs.8;
読み取り専用クラス
PHP 8.2.0 以降では、
クラスに対して readonly を指定することができます。
クラスに対して readonly を指定すると、
宣言されている全ての プロパティに対して readonly を指定した ことになり、
かつ 動的なプロパティ の作成を禁止したことになります。
さらに、AllowDynamicProperties
アトリビュートを指定しても動的なプロパティを作成できなくなります。
動的なプロパティを作成しようとすると、コンパイル時にエラーが発生します。
]]>
型を指定していないプロパティや、
static プロパティ に対しては readonly を指定できません。
readonly クラスには、それらをいずれも指定できません:
]]>
]]>
readonly を指定したクラスは、
子クラスでも readonly を指定した場合にのみ
継承 できます。
new
あるクラスのインスタンスを生成するには、new
キーワードを使わなければなりません。エラー時に
例外をスローするような
コンストラクタを定義していない限り、
オブジェクトが常に生成されます。
クラスは、そのインスタンスを作成する前に定義すべきです
(これが必須となる場合もあります)。
クラス名を含む文字列を new で指定すると、
そのクラスのインスタンスを作成します。クラスが名前空間に属している場合は、
完全修飾名を指定しなければなりません。
クラスのコンストラクタに引数を渡さなかった場合、
クラス名の後の括弧は省略しても構いません。
インスタンスを作成する
]]>
PHP 8.0.0 以降では、
new を任意の式と一緒に使う機能がサポートされました。
これによって、式が文字列を生成する場合に、
より複雑なインスタンス化を行えるようになります。
式は括弧で囲まなければいけません。
任意の式を使ってインスタンスを生成する
以下の例では、
クラス名を生成する有効な任意の式を複数示します。
関数呼び出し、文字列連結、そして
::class 定数です。
]]>
&example.outputs.8;
クラスのコンテキストにおいては、
new self や new parent
のようにして新しいオブジェクトを作成することができます。
作成済みのクラスのインスタンスを新たな変数に代入する場合、新しい変数は、
代入されたオブジェクトと同じインスタンスにアクセスします。
この動作は、インスタンスを関数に渡す場合も同様です。
作成済みのオブジェクトのコピーは、その
クローンを作成
することにより作成可能です。
オブジェクトの代入
var = '$assigned will have this value';
$instance = null; // $instance と $reference は null になります
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>
]]>
&example.outputs;
string(30) "$assigned will have this value"
}
]]>
複数のやり方で、オブジェクトのインスタンスを作ることが出来ます:
新しいオブジェクトの作成
]]>
&example.outputs;
新しく作成したオブジェクトのメンバーに、作成したその式の中でもアクセスすることができます。
新しく作成したオブジェクトのメンバーへのアクセス
format('Y'), PHP_EOL;
// PHP 8.4.0 以降、囲む括弧は省略可能です。
echo new DateTime()->format('Y'), PHP_EOL;
?>
]]>
&example.outputs.similar;
PHP 7.1 より前のバージョンでは、コンストラクタが定義されない場合、それへの引数が評価されていませんでした。
プロパティとメソッド
クラスのプロパティとメソッドは、それぞれ別の "名前空間" に存在するので、
同じ名前のプロパティとメソッドを共存させることもできます。
プロパティを参照する場合もメソッドを参照する場合も書きかたは同じです。
それがプロパティへのアクセスなのかメソッドの呼び出しなのかは、そのコンテキストによって決まります。
つまり、変数にアクセスしようとしているのか関数を呼び出そうとしているのかの違いです。
プロパティへのアクセスとメソッドの呼び出し
bar, PHP_EOL, $obj->bar(), PHP_EOL;
]]>
&example.outputs;
これはつまり、プロパティに 無名関数
を代入した場合に、その関数は直接呼び出せないということです。
その場合は、たとえば事前にプロパティを変数に代入しておく必要があります。
括弧で囲むと、プロパティを直接呼び出すこともできます。
プロパティに格納した無名関数の呼び出し
bar = function() {
return 42;
};
}
}
$obj = new Foo();
echo ($obj->bar)(), PHP_EOL;
]]>
&example.outputs;
extends
クラスは、宣言部に extends キーワードを含めることで、
他のクラスの定数や、メソッド、
およびプロパティを継承することができます。多重継承を行うことはできず、クラスが継承できるベース
クラスは一つだけです。
継承された定数やメソッド、プロパティをオーバーライド(上書き)するには、
親クラスで定義されているのと同じ名前でそれを再宣言します。
しかし、親クラスでそのメソッドや定数が
final
として定義されている場合はオーバーライドできません。
オーバーライドされた元のメソッドやstaticプロパティにアクセスするには、
parent::
で参照します。
PHP 8.1.0 以降では、定数も final として宣言できます。
簡単なクラスの継承
displayVar();
?>
]]>
&example.outputs;
シグネチャの互換性に関するルール
メソッドをオーバーライドするときは、
子クラスのシグネチャが親クラスのそれと互換性がなければいけません。
互換性が壊れた場合、致命的なエラーが発生します。
PHP 8.0.0 より前のバージョンでは、
互換性が壊れた場合に、E_WARNING レベルの警告が発生していました。
共変性と反変性
の規則を守っている場合は、シグネチャに互換性があります。
必須の引数をオプションにした場合も、互換性があります。
新しいオプションの引数を追加しただけで、アクセス権を厳しくせず、
緩めただけの場合も互換性があります。
これは、リスコフの置換原則(Liskov Substitution Principle)、
略して LSP として知られています。
但し、コンストラクタ
と private メソッドについては、
この規則の例外で、
オーバライドしたシグネチャにミスマッチがあっても致命的なエラーにはなりません。
互換性がある子クラスのメソッド
foo();
$extended2 = new Extend2();
$extended2->foo(1);
]]>
&example.outputs;
次の例は、引数を削除した子クラスのメソッドや、
オプションの引数を必須にしたりすることが、親クラスのメソッドと互換性がなくなることを示しています。
子クラスのメソッドで引数を削除すると致命的なエラーになる
&example.outputs.8.similar;
子クラスのメソッドで、オプションの引数を必須にすると致命的なエラーになる
&example.outputs.8.similar;
メソッドの引数の名前を子クラスで変更しても、
シグネチャ上は非互換になりません。
しかし、こうしてしまうと
名前付き引数
を使った時に実行時エラーになるので、おすすめできません。
子クラスで引数の名前を変更し、かつ名前付き引数を使うとエラーになる
test(foo: "foo", bar: "bar"); // エラー発生!
]]>
&example.outputs.similar;
::class
class キーワードでもクラス名の解決を行うことが出来ます。
クラスの名前が ClassName になっているクラスの完全修飾名を取得するには、
ClassName::class を使います。
これは、名前空間付きのクラスに使うと特に便利です。
クラス名の解決
]]>
&example.outputs;
::class によるクラス名の解決は、コンパイル時の変換です。
つまり、クラス名を作るタイミングでは、まだオートロードが行われていないということです。
結果的に、クラスがまだ存在しない時点でクラス名が展開されることになります。
この場合にはエラーは発生しません。
クラス名が存在しない場合の名前解決
]]>
&example.outputs;
PHP 8.0.0 以降では、
::class はオブジェクトに対しても使えるようになりました。
この名前解決はコンパイル時ではなく、実行時に行われます。
この場合、get_class をオブジェクトに対して使った時と同じ動きをします。
オブジェクトの名前解決
]]>
&example.outputs;
nullsafe メソッドとプロパティ
PHP 8.0.0 以降では、プロパティやメソッドは
"nullsafe" 演算子 ?-> を使ってアクセスすることもできます。
nullsafe 演算子は既に述べたメソッドやプロパティと同じように振る舞いますが、
オブジェクトが &null; と評価された場合に、例外はスローされず、
&null; が返される点だけが異なります。
オブジェクトの評価がチェインの一部だった場合は、
残りのチェインはスキップされます。
この演算子の働きは、
オブジェクトにアクセスするたびに is_null でラップするコードに似ています。
しかし、よりコンパクトです。
nullsafe 演算子
getUser(5)?->name;
// これは、以下のコードチェックと同等です:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>
]]>
nullsafe 演算子は、プロパティやメソッドが値を返す際、
null が正しく、期待されうる値とみなせる場合に一番よく使います。
エラーを示すためなら、例外をスローするほうが好ましいです。