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



クラス

クラスとは

クラス」は、『「メンバ変数(プロパティ)」という変数と「メンバ関数(メソッド)」という関数を内部に持つ』という特殊な構造をした、「オブジェクト型」の値、「オブジェクト」の構造を定義するものです。“論よりコード”ということで、まずは以下のサンプルと結果を見て雰囲気を掴んで下さい。

<?php
    class HODE{
        var $dog = "wan";
        var $cat = "nyan";
        var $cow = "mou";
        function func(){
            echo "HODENASU!!!";
        }
    }
    
    $obj = new HODE();
    
    echo $obj->cat;
    echo "<hr />";
    $obj->func();
?>
nyan

HODENASU!!!

まず、「class HODE{ 定義 }」の部分で、「HODE」という名の「クラス」を定義しています。このクラスの定義では、「メンバ変数(プロパティ)」という変数と「メンバ関数(メソッド)」という関数の定義を行います。

クラス定義内で、変数(メンバ変数)の定義をする場合は、必ず変数の前に「var」を置いて定義します。

次に、「$obj = new HODE();」の部分では、「new演算子」によって、クラス「HODE」の「オブジェクト」を生成し、「変数$obj」に格納しています。

その次の「echo $obj->cat;」と「$obj->func();」の部分では、オブジェクトが持つメンバ変数(プロパティ)とメンバ関数(メソッド)を使用しています。

オブジェクトが持つメンバ変数やメンバ関数を使用するには、「->(アロー演算子)」を使い「$obj->cat;」のようにしてアクセスします。

ちなみに、オブジェクトはクラスの実体ですが、クラスから見たオブジェクトの事を「インスタンス」と言います。要するに「オブジェクトはクラスのインスタンス」という事です。

今回のサンプルを見る限りでは、何故わざわざクラスというものの中に変数と関数を詰め込んでいるのか、その意味や必要性を感じ取る事が出来ないかと思います。実際、今回のサンプルの場合は全くクラスにした意味がありません。じゃぁ、クラスというものを作るのはどんな時なのか?それを説明するには、「PHPで“何かが出来る”ようになるのが目標」という本講座の趣旨の範囲を超える話になってしまうため、このページではクラスの概要と使い方だけ解説するにとどめておきます。

クラスにも名前を付けましょう

クラスも、関数等と同様、命名規則に沿って名前を付けなければなりません。クラス名の命名規則は、基本的に関数の命名規則と同じですが、「stdClass」という名前だけは、PHP内部で使用されているため使えません。

メンバ変数(プロパティ)とメンバ関数(メソッド)

クラスによって定義され、生成されたオブジェクトが持つ変数と関数を、それぞれ「メンバ変数(プロパティ)」、「メンバ関数(メソッド)」と言います。

これは、『「オブジェクト」とは、幾つかの「属性(プロパティ)」と「機能(メソッド)」を持った「物体(オブジェクト)」である』というイメージで理解すればいいです。これを踏まえて、以下のサンプルと結果をご覧下さい。

<?php
    class CAT{
        var $word = "nyan";
        var $favorite = "fish";
        var $weight = 6000;
        function cry(){
            echo $this->word."<br />";
        }
        function eat($food = ""){
            if($food == $this->favorite){
                $this->weight += 200;
                $this->reaction(1);
            }else if($food){
                $this->reaction(2);
            }else{
                $this->weight -= 50;
                $this->reaction(3);
            }
        }
        function reaction($feel){
            $word = "";
            switch($feel){
                case 1:
                    $word = "nyan nya nyan!<br />";
                    break;
                case 2:
                    $word = "nyaa...<br />";
                    break;
                case 3:
                    $word = "...<br />";
                    break;
            }
            echo $word;
        }
        function check_weight(){
            echo $this->weight." g<br />";
        }
    }
    
    $cat = new CAT();
    
    $cat->cry();
    $cat->check_weight();
    $cat->eat("fish");
    $cat->eat("dog");
    $cat->eat();
    $cat->check_weight();
    $cat->cry();
?>
nyan
6000 g
nyan nya nyan!
nyaa...
...
6150 g
nyan

上のサンプルでは、「CAT」という名のクラスを定義しています。名前の通り、このクラスは“猫”を簡単に定義したものです。

まず、「メンバ変数$word」に「"nyan"」を、「メンバ変数$favorite」に「"fish"」を、「メンバ変数$weight」に「6000」をそれぞれセットしています。これらは、「泣き声」、「好物」、「現在の体重」という猫の属性(プロパティ)です。

