PHPのクラスを解説

2020年3月5日

PHPでクラス(class)を使用する方法を初心者向けに解説します。

知っておくとより効率的な開発力が身に着きます。
また、自作で使用しない場合でも、PHPのライブラリなどで使用する場合もある(PDO等)ので、最低でも仕様を知っておくと役に立ちます。

クラス(class)とは?

クラスについては、解説書や解説サイトにて、様々な例え方がされています。
私が思うに、クラスとは、

「変数と関数の集合体」

です。

変数と関数なら、PHPで散々使用していると思うので皆さんご存知ですよね。

これらの変数や関数をすべてのまとまりをクラスと呼びます。
今はその程度の認識で構いません。

クラスはよく人やモノに例えられます。
例えば、RPGのキャラクターのデータを扱う、と考えてみてください。

キャラクターは、職業、HP、攻撃力など、様々なデータが内包されています。
こういった多くのデータを、クラスは一括で変数として管理が出来ます。

また、それらの変数を元にした一連の処理を行いたい場合は、関数を使用します。
例えば敵への攻撃はキャラクターの攻撃力(変数)を参照したいですよね。
クラス内の関数は、そのクラスの変数にアクセスできるため、変数を元にした処理も簡単に行えます。

クラス内における変数は「プロパティ」、関数は「メソッド」とも呼ばれています。

クラスの書き方

次に、PHPにおいてクラスはどのように書くのかを見ていきましょう。

クラスを設定する

クラスは、「class クラス名」で任意の名前を付け、波括弧{}を末尾に付けます。

//キャラクタークラスの設定
class character{
    //プロパティやメソッドをここに書く
}

ここの中に書かれた変数がプロパティ、関数がメソッドと呼ばれるものになります。

プロパティ(変数)を設定する

クラス内で使用できる変数である「プロパティ」の設定を行います。

class character{
    //プロパティ
    public $job;
    public $hp = 100;
    public $attack = 5;
}

事前に初期値を入れておくことが出来ます。

今回は「$hp」と「$attack」に、それぞれ初期値を付けました。
「$job」は初期値がないので、クラス外で設定を行います。

手前に付いている「public」は、これからクラスを学ぶ方は「なんだこれ?」と思うかもしれませんが、後で説明しますのでとりあえず今は置いておいてください。

メソッド(関数)を設定する

次に、クラス内で使用する関数、メソッドを追加しましょう。

class character{
    //プロパティ
    public $job;
    public $hp = 100;
    public $attack = 5;
    
    //メソッド
    function action(){
        return $this -> job . 'がスライムに' . $this -> attack . 'のダメージ!';
    }
}

クラス内でプロパティを使用する際は、「$this -> 変数名」という風にアロー演算子を使用して呼び出します。

ただし、通常の変数とは異なり、プロパティ名に$を付けてはいけません。

  • 正しい 「$this -> attack」
  • 間違い 「$this -> $attack」

また、クラス内でメソッドを使用する際も、$thisを付けた「$this -> action()」という風に書きます。

インスタンス化する

クラスの設定が終わりましたので、そのクラスを使用してみましょう。

class character{
    //プロパティ
    public $job;
    public $hp = 100;
    public $attack = 5;
    
    //メソッド
    function action(){
        return $this -> job . 'がスライムに' . $this -> attack . 'のダメージ!';
    }
}

//インスタンス化
$yusha = new character();

クラスを使用する(インスタンス化する)には「new クラス名()」と書きます。
先ほど定義した「character」クラスを$yushaという変数に代入しています。

インスタンス化された変数($yusha)のことをオブジェクトと呼びます。

オブジェクトからプロパティを変更する

クラスからインスタンス化した$yushaオブジェクトには、「$hp」と「$attack」と「$job」というプロパティがあります。

この中で「$job」はまだ値が入っていないので、オブジェクト側から設定を行ってみます。

$yusha -> job = '勇者';

プロパティはアロー演算子「->」を使用して呼び出します。

プロパティには$を付けない点は注意しましょう。

  • 正しい 「$yusha -> job」
  • 間違い 「$yusha -> $job」

オブジェクトからメソッドを実行する

最後に、オブジェクトからメソッドを実行してみましょう。

こちらもプロパティと同じく、アロー演算子から定義したメソッドを呼び出し、echoしてみましょう。

//キャラクタークラスの設定
class character{
    //プロパティ
    public $job;
    public $hp = 100;
    public $attack = 5;
    
    //メソッド
    function action(){
        return $this -> job . 'がスライムに' . $this -> attack . 'のダメージ!';
    }
}

