三陸牡蠣復興支援プロジェクト「SAVE SANRIKU OYSTER」



スタティックメンバ/クラス定数

インスタンスメンバとクラスメンバ/スタティック(静的)メンバ

PHP4では、「::演算子(コロン2個)」を使う事で、クラスのインスタンスを生成しなくても、静的にクラスのメンバ関数(この場合「クラス関数」と言う)をコールすることが出来ました。また、メンバ変数の参照は出来ず、静的にコールするクラス関数内でメンバ変数を参照する事も出来ませんでした(これは、メンバ変数を参照するために「変数$this(オブジェクト自身へのリファレンス)」を使用する必要があるが、静的にコールする場合はオブジェクトが存在しない為)。

PHP5では、ちょっとした小細工をする事でメンバ変数の参照が出来るようになります。

メンバ変数を静的に参照する場合は、そのメンバ変数の定義時に、アクセス制限修飾子とは別に「static修飾子」を付与してやります。また、この「スタティック修飾子」はメンバ関数にも付与する事が出来ます。「スタティック」宣言されたメンバは「スタティックメンバ」となり、「スタティック」なアクセスのみ可能となり、通常の、「->(アロー演算子)」を使ったオブジェクト経由でのアクセスは出来なくなってしまいます(メンバ関数は一応コールは出来る)。

スタティックメンバ」は、「スタティック」にアクセスされる事を想定したメンバで、オブジェクト経由でアクセスする事を想定しません。しませんというか出来なくなるんですが(メンバ関数は一応コールは出来る)。

メンバにアクセスする場合、オブジェクトを生成して「->(アロー演算子)」を用いてオブジェクト経由でアクセスする方法と、オブジェクトを生成せず、「::演算子」を用いて静的に(スタティックに)アクセスする方法とがありますが、この2つは全く意味が異なります。前者はインスタンスのメンバ(「インスタンスメンバ」と呼ぶ事にする)にアクセスし、後者はクラスのメンバ(「クラスメンバ」と呼ぶ事にする)にアクセスします。なお、「スタティックメンバ」は「クラスメンバ」ですが、「クラスメンバ」は必ずしも「スタティックメンバ」ではありません(「スタティック」でないメンバ関数をクラス関数としてコール出来るので)。

おさらいすると、「オブジェクト」はオブジェクトの構造を定義した「クラス」の“実例(インスタンス)”で、「クラス」は「オブジェクト」のいわば“雛型”です。「オブジェクト」は幾つでも生成できますが、「クラス」はひとつしかありません。

で、「インスタンスメンバ」にアクセスするという事は“実例(インスタンス)”のメンバにアクセスするという事で、「クラスメンバ」にアクセスするということは“雛型(クラス)”のメンバにアクセスするという事です。以下のサンプルと結果をご覧下さい。

<pre>
<?php
    class HODE{
        public $normal_var = "normal_property";
        static public $static_var = "static_property";
        public function normal_method(){
            return "normal_method()";
        }
        static public function static_method(){
            return "static_method()";
        }
        public function get_static_var(){
            #通常メソッド内でクラスメンバ参照
            return self::$static_var;
        }
        static public function get_normal_var(){
            #スタティックメソッド内でインスタンスメンバ参照
            return $this->normal_var;
        }
    }
    
    /*
    class APETOPE{
        #コンストラクタをスタティック宣言すると・・・?
        static public function APETOPE(){
            //empty
        }
    }
    */
    
    #オブジェクトを生成
    $hode1 = new HODE;
    
    #インスタンスアクセス
    echo '$hode1->normal_var       : ', $hode1->normal_var, "\n";
    echo '$hode1->static_var       : '/*, $hode1->static_var*/, "\n";
    echo '$hode1->normal_method()  : ', $hode1->normal_method(), "\n";
    echo '$hode1->static_method()  : ', $hode1->static_method(), "\n";
    
    #クラスアクセス
    echo 'HODE::$normal_var        : '/*, HODE::$normal_var*/, "\n";
    echo 'HODE::$static_var        : ', HODE::$static_var, "\n";
    echo 'HODE::normal_method()    : ', HODE::normal_method(), "\n";
    echo 'HODE::static_method()    : ', HODE::static_method(), "\n";
    
    #インスタンスプロパティ・クラスプロパティを更新
    $hode1->normal_var = "HODENASU!!!";
    HODE::$static_var = "HODENASU!!!";
    
    #オブジェクトを生成
    $hode2 = new HODE;
    
    #インスタンスプロパティ参照
    echo '$hode2->normal_var       : ', $hode2->normal_var, "\n";
    
    #インスタンスメソッド経由でクラスプロパティ取得
    echo '$hode1->get_static_var() : ', $hode1->get_static_var(), "\n";
    echo '$hode2->get_static_var() : ', $hode2->get_static_var(), "\n";
    
    #クラスメソッド経由でインスタンスプロパティ取得
    #クラスメンバとしてコール
    echo 'HODE::get_normal_var()   : '/*, HODE::get_normal_var()*/, "\n";
    #インスタンスメンバとしてコール
    echo '$hode1->get_normal_var() : '/*, $hode1->get_normal_var()*/,"\n";