次に、「メンバ関数cry()」、「メンバ関数eat()」、「メンバ関数reaction()」、「メンバ関数check_weight()」を定義しています。これらは、「鳴く」、「食べる(餌を与えられる)」、「リアクション」、「体重を量られる」という猫の機能(メソッド)です。

次に、メンバ関数内に、「$this->favorite」や「$this->reaction(1)」なる記述がありますが、これらの「変数$this」は、自らを指すという特別な変数で、メンバ関数内から、そのクラスのメンバ変数とメンバ関数にアクセスするために使われます。

そして、オブジェクトを生成して適当にメンバ関数を実行しています。

ついでに、今回のサンプルでは、メンバ関数内でしかメンバ変数を使用していません。メンバ変数の値を得るのにわざわざメンバ関数を使っています。これは、別にルールではありませんが、メンバ変数は出来るだけ直接弄らない方が良いという「オブジェクト指向プログラミング(よく解かってませんが)」という考えに沿ったものです。“猫”クラスで言ったら、外部からいきなり体重を操作出来たらおかしいですよね。

特殊な関数「__sleep()」と「__wakeup()

クラス内で定義される関数、つまりメンバ関数の名前には、付ける事が出来ない名前があります。正確に言うと、その付けてはいけない(付けてはいけないわけではない)名前を付けられた関数は、特別な意味を持った関数になってしまいます。

特別な意味を持った関数は、ある現象が起こった時に自動的にコールされるようになります。PHP4には「__sleep()」と「__wakeup()」がありますが、今後(PHP5で)これらに加えて「_(アンダーバー)」2個で始まる特殊関数が設定されるようなので、「_(アンダーバー)」2個で始まる関数名は付けないようにします。

__sleep()」と「__wakeup()」がどんな関数なのかは、これまた本講座の範囲を超える(自分もまだ理解していない)ので、ここでは、「メンバ関数の名前に付けられない」という説明だけにとどめます。詳しくは本家のマニュアル「クラスとオブジェクト」の章の「特殊関数 __sleep および __wakeup」のページをご参照下さい。

コンストラクタ

メンバ関数の中には、「コンストラクタ」という特別な関数を定義することが出来ます。メンバ関数をコンストラクタとして定義するには、関数名をクラス名と同名(大文字小文字は区別されない)にします。つまり、クラス名が「HODE」であれば、メンバ関数名を「HODE()」とすると、この関数はクラス「HODE」のコンストラクタになります。

で、コンストラクタとは何か?ですが、コンストラクタは、『オブジェクト生成時に自動的に実行される』という特殊なメンバ関数の事です。では、以下のサンプルと結果をご覧下さい。

<?php
    class HODE{
        var $hode;
        function HODE(){
            $this->hode = "HODENASU!!!";
        }
        function call(){
            echo $this->hode;
        }
    }
    
    $hode = new HODE;
    
    $hode->call();
?>
HODENASU!!!

今回のサンプルでは、オブジェクトを生成した後、メンバ関数の「call()」のみを実行していますが、結果を見ると、コンストラクタによって「メンバ変数$hode」に「"HODENASU!!!"」がセットされているのが判ります。

また、今回のサンプルコードの中には不思議な所が幾つかあります。

1つ目は、「var $hode;」の部分です。値がセットされていません。メンバ変数は、必ずしも値を初期設定する必要はありません。

ちなみに、新しく変数を登場させて値をセットする事を「定義」と言いますが、今回の様に、値はセットしないで登場だけさせる事を「宣言」と言います。また、この宣言に対して定義を「定義宣言」とも言ったような言わなかったような。

2つ目は、「$hode = new HODE;」の部分です。「HODE」の後ろに「()(丸括弧)」がありません。オブジェクトを生成する場合、殆どの場合この「()(丸括弧)」を省略出来ます。

クラス定義時、メンバ変数にセット出来ない値/コンストラクタでメンバ変数を初期設定

クラス定義時、メンバ変数に値をセットする場合、「var $hode = "HODE";」という書き方をするわけですが、この時、セット出来る値が制限されます。ここでセット出来る値は定数(ここでいう定数は「定数」のページで解説した「define()」で定義される定数の事ではありません)のみで、関数や演算子を使用して値を生成してのセットが出来ません。例えば、「var $hode = "HODENASU";」や「var $hode = array("dog", "cat", "cow");」はセット可能ですが、「var $hode = date("Y/m/d H:i:s");」や「var $hode = 60 * 60 * 24;」といったものは(関数や演算子を使っているので)エラーになります。ちなみに「array()」は関数ではありません(言語構造です)。また、「var $hode[] = "HODENASU";」のような書き方も出来ません。