//$yushaにキャラクターオブジェクトを生成 これをインスタンス化と呼ぶ
$yusha = new character();

//プロパティを設定
$yusha -> job = '勇者';

//メソッドを実行
echo $yusha -> action();
勇者がスライムに5のダメージ!

無事メソッドが実行されました。

プロパティの「$job」に「勇者」を代入したため、しっかり職業名も表示されていますね。

複数のオブジェクトを生成してみる

クラスの良いところは、複数のオブジェクトを生成し、オブジェクト毎に独立した形でプロパティを設定できるところです。

実際に「character」クラスを使用して、今度は2つのオブジェクトをインスタンス化してみましょう。

//戦士を生成
$senshi = new character();
$senshi -> job = '戦士';
$senshi -> attack = 10; //攻撃力を変更

//武闘家を生成
$butouka = new character();
$butouka -> job = '武闘家';
$butouka -> attack = 4; //攻撃力を変更

//2人で攻撃してみる
echo $senshi -> action();
echo $butouka -> action();
戦士がスライムに10のダメージ!
武闘家がスライムに4のダメージ!

コンストラクタを使用する

インスタンス化してオブジェクトを生成すると同時に、関数を実行することをコンストラクタと呼びます。

コンストラクタは、「__construct(){}」という形で書き、インスタンスを生成するとともに自動的にこの関数を通すことが出来るので、処理がより効率的になります。

通常の関数と同様に、引数を組み込むことが可能です。

//キャラクタークラスの設定
class character{
    //プロパティ
    public $job;
    public $hp = 100;
    public $attack = 5;
    
    //コンストラクタ
    function __construct($str){
        $this -> job = $str;
    }
    
    //メソッド
    function action(){
        return $this -> job . 'がスライムに' . $this -> attack . 'のダメージ!';
    }
}

では、先ほど行った「戦士」と「武闘家」オブジェクトの生成を、コンストラクタを使用して同じ処理をしてみましょう。

//戦士を生成
$senshi = new character('戦士');
$senshi -> attack = 10; //攻撃力を変更

//武闘家を生成
$butouka = new character('武闘家');
$butouka -> attack = 4; //攻撃力を変更

//2人で攻撃してみる
echo $senshi -> action();
echo $butouka -> action();
戦士がスライムに10のダメージ!
武闘家がスライムに4のダメージ!

コンストラクタから職業の設定である「$job」を設定したため、処理が簡略化されました。

インスタンス化する時はコンストラクタを使用することは多いので、覚えておきましょう。

アクセス修飾子を理解しよう

先ほどのクラスの解説で度々表示された「public」の表記は、アクセス修飾子と呼びます。

これは、プロパティ及びメソッドにアクセスできる権限を示しています。

public クラス外、クラス内どちらもアクセス化
private クラス内のみアクセス化
protected クラス内と継承先のみアクセス化

「protected」は後述する「継承」についての権限のため、今回はとりあえず「public」「private」にのみ触れていきます。

プロパティのアクセス権

先ほどのキャラクタークラスで使用したプロパティは、すべてpublicだったので、クラス外からも値の変更が出来ました。

それに対して、privateは、クラス内のメソッドでのみ変更可能な制限を付けることが出来ます。

//キャラクタークラスの設定
class character{
    //プロパティ
    private $job; //クラス内でのみ変更化
    public $hp = 100;
    public $attack = 5;
    
    //コンストラクタ
    function __construct($str){
        $this -> job = $str;
    }
    
    //メソッド
    function action(){
        return $this -> job . 'がスライムに' . $this -> attack . 'のダメージ!';
    }
}

例えばこの「$job」はprivate属性となっているため、クラス内でのみしか変更できません。
つまり上のソースコードでは、コンストラクタでしか値を代入できない、ということですね。

例えば、これに無理やりクラス外から値を代入しようとするとどうなるでしょうか。

//戦士を生成
$senshi = new character('戦士');
//やっぱり名前を変える
$senshi -> job = '超戦士';

すると、「Fatal error(致命的なエラー)」になり、下記のメッセージと共にプログラムが強制終了となります。

Fatal error: Uncaught Error: Cannot access private property character::$job in~

値の代入だけでなく、echoなどの出力も同様にエラーが出ます。

メソッドのアクセス権

メソッドも同様に、クラス外からの変更を受け付けないprivate設定が可能です。

メソッドは、アクセス修飾子を省略できます。
アクセス修飾子を付けなかった場合のデフォルト値はpublicとなります。

//キャラクタークラスの設定
class character{
    //プロパティ
    private $job;
    public $hp = 100;
    private $attack = 5;
    
