PHP入門 オブジェクト指向 クラス定義

PHPのクラス定義のクラス命名方法、プロパティ定義、メソッド定義、コンストラクター、デストラクター、静的メソッド、静的プロパティ、クラス定数について解説しています。

クラスの定義

クラスとは、関数や変数/定数を収めるための器です。

クラス構造図
クラスの構造

構文:class命令

class クラス名{
  ・・・プロパティ/メソッドの定義
}

中身のない簡単なクラスを定義

<?php
class Person{
    }

定義したクラスのインスタンス化は、new演算子で行います。

<?php
require_once 'Person.php';
$p = new Person();

クラス命名について

PHPでは、1つのクラスは1つのファイルにあたり、「クラス名.php」「クラス名.class.php」のようなファイル名で管理するのが一般的です。これらの規則を守ることで次のメリットがあります。

  • クラス単位でファイルが分かれていたほうが余計なクラスまで読み込む必要がない
  • ファイル名でクラスが識別できる
  • オートローダーを利用する場合に便利である

1、Pascal記法で統一

すべての単語の頭文字を大文字で表す記法。たとえば、DateTimeやArrayAccessなどのように命名します。文法上はアンダースコア(_)、マルチバイト文字なども利用できますが、一般的には使用しません。

2、目的に応じてサフィックスを付ける

慣例的な命名に従うことで、より大きなくくりの中でのクラスの位置づけが明確になります。

主なサフィックス

サフィックス概要
~Exception例外クラス
~Errorエラークラス
~Testテストクラス
~Abstract抽象クラス
~Formatterデータ整形のためのクラス

プロパティ

プロパティとは、インスタンスに属する変数のことです。メンバー変数とも呼ばれます。たとえば、Personクラスであれば人に関する情報ということで、$lastName(氏)、$firstName(名)のような変数を定義したりします。

<?php
class Person{
  public string $firstName;
  public string $lastName;
}

プロパティ宣言は変数の宣言と以下の点が異なります。

  • アクセス修飾子を付与できる
  • データ型を宣言できる

アクセス修飾子とは、その変数に対してどこからアクセスできるか決めるキーワードです。クラスの外から無条件にアクセスしたい場合は、publicキーワードを指定します。

プロパティには、初期設定を設定することもできます。

public string $firstName = "山田";

プロパティに対して、値の設定/参照

Person.php

<?php
class Person{
    public string $firstName;
    public string $lastName;
    }

basic.php

<?php
require_once 'Person.php';
$p1 = new Person();
$p1 -> lastName = '山田';
$p1 -> firstName = '太郎';

$p2 = new Person();
$p2 -> lastName = '山田';
$p2 -> firstName = '花子';

print "<p>私の名前は{$p1->lastName}{$p1->firstName}です</p>";
print "<p>私の名前は{$p2->lastName}{$p2->firstName}です</p>";

実行結果
実行結果

インスタンスごとにプロパティを追加する

プロパティは、class配下で追加するばかりではありません。作成したインスタンスに対して、あとからプロパティを追加することもできます。

<?php
require_once 'Person.php';
$p1 = new Person();
$p1->lastName = '山田';
$p1->firstName = '太郎';
$p1->age = 52;

print $p1->age;

実行結果
実行結果
同一のクラスをもとに生成されたインスタンスは同一の変数を持つのが常識ですが、PHPでは「同一のクラスをもとに生成されたインスタンスであっても、それぞれが持つ変数が同一であるとは限らない

メソッド

クラスに関わる共通的な処理は、メソッドとしてクラスにまとめることができます。メソッドとは、クラスの中で定義された型にもとづいた関数のことです。これをメンバー関数と呼びます。

例:Personクラスにshowメソッドを定義

<?php
class Person{
    public string $firstName;
    public string $lastName;
    public function show():void{
        print "<p>私の名前は{$this->lastName}{$this->firstName}です</p>";
    }
    }

基本的な構文はユーザー定義関数と同様ですが、先頭にアクセス修飾子を指定できます。アクセス修飾子を省略した場合、publicが指定されたものとみなされます。また、showメソッドの中で$thisキーワードを使用しています。$thisは、インスタンスメソッド(静的でないメソッド)のなかでのみ利用できる特別な変数で、現在のインスタンス(オブジェクト)を示します。

つまり、現在のオブジェクトのlastNameプロパティを参照できます。

showメソッド呼び出し方

<?php
require_once 'Person.php';
$p1 = new Person();
$p1 -> lastName = '山田';
$p1 -> firstName = '太郎';
$p1->show();
実行結果
実行結果