しかし、クラス定義時にセット出来ない値をメンバ変数にセットしたい場合もあります。そんな時は、コンストラクタ内でセットします。つまり、オブジェクト生成時に初期設定を行います。

コンストラクタに引数を渡す

先程、『オブジェクトを生成する場合、殆どの場合この「()(丸括弧)」を省略出来ます』なんて書きましたが、そもそも何のために「()(丸括弧)」があるのか?これは、「コンストラクタに引数を渡すため」です。では、以下のサンプルと結果をご覧下さい。

<?php
    class HODE{
        var $hode;
        function HODE($arg = array()){
            $this->hode = $arg;
        }
        function call(){
            echo "<pre>";
            print_r($this->hode);
            echo "</pre>";
        }
    }
    
    $hode = new HODE(array("dog", "cat", "cow"));
    
    $hode->call();
?>
Array
(
    [0] => dog
    [1] => cat
    [2] => cow
)

コンストラクタに配列の引数を渡しました。そして、メンバ変数にセットされています。

クラスの継承

上の方のサンプルで、「CAT」という名の“猫”を意味するクラスを定義しました。今回は、“動物”という意味のクラスを定義してみます。

<?php
    class ANIMAL{
        var $first_weight;
        var $weight;
        var $soul = true;
        var $x = 0;
        var $y = 0;
        function ANIMAL($weight){
            $this->first_weight = $this->weight = $weight;
            $this->old = $old;
        }
        function walk($x, $y){
            if(!$this->soul){
                return;
            }
            if($this->weight < $this->first_weight * 0.6){
                $this->death();
                return;
            }
            $this->x += $x;
            $this->y += $y;
            echo "walking... [{$x}], [{$y}]<br />";
            $this->weight -= 20;
        }
        function death(){
            $this->soul = false;
            echo "I died...<br />";
        }
        function get_weight(){
            echo "Weight...    {$this->weight} g<br />";
        }
        function get_posi(){
            echo "Position...  x : {$this->x}, y : {$this->y}"."<br />";
        }
    }
    
    $animal = new ANIMAL(6000);
    
    $animal->walk(5, 2);
    $animal->walk(-10, 20);
    $animal->walk(2, 0);
    $animal->walk(-50, 21);
    $animal->walk(-11, -20);
    ...省略
    $animal->walk(20, 30);
    
    $animal->get_weight();
    $animal->get_posi();
?>
walking... [5], [2]
walking... [-10], [20]
walking... [2], [0]
walking... [-50], [21]
walking... [-11], [-20]
...省略
I died...
Weight...    3580 g
Position...  x : -875, y : 1062

「メンバ変数$x」、「メンバ変数$y」はそれぞれ、「X座標」、「Y座標」を意味し、つまり動物の位置を表す数字です。歩く(メンバ関数の「walk()」を実行する)たびに更新され、また体重(「メンバ変数$weight」)が減っていき、元の体重の6割を切ったら死んでしまいます(メンバ関数の「death()」を実行する)。

これが“動物”を意味する「ANIMAL」クラスです。では次に、『「ANIMAL」クラスを「継承」して「派生クラス」、“猫”を意味する「CAT」クラスを定義する』という事をやってみます。以下のサンプルと結果をご覧下さい。

<?php
    class ANIMAL{
        ...省略
    }
    class CAT extends ANIMAL{
        var $word = "nyan";
        var $favorite = "fish";
        var $weight = 6000;
        function CAT($weight){
            $this->ANIMAL($weight);
        }
        function cry(){
            if(!$this->soul){
                return;
            }
            echo $this->word."<br />";
        }
        function eat($food = ""){
            if(!$this->soul){
                return;
            }
            if($food == $this->favorite){
                $this->weight += 200;
                $this->reaction(1);
            }else if($food){
                $this->reaction(2);
            }else{
                $this->weight -= 50;
                $this->reaction(3);
            }
            if($this->weight > $this->first_weight * 1.6){
                $this->death();
            }
        }
        function reaction($feel){
            $word = "";
            switch($feel){
                case 1:
                    $word = "nyan nya nyan!<br />";
                    break;
                case 2:
                    $word = "nyaa...<br />";
                    break;
                case 3:
                    $word = "...<br />";
                    break;
            }
            echo $word;
        }
    }
    
    $cat = new CAT(6000);
    
    $cat->cry();
    $cat->eat("fish");
    $cat->eat("dog");
    $cat->eat();
    $cat->cry();
    
    $cat->walk(5, 2);
    $cat->walk(-10, 20);
    $cat->walk(2, 0);
    $cat->walk(-50, 21);
    $cat->walk(-11, -20);
    ...省略
    $cat->walk(20, 30);
    
    $cat->eat("fish");
    $cat->cry();
    
    $cat->get_weight();
    $cat->get_posi();