    //コンストラクタ
    function __construct($str){
        $this -> job = $str;
    }
    
    //メソッド アクセス修飾子を書かない場合は「public」になる
    function action(){
        $this -> powerUp(); //action()を実行する度に攻撃力が倍に
        return $this -> job . 'がスライムに' . $this -> attack . 'のダメージ!';
    }
    
    //攻撃力を倍にする
    private function powerUp(){
        $this -> attack *= 2;
    }
}

「powerUp」というメソッドに、privateを付与しました。

これにより、powerUp()を実行できるのはcharacterクラス内だけ、という設定が可能です。

//戦士を生成
$senshi = new character('戦士');

//3回分攻撃してみる
echo $senshi -> action();
echo $senshi -> action();
echo $senshi -> action();
戦士がスライムに10のダメージ!
戦士がスライムに20のダメージ!
戦士がスライムに40のダメージ!

そして、クラス外からpowerUp()を実行しようとすると、privateなメソッドを呼び出したことによりFatal Errorとなります。

Fatal error: Uncaught Error: Call to private method character::powerUp() from~

//戦士を生成
$senshi = new character('戦士');

//攻撃力を倍に
$senshi -> powerUp(); //Fatal Error

継承を理解しよう

クラスには「継承」という機能が備わっています。

継承とは、「既存のクラスを下敷きにして、新しい拡張クラスを作ること」です。

身近な例として、カップヌードルを挙げてみました。

カップヌードルの先祖といえば白いパッケージの「しょうゆ味」です。

続いて販売された「シーフード」や「カレー」はもちろん別商品ですが、違うのは味やパッケージデザイン等で、サイズ感や容器の素材などは「しょうゆ味」とほぼ一緒ですよね。

このように、元々作られていたモノの情報の一部を「引き継ぎ」、別のクラスを作ることで、より効率的な開発が可能になる仕組みを「継承」と呼びます。

「継承」について、以下の3点の通称と仕様を覚えておきましょう。

  • 継承元となる親のクラスを「スーパークラス」と呼ぶ
  • 継承された子のクラスを「サブクラス」と呼ぶ
  • サブクラスは、スーパークラスで定義されたプロパティ、メソッドを使用できる

継承の書き方

では実際に、先ほどまで使用していたキャラクタークラスを元に、継承を行ってみましょう。

大元のスーパークラスから、攻撃力と攻撃時のセリフをサブクラス毎に変更してみます。

//キャラクター親クラス(スーパークラス)
class character{
    protected $job;
    public $hp = 100;
    protected $attack = 5;
    
    function __construct($str){
        $this -> job = $str;
    }
    
    public function action(){
        return $this -> job . 'がスライムに' . $this -> attack . 'のダメージ!';
    }
}

//魔法使い 子クラス(サブクラス)
class wizard extends character{
    
    function __construct(){
        $this -> job = '魔法使い';
    }
    
    public function action(){
        return $this -> job . 'の魔法の力でスライムに' . $this -> attack . 'のダメージ!';
    }
}

//剣士 子クラス(サブクラス)
class swordsman extends character{
    
    function __construct(){
        $this -> job = '剣士';
    }
    
    public function action(){
        return $this -> job . 'の剣技でスライムに' . $this -> attack . 'のダメージ!';
    }
}

クラスの継承は、

class 子クラス extends 親クラス{
}

という文法で書きます。

これにより、親クラスから継承された魔法使いの「wizard」クラスと、剣士の「swordsman」クラスが誕生しました。

子クラスにより定義されているコンストラクタの「__construct()」と、「action()」は、子クラスで作られたメソッドに上書きされます。
今回はコンストラクタから代入しましたが、プロパティとして設定して上書きも可能です。

では実際に、子クラスをオブジェクトにしてみましょう。

$mahotsukai = new wizard();
$kenshi = new swordsman();

echo $mahotsukai -> action();
echo $kenshi -> action();
魔法使いの魔法の力でスライムに5のダメージ!
剣士の剣技でスライムに5のダメージ!

台詞が子クラスで定義したaction()の台詞に置き換わっているので、継承成功です。

また、親クラスで設定したプロパティ$attackも、「5」と表示されているのでしっかり引き継がれていることが分かりますね。

継承された子クラスは、親クラスのプロパティ(変数)をprivate属性でない限り引き継げるので、職業毎に攻撃力を変えてみる、のも良いかもしれません。

参照渡しとclone

PHPのクラスで、通常の変数のように「=」で代入をする時の注意点です。

例えば、一度生成したオブジェクトを、別の変数に「=」で代入してみました。

class human{
    public $age;
}