動的にメソッドを追加する

プロパティ同様、メソッドもまたインスタンス個々にメソッドを追加できます。

<?php

class Person{
    public string $firstName;
    public string $lastName;
    //追加のメソッド群を格納する配列
    private array $methods = [];

    //指定のメソッドを登録
    public function __set(string $name, Closure $method): void{
        $this->methods[$name] = $method->bindTo($this, self::class);//➋
    }
    //動的に登録されたメソッドを実行
    public function __call(string $name, array $args): mixed{
        //methodsプロパティに未登録のメソッドはエラー
        if(!array_key_exists($name, $this->methods)){
            throw new Exception("${name} method is not existed.");
        }
        return $this->methods[$name](...$args);
    }
}

$p1 = new Person();
$p1->lastName = '山田';
$p1->firstName = '太郎';
$p1->bye = function(): void{               //➊
    print "{$this->lastName}{$this->firstName}さん、さよなら";
};
$p1->bye();


メソッドの登録は(❶)のように「インスタンス変数->メソッド名=無名関数」の形式で表します。存在しないプロパティへの設定(__set)のタイミングでbindToメソッドを呼び出します(❷)。存在しないプロパティへの設定(__set)のタイミングで呼び出します。

実行結果
実行結果

構文:bindToメソッド

Closure::bindTo(object $newthis[, mixed $newscope = "static"]):Closure|null
$newthis関連付けるオブジェクト
$newscope関連付けるクラス

インスタンスメソッドを追加するならば、bindToメソッドに渡す$this、self::classは決まり事です。メソッド化された関数は、あとで利用できるようにmethodsプロパティに格納しておきます。登録済みのメソッドは、__calメソッド経由で呼び出せます(➌)。引数$argsは配列型なので、「…」演算子で個々の引数に分解します。

コンストラクター

new演算子によってインスタンス化という作業をしたときに実行される特別なメソッドがコンストラクターです。コンストラクターの名前は__constructに固定されています。アンダースコア2個を繋げます。コンストラクターはインスタンス化のタイミングで実行されるため、プロパティの初期化やクラスの内部で利用する外部リソースの初期化といった処理を記述します。初期化が不要な場合は、コンストラクターは省略できます。

構文:__constructメソッド

__construct([mixed ...$values])
$values任意の引数

引数$firstName、$lastNameの値を、それぞれ対応しているプロパティに設定しています。インスタンスプロパティにアクセスするには、$thisキーワードを利用します。

<?php
class Person{
    public string $firstName;
    public string $lastName;

