PHPの基礎文法をおさらいする(クラスの定義と継承)
PHPはSmalltalkに代表されるような純粋なオブジェクト指向言語ではないが、そのバージョン5から、Zend Engine 2を搭載することによって、オブジェクト指向によるプログラミングの真似事ができるようになった。クラスと呼ばれるものを新たなデータ型としてユーザーが独自に設計することによって、それを変数に代入してオブジェクトとして利用することができる。
オブジェクトとは、C言語でいうところの構造体、Pascalでいうところのレコード、データベースでいうところのテーブルに、それ専用の函数を付属させて作られる変数の一種。
クラスの定義
クラスとはオブジェクトのもとになるデータ型の一種であり、オブジェクト作成用のテンプレートのようなもの。クラスはclassというキーワードを用いてclass クラス名{クラスの定義文;}
という書式で定義することができる。
次のコードでは、ユーザーのIDとユーザーのメールアドレスを管理するためのuserクラスを定義している。ユーザーのIDとメールアドレスを保持する変数にはprivateというアクセス修飾子を付けて外部から操作できないようにした。その代わり、それらの変数にアクセスするための函数をpublicというアクセス修飾子を付けて定義し、それらの函数を外部から呼び出して使えるようにした。
<?php // userという名のクラスを定義 class user { // ユーザーID用の変数 private $userID; // メールアドレス用の変数 private $mailAddress; // コンストラクタ(初期化メソッド) public function __construct(){ // それぞれの変数に初期値を代入 $this->userID = 'ゲストユーザー'; $this->mailAddress = 'メールアドレス'; } // ユーザーIDの設定用メソッド public function set_userID($value){ $this->userID = (string)$value; } // メールアドレスの設定用メソッド public function set_mailAddress($value){ $this->mailAddress = (string)$value; } // ユーザーIDの取得用メソッド public function get_user(){ return $this->userID; } // メールアドレスの取得用メソッド public function get_mailAddress(){ return $this->mailAddress; } }
クラスの内部で定義された変数をプロパティと呼ぶことがあり、同じくクラスの内部で定義された函数をメソッドと呼ぶことがある。
クラスからいくつかのオブジェクトを作り出すことによって、それらをメモリに割り当てる手続きをインスタンス化と呼ぶ。そのためにはクラス名の前にnewというキーワードを置いて変数に代入する。
メソッドの定義中に用いている$thisという変数は、インスタンス化によって代入した変数を指している。つまり、次のコードでいえば、$thisは$user1または$user2を指している。クラスがインスタンス化されるまではオブジェクトとなるその変数名は未定なので、$thisを使って将来の自分自身を指し示す必要がある。
// インスタント化でオブジェクトを2つ作成 $user1 = new user; $user2 = new user;
インスタンス化のときに自動的に実行されるのが、クラス内で定義したコンストラクタと呼ばれる初期化メソッド。コンストラクタには__construct()というメソッド名を付けるかクラス名と同じメソッド名を付ける。両方存在する場合には__construct()のほうが優先される。
ちなみに、オブジェクトが破棄されるときに自動的に呼び出されるメソッドにデストラクタというのがある。これは__destruct()というメソッド名を付けてクラス内で定義する。
インスタンス化されたクラスを代入した変数はオブジェクト型の変数となる。変数のデータ型を取得するgettype函数をこのコードに加えてそのことを確認できるようにしておこう。
// 変数のデータ型を標準出力 echo gettype($user1),PHP_EOL; echo gettype($user2),PHP_EOL;
作成したオブジェクトのメソッドはオブジェクト変数名->メソッド名();
という書式で呼び出すことができる。
// 取得用メソッドを呼び出して標準出力 echo $user1->get_user(),PHP_EOL; echo $user1->get_mailAddress(),PHP_EOL; echo $user2->get_user(),PHP_EOL; echo $user2->get_mailAddress(),PHP_EOL; // 設定用メソッドを呼び出してユーザーIDとメアドを設定 $user1->set_userID('yuuchan'); $user1->set_mailAddress('yuuchan@neophp.jp'); $user2->set_userID('miichan'); $user2->set_mailAddress('miichan@neophp.jp'); // 再び取得用メソッドを呼び出して標準出力 echo $user1->get_user(),PHP_EOL; echo $user1->get_mailAddress(),PHP_EOL; echo $user2->get_user(),PHP_EOL; echo $user2->get_mailAddress(),PHP_EOL;
これらの一連のコードをひとつにまとめると次のようになる。
<?php // userという名のクラスを定義 class user { // ユーザーID用の変数 private $userID; // メールアドレス用の変数 private $mailAddress; // コンストラクタ(初期化メソッド) public function __construct(){ // それぞれの変数に初期値を代入 $this->userID = 'ゲストユーザー'; $this->mailAddress = 'メールアドレス'; } // ユーザーIDの設定用メソッド public function set_userID($value){ $this->userID = (string)$value; } // メールアドレスの設定用メソッド public function set_mailAddress($value){ $this->mailAddress = (string)$value; } // ユーザーIDの取得用メソッド public function get_user(){ return $this->userID; } // メールアドレスの取得用メソッド public function get_mailAddress(){ return $this->mailAddress; } } // インスタント化でオブジェクトを2つ作成 $user1 = new user; $user2 = new user; // 変数のデータ型を標準出力 echo gettype($user1),PHP_EOL; echo gettype($user2),PHP_EOL; // 取得用メソッドを呼び出して標準出力 echo $user1->get_user(),PHP_EOL; echo $user1->get_mailAddress(),PHP_EOL; echo $user2->get_user(),PHP_EOL; echo $user2->get_mailAddress(),PHP_EOL; // 設定用メソッドを呼び出してユーザーIDとメアドを設定 $user1->set_userID('yuuchan'); $user1->set_mailAddress('yuuchan@php.jp'); $user2->set_userID('maakun'); $user2->set_mailAddress('maakun@php.jp'); // 再び取得用メソッドを呼び出して標準出力 echo $user1->get_user(),PHP_EOL; echo $user1->get_mailAddress(),PHP_EOL; echo $user2->get_user(),PHP_EOL; echo $user2->get_mailAddress(),PHP_EOL;
このコードをPHPインタプリタに渡すと次のように標準出力されるはず。
$ php sample.php object object ゲストユーザー メールアドレス ゲストユーザー メールアドレス yuuchan yuuchan@php.jp maakun maakun@php.jp
この実行結果から、クラスをインスタンス化して代入した2つの変数がオブジェクト型であることがまず分かる。そして、1度目に取得用メソッドを呼び出したときにはコンストラクタが自動的に設定した初期値が標準出力されていることが分かり、2度目に呼び出したときには設定用メソッドによって2つのプロパティに設定した値が標準出力されていることが分かる。
ちなみに、設定用メソッドはセッタメソッドと呼ぶことがあり、取得用メソッドはゲッタメソッドと呼ぶことがある。これらのメソッドをまとめてアクセサメソッドと呼ぶことがある。
クラス内で定義した変数をメンバー変数とかプロパティと呼ぶが、これらの変数のアクセス修飾子をprivateかprotectedにすることによって外部からのアクセスが直接できないように定義できる。このことをデータの隠蔽とか遮断とかカプセル化などと呼ぶとがある。そのアクセス修飾子には次の3つがある。
- private
- そのクラス内からしかアクセスできない
- public
- 外部からもアクセスが可能
- protected
- そのクラス内および下位のクラスからのみアクセスが可能
継承
下位のクラスとは、言い換えるとサブクラスのことであり、その上位のクラスであるスーパークラスを継承して作られたクラス。あるクラスから見て相対的に上位のクラスを親クラスまたは基本クラスと呼ぶことがあり、あるクラスから見て相対的に下位のクラスを子クラスと呼ぶことがある。クラスはこの継承の機能によってその名のとおり階層構造を持つことができる。
クラスの階層構造は、生物の分類階層に喩えるとよいかもしれない。例えばわれわれヒトという種は、真核生物の動物界の脊索動物門の哺乳網の霊長目のヒト科のヒト属の子クラスということになる。ヒトという種は、その親クラスたちの属性や機能になんらかの変更を加えつつも、それらの属性や機能を継承してできている。
あるクラスの属性や機能すなわちプロパティやメソッドを引き継ぎつつ変更を加えて新たなクラスを定義したいときには、extendsというキーワードを用いて継承することができる。その書式はclass クラス名 extends 親クラス名{クラスの定義部;}
というものになる。
次のコードでは、userクラスを定義した上で、そのuserクラスを継承してuser_profクラスを定義している。
<?php // 親クラス class user { protected $userID; protected $mailAddress; public function __construct(){ $this->userID = 'ゲストユーザー'; $this->mailAddress = 'メールアドレス'; } public function get_userID(){ return $this->userID; } public function get_mailAddress(){ return $this->mailAddress; } public function set_userID($value){ $this->userID = (string)$value; } public function set_mailAddress($value){ $this->mailAddress = (string)$value; } } // userクラスを継承した子クラス class user_info extends user { // プロパティ protected $nickname; protected $age; // コンストラクタ public function __construct(){ // コンストラクタも継承する parent::__construct(); // 追加する $this->nickname = 'ニックネーム'; $this->age = 1; } // ゲッタメソッド public function get_nickname(){ return $this->nickname; } public function get_age(){ return $this->age; } // セッタメソッド public function set_nickname($value){ $this->nickname = (string)$value; } public function set_age($value){ $this->age = (int)$value; } } // 子クラスのオブジェクトを2つ作成 $user1 = new user_info; $user2 = new user_info; // オブジェクトのプロパティの値を取得して標準出力 echo $user1->get_userID(),PHP_EOL; echo $user1->get_mailAddress(),PHP_EOL; echo $user1->get_nickname(),PHP_EOL; echo $user1->get_age(),'歳',PHP_EOL; echo $user2->get_userID(),PHP_EOL; echo $user2->get_mailAddress(),PHP_EOL; echo $user2->get_nickname(),PHP_EOL; echo $user2->get_age(),'歳',PHP_EOL; // オブジェクトのプロパティに値をセット $user1->set_userID('yuuchan'); $user1->set_mailAddress('yuuchan@php.jp'); $user1->set_nickname('ユーちゃん'); $user1->set_age(30); $user2->set_userID('maakun'); $user2->set_mailAddress('maakun@php.jp'); $user2->set_nickname('マーくん'); $user2->set_age(30); // オブジェクトのプロパティの値を取得して標準出力 echo $user1->get_userID(),PHP_EOL; echo $user1->get_mailAddress(),PHP_EOL; echo $user1->get_nickname(),PHP_EOL; echo $user1->get_age(),'歳',PHP_EOL; echo $user2->get_userID(),PHP_EOL; echo $user2->get_mailAddress(),PHP_EOL; echo $user2->get_nickname(),PHP_EOL; echo $user2->get_age(),'歳',PHP_EOL;
コンストラクタを親クラスから引き継ぐには子クラスのコンストラクタの中でparent::__construct();
という文が必要になる。子クラスの中にコンストラクタが定義されていないときには、親クラスのコンストラクタが自動的に継承される。ただしその場合、親クラスのコンストラクタのアクセス修飾子がprivateでないことが必要。
このコードの実行結果は次のようになるはず。
$ php sample.php ゲストユーザー メールアドレス ニックネーム 1歳 ゲストユーザー メールアドレス ニックネーム 1歳 yuuchan yuuchan@php.jp ユーちゃん 30歳 maakun maakun@php.jp マーくん 30歳
ゲッタメソッドを使った1度目の呼び出しではコンストラクタによって設定されたプロパティの値が標準出力されているが、2度目の呼び出しではセッタメソッドによって設定されたプロパティの値が標準出力されている。
クラスについてはまだまだつづく。
コメント
コメントを投稿