?>
nyan
nyan nya nyan!
nyaa...
...
nyan
walking... [5], [2]
walking... [-10], [20]
walking... [2], [0]
walking... [-50], [21]
walking... [-11], [-20]
...省略
walking... [2], [0]
I died...
Weight... 3590 g
Position... x : -909, y : 1096

まず、「class CAT extends ANIMAL{ 定義 }」によって「CAT」クラスを定義していますが、「extends」なるものがあります。これは、「class 派生クラス extends 基底クラス(派生元のクラス){ 定義 }」という書き方をする事で、「派生クラス」は、「基底クラス(派生元のクラス)」が持つ全てのメンバ変数とメンバ関数を受け継ぐ事が出来ます。そして、この事を、「継承」と言います。

注意しなくてはいけない事は、オブジェクトの生成時に自動的に実行されるコンストラクタが、派生クラスのものだけという点です。これは、基底クラスのコンストラクタが派生クラスにとっては単なるメンバ関数に過ぎないためです。よって、基底クラスのコンストラクタを実行させたい場合は、派生クラスのコンストラクタ内で実行させます。

また、派生クラスから見て基底クラスを「親クラス」、逆に基底クラスから見て派生クラスを「子クラス」とも言います。子クラスのさらに子クラス(孫クラス)を定義することも出来ます。

メンバ関数のオーバーライド

同じクラス内で同じ名前のメンバ関数は定義できませんが(当たり前だけど)、クラスを「extends」して継承したメンバ関数(基底クラスで定義されたメンバ関数)と同じ名前のメンバ関数を、派生クラスで定義することが出来ます。で、これを「オーバーライド」と言います。

インスタンスを持たないクラスのメンバ関数を直接使う

あるクラスのメンバ変数やメンバ関数を使うには、当然そのインスタンスであるオブジェクトが生成されている必要があったわけですが、「::演算子(コロン2個)」を使う事で、クラスのインスタンスを生成しなくても、クラスのメンバ関数(この場合「クラス関数」と言う)を使うことが出来ます。しかし、メンバ変数は使えません。また、クラス関数内でクラスのメンバ変数を参照する事も出来ません。以下にサンプルと結果を。

<?php
    class HODE{
        function func(){
            echo "HODENASU!!!";
        }
    }
    
    HODE::func();
?>
HODENASU!!!

オブジェクト(クラスのインスタンス)を生成する事なく、メンバ関数(クラス関数)を使っています。

parent::」と、「$this」の意味

メンバ関数をオーバーライドしても、オーバーライドする前の、要するに基底クラスで定義された方のメンバ関数を使いたい事ももしかしてあるかもしれません。そんな時は以下の様にしてどちらを使うか明確に指定出来ます。

<?php
    class HODE{
        function func(){
            echo "I'm [ HODE::func() ], ";
            echo 'and Call [ $this->func2() ]<br />';
            $this->func2();
            echo '<br />$this(on class HODE) is ';
            var_dump($this);
        }
        function func2(){
            echo "I'm [ HODE::func2() ]<br />";
        }
    }
    class APETOPE extends HODE{
        function APETOPE(){
            echo 'Call [ $this->func() ]<br />';
            $this->func();
            echo "<hr />";
            echo "Call [ parent::func() ]<br />";
            parent::func();
        }
        function func(){
            echo "I'm [ APETOPE::func() ], ";
            echo 'and Call [ $this->func2() ]<br />';
            $this->func2();
            echo '<br />$this(on class APETOPE) is ';
            var_dump($this);
        }
        function func2(){
            echo "I'm [ APETOPE::func2() ]<br />";
        }
    }
    
    new APETOPE;
?>
Call [ $this->func() ]
I'm [ APETOPE::func() ], and Call [ $this->func2() ]
I'm [ APETOPE::func2() ]

$this(on class APETOPE) is object(apetope)(0) { } 