    public function __construct(string $firstName, string $lastName){
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    public function show():void{
        print "<p>私の名前は{$this->lastName}{$this->firstName}です</p>";
    }
    }

new クラス名(引数,・・・)でインスタンス化したとき、引数にセットされた値がコンストラクターに渡されます。

<?php
require_once 'Person.php';
$p1 = new Person('太郎','山田');
$p->show();
実行結果
実行結果

コンストラクターの省略構文

「PHP8」以降では、コンストラクターの初期化式を簡単化するための省略構文を用意しています。

<?php
class Person{
    public function __construct(
        public string $firstName,
        public string $lastName
    ){

    }
}

これで、引数$firstName/$lastNameをもとに、同名のpublicプロパティfirstName/lastNameを初期化しないさないという意味になります。ポインターは、コンストラクターの引数にアクセス修飾子を付与する点です。これでプロパティの定義から代入までのコードを代用できます。

初期値を指定することもできます。

<?php
class Person{
    public function __construct(
        public string $firstName='太郎',
        public string $lastName='山田'
    ){

    }
}

デストラクター

コンストラクターとは反対に、オブジェクトが破棄されるタイミングで実行されるのがデストラクターです。名前は、__destructに固定されています。デストラクターは、引数も受け取らなければ、戻り値も返しません。アクセス修飾子はかならずpublicにします。

PHPでは、リソースのほとんどがスクリプト終了タイミングで自動的に破棄されるので、あえてデストラクターを利用しなければならないケースはあまりありません。

構文:__destructメソッド

デストラクターには、クラスの中で利用したリソースを破棄するなど、主に終了するときの処理を記述します。

<?php
class Person{
    public function __destruct(){
        print '<p>'.__CLASS__.'オブジェクトが破棄されました。</p>';
    }

    }
<?php
require_once 'Person.php';
$p1 = new Person('太郎','山田');
$p1->show();

スクリプト終了のタイミングでオブジェクトが破棄され、デストラクターが呼び出されます。__CLASS__は定義済み定数の一種で、現在のクラス名を表します。オブジェクトは、オブジェクト変数にnullをセットすることで明示的に破棄することができます。

実行結果
実行結果

静的メソッド

静的メソッドとは、インスタンスを生成しなくれてもクラスから直接呼び出せるメソッドです。静的メソッドを定義するには、メソッド定義にstaticキーワードを付与するだけです。

静的メソッドの注意点

1、$thisは、現在のインスタンスを表すキーワードでした。静的メソッドではインスタンスそのものが存在しないので、当然、$thisキーワードは利用できません。

2、インスタンスメソッドの呼び出しに「::」演算子は利用しない

PHP7.4までは「::」演算子で、インスタンスメソッドを呼び出すことができました。ただし、あくまでの下位互換性のためのもので、PHP8では致命的エラーが発生します。

例:Areaクラスに円の面積を求めるcircleメソッドを定義

<?php
class Area {
    public static function circle(float $radius): float{
        return pow($radius, 2) * 3.14;
    }
}

呼び出し方

静的メソッドを呼び出すには「::」演算子を利用します。


<?php
require_once 'Area.php';
print '円の面積:'.Area::circle(10).'cm^2';
実行結果
実行結果

静的プロパティ

オブジェクトを経由せず、クラスから直接呼び出すことができるプロパティが静的プロパティです。staticキーワードによって定義できます。静的メソッドからも静的プロパティを呼び出すことはできます。この場合、「self::$プロパティ名」のように、selfキーワードを使用します。

例:Areaクラスに円周率の静的プロパティ$piを追加したコード

<?php
class Area {
    public static float $pi = 3.14;
    public static function circle(float $radius): float{
        return pow($radius, 2) * self::$pi;
    }
}

呼び出し方


<?php
require_once 'Area.php';
print '円周率:'.Area::$pi.'<br />';
print '円の面積:'.Area::circle(10).'cm^2';
実行結果
実行結果

シングルトンパターン

静的プロパティを利用する状況は、それほど多くありません。というもの、クラス単位で保有される情報である静的プロパティは、インスタンスプロパティとは違って、その内容を変更した場合に、コード内のすべての箇所に影響が及ぶからです。

クラス変数の利用は、原則として

  • 読み取り専用(定数)
  • さもなくば、クラス自体の状態を監視する

など、ごく限りられた状況に留めます。読み取り専用については、静的プロパティよりもクラス定数を利用することが多いです。

例:クラス自体の状態を監視する

この例は、シングルトン(Singleton)パターンと呼ばれるデザインパターンの一種です。あるクラスのインスタンスは1つしか生成しないし、また、しくたないという状況で利用します。シングルトンパターンのポイントは、コンストラクターをprivate宣言してしまうことです。また、唯一のインスタンスを保持するための静的プロパティを用意しておきます。ここに、初回アクセス時は生成したインスタンスを保存しておき、2回目以降のアクセスでは再利用させてもらいます。

インスタンスの有無をチェックし、生成を制御しているのは静的メソッドgetInstanceメソッドの役割です。ここでは、静的プロパティ$instanceに既存のインスタンスが存在するかをチェックし、存在する場合はそのインスタンスをそのまま返し、そうでなければ新たにインスタンスを生成します。これによって、生成されるインスタンスが1つであることを保証します。

このようにインスタンスそのものの管理/生成を担う変数/メソッドは、まさにクラスに属するものなので、静的プロパティ/静的メソッドとして定義する必要があります。

<?php
class MySinglete{
    private static self $instance;
    private function __construct(){}

    //インスタンスの有無をチェックし、存在しない場合にだけインスタンス化
    public static function getInstance(): self{
        if(!isset(self::$instance)){
            self::$instance = new MySinglete();
        }
        return self::$instance;
    }
}

$c1 = MySinglete::getInstance();
$c2 = MySinglete::getInstance();
var_dump($c1 === $c2);
実行結果
実行結果

クラス定数

クラス定数とは、classブロックの中で定義された定数のことです。constキーワードを定義できますが、アクセス修飾子を付与できる点だけが異なります。(無指定の場合は、public)

<?php
class Area {
    public const PI = 3.14;
    public static function circle(float $radius): float{
        return pow($radius, 2) * self::PI;
    }
}

クラスを参照するには「::」演算子を利用します。


<?php
require_once 'Area.php';
print '円周率:'.Area::PI.'<br />';
print '円の面積:'.Area::circle(10).'cm^2';
実行結果
実行結果