PHP8では、クラスのメソッドの戻り値でしか使えないstatic型というものが追加されました。
一応、ドキュメントの説明でなんとなくは分かりましたが、いくつか気になるところが。
何をするもので、何ができないのかを見ていきます。
そしてややこしい。'static' というワード使いすぎ。PHPは。
自クラスのインスタンスを返さなければならない型
まずは、PHPドキュメントの文言を見てもらいましょう。
値が、メソッドが呼び出されているクラスと同じインスタンスでなければなりません。 PHP 8.0.0 以降で利用できます。
PHP公式ドキュメントより
説明はこれだけ。
ただし、自インスタンスを返すための型だということは分かります。
<?php
class Test
{
public string $prop = 'initial';
public function get_instance() : static
{
return $this;
}
}
$test = new Test();
var_dump($test->get_instance());
object(Test)#1 (1) {
["prop"]=>
string(7) "initial"
}
以上。となれば簡単で説明するほどでもないんですが、もう少し考えてみましょう。
ドキュメントには『メソッドが呼び出されているクラスと同じインスタンス』とあります。
それなら、新しいインスタンスを返したらどうなるのか? 試してみましょう。
class Test
{
public string $prop = 'initial';
public function get_instance() : static
{
$new = new Test();
$new->prop = 'new instance';
return $new;
}
}
$test = new Test();
var_dump($test->get_instance());
object(Test)#2 (1) {
["prop"]=>
string(12) "new instance"
}
自インスタンスじゃなくても、実行しているメソッドが属しているクラスのインスタンスだったらOKらしい。
自クラス(または自クラスと同一クラス)のインスタンスと違うものを返すとエラー例外が発生します。
class Test2{}
class Test
{
public string $prop = 'initial';
public function get_instance() : static
{
return new Test2();
}
}
$test = new Test();
var_dump($test->get_instance());
PHP Fatal error: Uncaught TypeError: Test::get_instance(): Return value must be of type Test, Test2 returned in /home/vagrant/php-test.php:44
Stack trace:
#0 /home/vagrant/php-test.php(49): Test->get_instance()
#1 {main}
thrown in /home/vagrant/php-test.php on line 44
これはシンタックスエラー(文法エラー)ではないので、クラスを実装しただけでは起きません。メソッドをコールして初めて発生します。
テストは重要ですね?
staticというネーミングが気になる
ただ、'static' というネーミングはどうにかできなかったのか。staticはメモリ上に展開されるクラスオブジェクトの静的領域を指します。
static領域はインスタンスとは無関係。new演算子を使わずにクラス名に '::' を付けて参照しますよね?
それなのに、static型はインスタンスを返します。この矛盾が気持ち悪くてイマイチ意味が分かりませんでした。
細かすぎますか?
それとも粘着しすぎ?
でも、同じように気になる人いると思うけどな~。
と、考えていたところ思い出した。クラス内で self:: と似たものに static:: がある。
static:: は staticメソッド内で使う特殊なクラスで、メソッドをコールしたクラスを指す。
ここからネーミングしたんだろう。
ややこしい。PHPはstaticの意味が多すぎる。
親クラス、子クラスを返したらどうなるか?
ここまできたら、細かすぎ、粘着質の性格をフルオープンしようと思います。
extends(継承)関係のあるクラスならどうなるか見てみましょう。
まずは、子クラスからメソッドをコールして親クラスを返したらどうなるか?
class Test
{
public string $prop = 'initial';
public function get_instance() : static
{
return new Test();
}
}
class Test2 extends Test {}
$test = new Test2();
var_dump($test->get_instance());
PHP Fatal error: Uncaught TypeError: Test::get_instance(): Return value must be of type Test2, Test returned in /home/vagrant/php-test.php:60
Stack trace:
#0 /home/vagrant/php-test/php8-test.php(67): Test->get_instance()
#1 {main}
thrown in /home/vagrant/php-test.php on line 60
子クラスから見ると親クラスはちがうものと認識するようです。
今度は親クラスから子クラスを返します。
class Test
{
public string $prop = 'initial';
public function get_instance() : static
{
return new Test2();
}
}
class Test2 extends Test {}
$test = new Test();
var_dump($test->get_instance());
object(Test2)#2 (1) {
["prop"]=>
string(7) "initial"
}
正常に返りました。
これは例外クラスのcatcheを考えると分かりやすい。
例外のcatcheでは、親クラスを指定すると子クラスの例外をキャッチしますが、子クラスを指定したときに親クラスの例外はキャッチしません。
それと一緒。
というかそうじゃないと困る。共通の親を持つ子クラスはいくらでも作れるし、そのピラミッド構造が継承の本質だから。
最後に、共通の親をもつ兄弟関係のクラスでも見てみましょうか? エラーになると思います。
class Test
{
public string $prop = 'initial';
public function get_instance() : static
{
return new Test2();
}
}
class Test2 extends Test {}
class Test3 extends Test {}
$test = new Test3();
var_dump($test->get_instance());
PHP Fatal error: Uncaught TypeError: Test::get_instance(): Return value must be of type Test3, Test2 returned in /home/vagrant/php-test.php:96
Stack trace:
#0 /home/vagrant/php-test/php8-test.php(105): Test->get_instance()
#1 {main}
thrown in /home/vagrant/php-test.php on line 96
ね? やっぱりエラーになる。
そのほか、エラーが出るパターン
もう少し、粘着質にお付き合いください。そのほかのエラーになるパターンを見てみましょう。
まずは、関数の戻り値で使ってみます。
function test(int $param) : static
{
return $param . '-change';
}
test(1);
PHP Fatal error: Cannot use "static" when no class scope is active in /home/vagrant/php-test.php on line 110
PHPドキュメントでは『戻り値専用の型』とあったのでどうなるかなと思いましたが、メッセージにはクラス内でしか使えないと出ました。
まぁ、説明通りですね?
今度はパラメータで使ってみます。クラス内で使えってすでに出てるので、クラスのメソッドのパラメータを使って。
class Test
{
public string $prop = 'initial';
public function set_prop(static $prop) : void
{
$this->prop = $prop;
}
}
PHP Parse error: syntax error, unexpected token "static", expecting variable in /home/vagrant/php-test.php on line 124
シンタックスエラー(文法エラー)なので、クラスを実装した段階で出ます。
もうひとつ、クラスのプロパティで使ってみましょうか? ...って、それはただの static プロパティじゃないか!
(もちろんstatic型とstaticキーワードの 'static' は意味が違う。)
サンプルを作るまでもありません。
もちろんですけど、下位互換はないのでPHP7系以下では動きません。
PHP Parse error: syntax error, unexpected 'static' (T_STATIC) in /home/vagrant/php-test.php on line 7
シンタックスエラーになります。
PHP公式ドキュメント