PHP5.2でstatic functionのオーバーライド

PHP5.2以前(PHP5.2含む)でstaticなfunctionの継承というと面倒な印象があるかと思いますが、簡単な方法を見つけたので紹介します。

やりたいこと

PHP5.2以前で下記のようにstaticなメソッドをオーバーライドしたいとします。
このソースではSuperClassを継承したSubClassでメソッドをオーバーライドして、new SubClass();を実行した時「SubClass::hoge()」と出力される想定です。
しかし実際にはこのソースを実行すると、期待とは違い「SuperClass::hoge()」と出力されてしまいます。

<?php
class SuperClass{
    public function __construct(){
        self::hoge();
    }
    public static function hoge(){
        var_dump("SuperClass::hoge()");
    }
}
class SubClass extends SuperClass{
    public static function hoge(){
        var_dump("SubClass::hoge()");
    }
}
new SubClass(); //"SubClass::hoge()"と出力されて欲しいがSuperClass::hoge()と出力される

よくある解決方法

上記の問題の対策についてググってよく見る解決方法は、マジック定数__CLASS__を使用する方法です。具体的なソースは次のような感じになり、このソースを実行すると期待通り「SubClass::hoge()」と出力されます。実際私も案件でこういったオーバーライドが必要な際にはこの手法を使っています。

<?php
class SuperClass{
    public function __construct($className=__CLASS__){
        call_user_func(array($className, 'hoge'));
    }
    public static function hoge(){
        var_dump("SuperClass::hoge()");
    }
}
class SubClass extends SuperClass{
    public function __construct($className=__CLASS__){
        parent::__construct($className);
    }
    public static function hoge(){
        var_dump("SubClass::hoge()");
    }
}
new SubClass(); //string(16) "SubClass::hoge()"

それにしてもこの方法では__constructをサブクラスでも実装しなおしているので、無駄な記述が増えます。PHPの経験が浅い人がこのソースを見た時何をしているかパッと見で分かりにくいというデメリットもあると思います。

もっと簡単な方法

__CLASS__を使うよりももっと簡単な方法があります。$thisを使う方法です。
実はstaticなfunctionでも$thisを使って呼び出すことができる事をご存知でしょうか。
この実装方法だとPHP5.2以前でも遅延的束縛っぽいことをでき、$thisを使った次のコードで想定通りstaticなfunctionをoverrideできます。

<?php
class SuperClass{
    public function __construct(){
        $this->hoge();
    }
    public static function hoge(){
        var_dump("SuperClass::hoge()");
    }
}
class SubClass extends SuperClass{
    public static function hoge(){
        var_dump("SubClass::hoge()");
    }
}
new SubClass(); //string(16) "SubClass::hoge()"

この方法のデメリットはstaticなfunctionを$thisを使って呼び出していることで、この仕様をしらない人からすると一見記述ミスに見えることでしょうか。
この書き方がいいか悪いか、意見が別れるところだと思います。

PHP5.3からは遅延静的束縛が使える

PHP5.3以降なら次のようにstatic::を使って呼び出せますので、こちらを使えば簡単に実装できるかと思います。
上記のような余計な事を考える必要はありませんね。

<?php
class SuperClass{
    public function __construct(){
        static::hoge();
    }
    public static function hoge(){
        var_dump("SuperClass::hoge()");
    }
}
class SubClass extends SuperClass{
    public static function hoge(){
        var_dump("SubClass::hoge()");
    }
}
new SubClass(); //string(16) "SubClass::hoge()"

 

以上です。
PHP5.2を使う機会はだんだん減ってくるかと思いますが、何かの役にたてばと思います。