Call [ parent::func() ] I'm [ HODE::func() ], and Call [ $this->func2() ] I'm [ APETOPE::func2() ] $this(on class HODE) is object(apetope)(0) { }

parent::func()」で、基底クラスで定義された方のメンバ関数がコールされています。

しかし、基底クラスの方のメンバ関数内で「$this->func2()」をコールすると、派生クラスの方の「func()」がコールされています。

で、両クラスのメンバ関数内で「$this」を「var_dump()」関数(指定した変数に関する情報を出力する関数)で調べてみると、どちらも「APETOPE」クラスのオブジェクトである事が判ります。実際には、「$this」は“生成されたオブジェクト自身へのリファレンス(「リファレンス」については「変数2」のページで解説)”です。「new APETOPE」で「APETOPE」クラスのオブジェクトは生成されたが、「HODE」クラスのオブジェクトは生成されていない。

・・・というか、「APETOPE」クラスが「HODE」クラスのメンバ関数を継承したら「$this」が「APETOPE」を指すのは当たり前か。。。(ぇ)

定義済みのクラス

PHPには、既に「定義済みのクラス」が存在します。この中の、「標準で定義されているクラス」には、以下のものがあります。

クラス名概要
Directory関数「dir()」によってオープンされたディレクトリの情報(プロパティ)や操作するための機能(メソッド)を持ったクラス。また、これと同じ名前のクラスは定義出来ない。
stdClassPHPの内部で使用されるため、これと同じ名前のクラスは定義出来ない。

その他の「定義済みのクラス」については、本家のマニュアルをご参照下さい。

おさらいと予告

これで、クラスの解説は終わりです。

クラスとは

  • 「クラス」は、『「メンバ変数(プロパティ)」という変数と「メンバ関数(メソッド)」という関数を内部に持つ』という特殊な構造をした、「オブジェクト型」の値、「オブジェクト」の構造を定義するもの。
  • オブジェクトのイメージは『「オブジェクト」とは、幾つかの「属性(プロパティ)」と「機能(メソッド)」を持った「物体(オブジェクト)」である』といった感じ。
  • クラスの定義は、「class クラス名{ 定義 }」という形で行い、定義部にて「メンバ変数」と「メンバ関数」の定義を行う。
  • クラス名の命名規則は、基本的に関数の命名規則と同じ。が、「stdClass」という名前は使えない。
  • メンバ変数を定義する場合は、必ず変数の前に「var」を置いて定義する。
  • メンバ関数の名前には、クラス内でのみ使用される特殊な関数の名前である「__sleep()」と「__wakeup()」という名前は使えない(特別な意味を持つようになる)。
  • 定義されたクラスの「インスタンス」であるオブジェクトを生成するには「var演算子」を使い、「$obj = new HODE()」として変数に格納する。
  • オブジェクトが持つメンバ変数やメンバ関数を使う場合には、「->(アロー演算子)」を用いて、「$obj->func();」という形で使う。
  • メンバ関数内で、そのクラスのメンバ変数やメンバ関数を使用する場合は、「$this」という特別な変数を使い、「$this->hode」という形で使用する。
  • クラスと同名のメンバ関数を「コンストラクタ」と言い、これはインスアンス(オブジェクト)生成時に自動的に実行される。
  • var」を使ったメンバ変数の定義でセットできるのは定数のみで、定数以外のものをセットしたい場合は、コンストラクタ内でセットする。
  • オブジェクト生成時の、クラス名の後の「(丸括弧)」は省略出来る。むしろ、コンストラクタに引数を渡す必要が無い場合は書く必要が無い。
  • extends」を用いて「class 派生クラス extends 基底クラス{ 定義 }」とすると、基底クラス(派生元クラス)が持つ全てのメンバ変数とメンバ関数を兼ね備えた派生クラスを定義出来る。この事を、「継承」と言う。
  • 基底クラスから継承したメンバ関数と同じ名前の関数を定義する(「オーバーライド」)事が出来る。
  • ::演算子(コロン2つ)」を用いて「HODE::func();」のように使うと、インスタンスを生成していないクラスのメンバ関数を使う事が出来る。ただし、メンバ関数内でメンバ変数は使えない。
  • メンバ関数をオーバーライドしても、「parent::メソッド名」で基底クラスで定義された方をコールできる。
  • $this」は、生成されたオブジェクト自身へのリファレンス(「リファレンス」については「変数2」のページで解説)。

変数パート2

次回は、「変数」のページで解説しきれなかった、変数の残りの部分(「定義済みの変数」と「リファレンス」)を解説します。

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