?>
</pre>
Fatal error:「コンストラクタ」は「スタティック」宣言できない)
$hode1->normal_var       : normal_property
$hode1->static_var       : (Notice:「HODE::$static_var」が未定義)
$hode1->normal_method()  : normal_method()
$hode1->static_method()  : static_method()
HODE::$normal_var        : (Fatal error:存在しないクラスプロパティ)
HODE::$static_var        : static_property
HODE::normal_method()    : normal_method()
HODE::static_method()    : static_method()
$hode2->normal_var       : normal_property
$hode1->get_static_var() : HODENASU!!!
$hode2->get_static_var() : HODENASU!!!
HODE::get_normal_var()   : (Fatal error:クラスメソッドで「$this」は不可)
$hode1->get_normal_var() : (Fatal error:クラスメソッドで「$this」は不可

「APETOPE」クラスでコンストラクタを「スタティック」宣言しようとしてますが、その時点で「E_ERROR」エラーを発します。“コンストラクタは「スタティック」宣言できない(そもそもスタティックにコールされる事を想定しないものなので)”のです。

なお、定義時に指定する「アクセス制限修飾子」と「static修飾子」の順番は任意です。

オブジェクト生成後、各メンバにインスタンスメンバとしてアクセスを試みますが、「$hode1->static_var」が「未定義のプロパティ」として参照出来ませんでした(「E_NOTICE」通知)。

更にその後、各クラスメンバにアクセスを試みますが、「HODE::$normal_var」が「存在しないクラスプロパティ」として参照出来ませんでした(「E_ERROR」エラー)。

「スタティック」宣言したプロパティは完全に「クラスプロパティ」となり、「インスタンスプロパティ」として参照する事が出来なくなり、また「インスタンスプロパティ」を「クラスプロパティ」として参照する事も出来ません(PHP4と同様に)。

一方、メンバ関数の方は、何れもコール出来ています。

次に「$hode1->normal_var = "HODENASU!!!"」で「インスタンスプロパティ」の値を更新し、「HODE::$static_var = "HODENASU!!!"」で「クラスプロパティ」の値を更新しました。

新たにオブジェクトを生成し、「インスタンスプロパティ」と「クラスプロパティ(インスタンスメソッド経由で)」を取得していますが、「インスタンスプロパティ」はいいとして、「クラスプロパティ」の値は、オブジェクトを生成する前に更新した値になっています。

この結果から、「インスタンスプロパティ」と「クラスプロパティ」の違いが解かったかと思います。

最後に、「スタティック」宣言したメソッドつまり「クラスメソッド」で「インスタンスプロパティ」を参照しようと試みますが、「オブジェクトじゃないから「$this」は使えない」として「E_ERROR」エラーを発します。

サンプルの最初の方でコールに成功した「$hode1->static_method()」は、「インスタンスメソッド」のようにコールしていましたが、実際にコールされていたのは「クラスメソッド」の方です。「クラスメソッド」は「オブジェクト」ではないのでメソッド内でオブジェクトへのリファレンスを保持する「$this」を使用することは出来ません。

「クラスメソッド」がアクセスできるのは「クラスメンバ」だけです。「クラスメンバ」にアクセスするには、「self::」を使って「self::$static_var」や「self::static_method()」のようにしてアクセスします。ちなみに、「スタティックメソッド」でない「通常メソッド」もこれでコール出来ます。しかし、この場合にコールされるのは「インスタンスメソッド」です(当然、「クラスメソッド」からのアクセスの場合は「クラスメソッド」としてコールされる)。

と、そういえばPHP4には「parent::」なんてのがありましたが、これは“派生クラスでメソッドをオーバーライドしたが、基底クラスの方のメソッドをコールしたい時に使う”というものでした。実はこれは「self::」の仲間で、同様に「クラスメンバ」にアクセス(「インスタンスメソッド」から「通常メソッド」への場合は「インスタンスメソッド」にアクセス)します。

以上の事を表にまとめると下表のようになります。

インスタンスメンバとクラスメンバの違い
メンバの種類 ->」によるインスタンスアクセス ::」によるクラスアクセス
通常プロパティ 参照可 参照不可(「E_ERROR」)
スタティックプロパティ 参照不可(「E_NOTICE」) 参照可
通常メソッド コール可 コール可(※)
スタティックメソッド コール可(「$this」使用不可) コール可(「$this」使用不可)

※.「通常メソッド」は、インスタンスを生成しないで「クラス関数」としてコールした場合は当然「$this」が使えません。インスタンスを生成し、そのメンバ関数内で「self::」または「parent::」を使用してコールされた場合は「インスタンスメソッド」となり、「$this」が使えます。一方、「スタティックメソッド」は、同じ様にコールされても常に「クラスメソッド」なので使えません。

ついでの補足ですが、クラスの継承時に注意しなければならない事があります。派生クラスで基底クラスと同名のプロパティを再定義する場合も、メソッドをオーバーライドする場合も、「スタティックメンバ」だったものを「通常メンバ」にしたり、「通常メンバ」だったものを「スタティックメンバ」に変更することは出来ません。また、“「パブリック」な「スタティックプロパティ」”に間しては、何れの場合も(派生クラスで「スタティック」宣言したとしても)派生クラスで再定義することが出来ません(「E_ERROR」エラーを発する)。

クラス定数

PHP5になって、「クラス定数」なるものが新登場しました。これは「クラスメンバ」の新種みたいなもので、「::演算子」を用いて「CLASS_NAME::CONSTANT_NAME」やメンバ内で「self::CONSTANT_NAME」のようにして参照出来ます。

通常の「定数」は“グローバルに存在してスコープが無制限(何処からでも参照可能)”ですが、実はこの「クラス定数」も殆どこれと同じで、違いは“クラスに属する定数である”という事だけです。

「クラス定数」を定義するには、クラスの定義内で、「const修飾子」を用い、プロパティの場合と同じ様に定義します。以下にそのサンプルと結果を。

<pre>
<?php
    #通常の「定数」の定義
    define("CONST1", "NORMAL_CONST");
    
    class HODE{
        static private $static_var;
        #「クラス定数」の定義
        const CONST_A = "CLASS_CONSTANT";
        /* const CONST_B = array(); */
        /* const CONST_C; */
        static public function static_method(){
            echo "****** begin HODE::static_method() ******\n";
            self::$static_var    = "HODENASU!!!";
            /* self::CONST_A     = "HODENASU!!!"; */
            echo 'self::$static_var : ', self::$static_var, "\n";
            echo 'self::CONST_A     : ', self::CONST_A, "\n";
            echo self::defined_constant("CONST1"), "\n";
            echo self::defined_constant("CONST_A"), "\n";
            echo self::defined_constant("self::CONST_A"), "\n";
            echo self::defined_constant("HODE::CONST_A"), "\n";
            echo "****** end HODE::static_method() ******\n\n";
        }
        static private function defined_constant($const_name){
            if(defined($const_name))
                return str_pad($const_name, 14) ." is ". constant($const_name);
            else
                return str_pad($const_name, 14) ." is ". "Undefined...";
        }
    }
    
    #「クラス定数」オンリークラス
    class MY_CONSTANTS{
        const CONST_A = "MY_CONSTANTS::CONST_A";
        const CONST_B = "MY_CONSTANTS::CONST_B";
        const CONST_C = "MY_CONSTANTS::CONST_C";
    }
    #派生クラスで「クラス定数」を再定義
    class EXT_MY_CONSTANTS extends MY_CONSTANTS{
        const CONST_C = "EXT_MY_CONSTANTS::CONST_C";
    }
    
    HODE::static_method();
    
    echo HODE::CONST_A, "\n\n";
    
    echo MY_CONSTANTS::CONST_A, "\n";
    echo MY_CONSTANTS::CONST_B, "\n";
    echo MY_CONSTANTS::CONST_C, "\n\n";
    
    echo EXT_MY_CONSTANTS::CONST_A, "\n";
    echo EXT_MY_CONSTANTS::CONST_B, "\n";
    echo EXT_MY_CONSTANTS::CONST_C;
?>
</pre>
Fatal error:「クラス定数」に配列は格納できない)
(Parse error:「const CONST_C;」でシンタックスエラー)
(Parse error:「self::CONST_A = "HODENASU!!!";」でシンタックスエラー)
****** begin HODE::static_method() ******
self::$static_var : HODENASU!!!
self::CONST_A     : CLASS_CONSTANT
CONST1         is NORMAL_CONST
CONST_A        is Undefined...
self::CONST_A  is CLASS_CONSTANT
HODE::CONST_A  is CLASS_CONSTANT
****** end HODE::static_method() ******

