【PHP】 クラスとインターフェースの実装
PHPのクラスでは、他の言語と同様に継承やinterface、プライベートなメンバなど、一通りの機能を使用できます。
目次
クラスの宣言
class句を使用して宣言します。
各メンバはクラス直下に、public
かprivate
を明示して宣言します。constな値は自動的にpublic
になります。
メンバを利用する際は、クラス内からは$this->メンバ
で、constな値はクラス名::定数名
でアクセスします。メンバにアクセスする際に$
は不要です。
class Hoge { public $m1 = 'hoge'; private $p1 = 'fuga'; private $v = 0; const c = 'const value'; // コンストラクタ。インスタンス生成時に実行される function __construct($arg1, $arg2) { print("constructor\n"); $this->v = $arg1 + $arg2; } private function mf() { $s = $this->m1 . $this->p1; print("privateなメソッド {$s}\n"); } public function pf() { // privateなメソッド呼び出し $this->mf(); print("publicなメソッド {$this->v}\n"); } } $hoge = new Hoge(10, 20); $hoge->pf(); print($hoge->m1); print(Hoge::c);
constructor privateなメソッド hogefuga publicなメソッド 30 hogeconst value
メンバ変数 (フィールド)
フィールドでは、そのクラス内からのみアクセスできるprivate
か、クラス外から自由にアクセスできるpublic
、自クラスと子孫クラスからのみ参照できるprotected
の3種類を指定できます。
フィールドは、原則private
にするべきです。 (カプセル化のため)
class Hoge { private $p = ""; public $m = ""; function f() { // インスタンス内では、$this->変数名 で呼ぶ。変数名に$は不要 print($this->p); } } $hoge = new Hoge(); $hoge->m = "abc"; // OK。mはpublic $hoge->p = 100; // Fatal error: Uncaught Error: Cannot access private property
メンバ関数 (メソッド)
メソッドの宣言は、従来の関数と同様にfunction 関数名(args) {...}
で宣言できますが、アクセス識別子を明示することもできます。デフォルトはpublic
です。
クラス内からメソッドを呼ぶ際は、$this->関数名()
のようにします。
class Hoge { private $p = "aa"; // デフォルトはpublic function f() { print($this->p); } private function pf() { print("クラス内からのみ呼び出せる\n"); } public function mf() { print("どこからでも呼び出せる\n"); // 自クラス内の他の関数を呼ぶ $this->pf(); } } $hoge = new Hoge(); // メソッド呼び出し $hoge->mf(); $hoge->pf(); // Fatal error: Uncaught Error: Call to private method Hoge::pf()
コンストラクタとデストラクタ
クラスのインスタンス生成時に自動で実行されるコンストラクタは、function __construct($args) {...}
で宣言します。
インスタンスへの参照がなくなった際に呼ばれるデストラクタは、function __destruct() {...}
で宣言します。デストラクタには引数はありません。
コンストラクタとデストラクタは不要なら書く必要はありません。
class Hoge { private $s = ""; // コンストラクタ。インスタンス生成時に実行される function __construct($arg) { $this->s = $arg; print("constructor {$this->s}\n"); } // デストラクタ。インスタンスの参照がなくなった際に呼ばれる。 function __destruct() { print("destructor {$this->s}\n"); } } $hoge = new Hoge("hoge1"); $hoge3 = null; { $hoge2 = new Hoge("hoge2"); $hoge4 = new Hoge("hoge4"); $hoge3 = $hoge4; } // $hoge2が破棄される。hoge4も終わるが、hoge3が参照しているのでまだデストラクタは呼ばれない // 全てのコードが終わると同時に全ての変数が破棄される
constructor hoge1 constructor hoge2 constructor hoge4 destructor hoge2 destructor hoge1 destructor hoge4
staticなメンバ
static
を付与することで、そのクラスのインスタンス全てで共有するメンバを作成できます。
インスタンスを作成してなくても利用でき、クラス名::変数名
($
が必要) でアクセスできます。自クラス内からはself::変数名
($
が必要) でもアクセス可能です。
static
なメソッド内では、$this
を使用することができません。
const
なフィールドは、実質static
です。ただ、static
と違ってアクセス修飾子を指定できず、強制的にpublic
となります。
class Hoge { private $p = "aa"; public static $sp = "hoge"; const c = 100; // static public function ...でも良い public static function mf() { print(self::$sp . "\n"); print(Hoge::$sp . "\n"); print(self::c . "\n"); print(Hoge::c . "\n"); // $thisは使えない // print($this->p); // Fatal error: Uncaught Error: Using $this when not in object context } public function f() { // staticなメソッド呼び出し self::mf(); } } print(Hoge::$sp . "\n"); // hoge Hoge::mf(); // hoge
継承
クラスの継承は、クラス名の後にextends クラス名
とすると継承できます。
ここで、アクセス修飾子にprotected
を指定すると、アクセスをそのクラスと子クラスからのみに制限できます。
親のメソッドを呼び出す際は、parent::メソッド名
のようにして呼び出します。親のコンストラクタやメソッドは、子がparent::__construct(...);
などで呼び出しをしない限り実行されません。
多重継承はありません。
class MyParent { // このクラスのみ参照できる private $fuga = "aaa"; // このクラスと子クラスのみ参照できる protected $hoge = "100"; // どこからでも参照できる public $hige = 500; function __construct($arg) { print("親クラスのコンストラクタ\n"); $this->hoge = $arg; } public function outputHoge() { print($this->hoge . "\n"); } public function outputFuga() { print($this->fuga . "\n"); } } class MyDerived extends MyParent { function __construct() { // 親のコンストラクタ呼び出し parent::__construct("abcde"); } // メソッドのオーバーライド public function outputFuga() { print("子クラスから親メソッド呼び出し\n"); parent::outputFuga(); } } $d = new MyDerived(); $d->outputHoge(); // 子クラスには無いので親のが呼ばれる $d->outputFuga(); // 子クラスのほうが呼ばれる
親クラスのコンストラクタ abcde 子クラスから親メソッド呼び出し aaa
interface
JavaやTypescriptなどにあるようなinterfaceをPHPでも使用できます。
interfaceには、関数のインターフェース (メソッド名と仮引数) を並べます。
クラスにinterfaceを実装する際は、implements インターフェース, インターフェース, ...
と並べます。クラスと違い、複数のインターフェースを実装できます。このとき、interfaceにあるメソッドはクラスに全て実装しなければなりません。
インターフェースのメソッドは、全てpublic
である必要があります。インターフェースは、クラス外に向けて、どのようなインターフェースを持っているかを公開するためのものであるためです。
interface iList { public function insert($pos, $elem); public function remove($pos); public function next(); // ... } class MyList implements iList { private $list = []; private $ptr = null; public function insert($pos, $elem) { // 実装 } public function remove($pos) { // 実装 } public function next() { // 実装 } }
interface A { // publicは省略できる public function a(); } interface B { function b(); } interface C { function c(); } class MyClass implements A, B, C { function a() { // ... } function b() { // ... } function c() { // ... } }
また、interfaceには定数を置くことができます。
interface A { const a = "hoge"; } class MyClass implements A { function f() { print(self::a); print(A::a); print(MyClass::a); // これもOK } } $c = new MyClass(); $c->f();
hogehogehoge
抽象クラス
クラスは、abstruct
を付与することで抽象クラスにすることができます。抽象クラスにしたクラスはそれだけではインスタンスを作成できず、必ず子クラスを作ってインスタンスを作成する必要があります。
抽象クラスでは、interfaceを実装する必要はありません。(実装することはできます)
また、抽象クラスではメソッドにabstract
を指定できます。abstract
が指定されたメソッドは、子孫クラスで必ず実装を書く必要があります。
interface MyInterface { function f(); } abstract class AbstractMyClass implements MyInterface { private $hoge = 100; // abstractなため、f()は実装しなくて良い // abstractな関数は関数の存在だけ定義し、実装を子クラスに委譲する // protectedにもできるが、子クラスでpublicに上書きできるためあまり意味はない abstract function g(); // ここで実装しているため、子クラスで実装する必要はない function h() { // ... } } class MyClass extends AbstractMyClass { // abstractなクラスではないため、残り (fとg) を実装しなければならない function f() { print("f\n"); } function g() { print("g\n"); } } // abstractなクラスはnewできない // $a = new AbstractMyClass(); // 子クラスはnewできる $c = new MyClass(); $c->f();
interfaceと抽象クラスの違い
どちらも継承して利用するものですが、どのように違うのでしょうか。
内容 | interface | abstract |
---|---|---|
中身 | メソッドの名前と引数、定数 | クラスと同様の実装が可能、abstractなメソッドを定義可能 |
継承や実装 | 複数実装可能 | 1つだけ継承可能 |
つまり、
- interfaceは、どのようなメソッド (インターフェース) を持つかを定義するもので、何ができるかを外に伝えるためのもの
- abstractなクラスは、一部の実装を子クラスに委譲したクラス
ということです。
まとめ
PHPのクラスでも、他言語と同じような事が可能です。abstract
を使用することで、必ず継承が必要なクラスを作成することができます。
interfaceとabstractの違いを把握して、意味に応じて使い分けられるようになりましょう。