$test1 = new human();
$test1 -> age = 20;
$test2 = $test1;
$test2 -> age = 25;

echo $test1 -> age;

echoした「$test1 -> age」は何が表示されるでしょうか。
$test1 -> ageで代入したのは「20」なので、「20」が返ってくる、と思うかもしれません。

25

返ってきた数値は、「$test2 -> age」で代入した「25」でした。

「$test2 -> age」で代入したはずの値が、「$test1 -> age」にも代入されてしまっています。

何故なら、クラスの代入は、参照先(データの大元)の値を渡しているだけで、オブジェクトそのものを複製しているわけではないからです。
このように、「コピー」ではなく「共有」するような仕組みを「参照渡し」と呼びます。

もし「$test1」と「$test2」を完全に別のオブジェクトとしたい場合は、2回インスタンス化してしまうのが手っ取り早いです。

$test1 = new human();
$test2 = new human();

もしくは、「clone」を使う、という手もあります。

$test1 = new human();
$test2 = clone $test1;

「clone」はオブジェクトを値として渡しているので、$test1と$test2は別々のオブジェクトとして扱うことが出来るようになります。

クラスのオブジェクト同士で戦わせてみた

最後のまとめに、クラスの「継承」「参照渡し」を使って、2つのサブクラスを戦わせてみます。

ちょっとしたゲームですね。

まあ、結果の見えたゲームは楽しくないので、ランダム要素を持たせて処理毎に結果が変わるようにしてみました。

HP ダメージ
勇者 100 10固定
魔法使い 70 8~20

安定したHPと攻撃力を持つ勇者と、低いHPの代わりに強い乱数を引けば爆発的な攻撃力になる魔法使いのバトルです。

//キャラクター親クラス(スーパークラス)
class character{
    public $job;
    public $hp = 100;
    public $enemy;
    
    function addDamage($damage){
        $this -> enemy -> hp -= $damage; //敵へのダメージ
    }
}

//勇者 子クラス(サブクラス)
class hero extends character{
    public $job = '勇者';
    
    public function action(){
        $damage = 10;
        $this -> addDamage($damage);
        return $this -> job . 'の剣技で' . $this -> enemy -> job . 'に' . $damage . 'のダメージ!';
    }
}

//魔法使い 子クラス(サブクラス)
class wizard extends character{
    public $job = '魔法使い';
    public $hp = 70;
    
    public function action(){
        $damage = mt_rand(8, 20);
        $this -> addDamage($damage);
        return $this -> job . 'の魔力で' . $this -> enemy -> job . 'に' . $damage . 'のダメージ!';
    }
}

$chara1 = new hero();
$chara2 = new wizard();

//お互いを敵と設定
$chara1 -> enemy = $chara2;
$chara2 -> enemy = $chara1;

$aryChara = [$chara1, $chara2];

//お互いのどちらかのHPが0になるまでループ
while(1){
    foreach($aryChara as $value){
        echo $value -> action()."<br>";
    }
    //どちらかのHPが0になったらbreak
    if(0 >= $chara1 -> hp || 0 >= $chara2 -> hp){
        break;
    }
}

//結果発表
if($chara1 -> hp > 0){
    echo '勇者の勝利!';
}elseif($chara2 -> hp > 0){
    echo '魔法使いの勝利!';
}else{
    echo '引き分け!';
}
勇者の剣技で魔法使いに10のダメージ!
魔法使いの魔力で勇者に12のダメージ!
勇者の剣技で魔法使いに10のダメージ!
魔法使いの魔力で勇者に12のダメージ!
勇者の剣技で魔法使いに10のダメージ!
魔法使いの魔力で勇者に19のダメージ!
勇者の剣技で魔法使いに10のダメージ!
魔法使いの魔力で勇者に15のダメージ!
勇者の剣技で魔法使いに10のダメージ!
魔法使いの魔力で勇者に14のダメージ!
勇者の剣技で魔法使いに10のダメージ!
魔法使いの魔力で勇者に13のダメージ!
勇者の剣技で魔法使いに10のダメージ!
魔法使いの魔力で勇者に9のダメージ!
勇者の勝利!

対戦結果はページをリロードする毎に変わります。

HPなど、キャラクター毎にデータの保持がやりやすいのも、クラスを使うメリットになりますね。


長くなりましたが、PHPにおけるクラスの基礎をまとめました。

PHPで開発する手段の選択肢の1つに「クラスを使う」が選べるようになると、開発力、そして運用後の保守力が向上します。

毛嫌いせず、是非こちらのサンプルコードなどを使用して、クラスに慣れてみてください。

以上、PHPのクラスを解説、でした。

PHP