CLASS_CONSTANT

MY_CONSTANTS::CONST_A
MY_CONSTANTS::CONST_B
MY_CONSTANTS::CONST_C

MY_CONSTANTS::CONST_A
MY_CONSTANTS::CONST_B
EXT_MY_CONSTANTS::CONST_C

クラス定義内の「const CONST_A = "CLASS_CONSTANT"」の部分で「クラス定数」を定義しています。

「クラス定数」は“クラスに属する定数”ってだけの違いの「定数」なので、意識するのは定義法と参照法だけです。通常の「定数」と同様に、格納出来るのは「スカラー値」のみで、当然“定数”なので再定義も出来ません。また「クラス定数」はプロパティの様に値を省略する事は出来ません。

「MY_CONSTANTS」クラスの様に、「クラス定数」のみのクラスを作れば、通常の「定数」の様に他の定数名との衝突(名前の重複)を気にしないで済みます。また、クラスを継承した場合、基底クラスと同名の「クラス定数」を定義することが出来ます。

なお、「クラス定数」の定義時に「アクセス制限修飾子」や「static修飾子」を指定する事は出来ません。“「クラス定数」は常に「スタティック」で「パブリック」”です(「定数」の一種なので)。

作成日:2004年12月20日 最終更新日:2004年12月20日
【印刷モード風モード で表示】