PHP レッスン — レッスン 13 — OOP(オブジェクト指向プログラミング)の基礎
前のレッスンで、私はすでに次の PHP のデータ型について書きました。論理型、整数型、小数型、テキスト型、配列です。このレッスンでは、PHP で使えるもう 1 つのデータ型、すなわちオブジェクトについて説明します。
オブジェクトは配列と少し似ています。オブジェクトは、配列と同じように、数値、テキスト、配列、さらには他のオブジェクトといったさまざまなデータ型を自身の中に含められます。
オブジェクトは、配列のように他のデータ型を含められます。その場合、これらのデータをオブジェクトのプロパティ(フィールド)と呼びます。また、オブジェクトは自身の中に関数を含められ、その場合、これらの関数をメソッドと呼びます。
しかし、オブジェクトには配列との 2 つの大きな違いがあります:
1. オブジェクトはメソッドを含められる。
メソッドは、オブジェクトの名前から呼び出される関数です。関数をオブジェクトに結びつけることで、オブジェクトがどんな操作を行い、オブジェクトに対してどんな操作が行われるかを表せます。
2. オブジェクトはクラスから作られる。
オブジェクトを作る前に、何らかのクラスを定義する必要があります。クラスでは、オブジェクトがどんなフィールドとメソッドを持つかを記述します。これにより、オブジェクトの構造を定義でき、オブジェクトがどんなデータを保存しどんな操作を行うべきかの理解が容易になります。クラスからオブジェクトを作るとき、オブジェクトに初期データを入れられるので、新しく作られたオブジェクトがすべて空でないようにできます。
オブジェクトは、データを保存するおそらく最も便利なコンテナです。php のオブジェクトは、現実世界のオブジェクトになぞらえるのに役立ちます。例えば、書籍の情報を保存するデータベースがあるとします。書籍には著者、ページ数、出版社、版番号、書評、印刷データがあります。ですから、データを扱いやすくするために、それらを Book クラスにまとめます。そのクラスには、著者、ページ数、出版社、版番号、書評、印刷データなどのプロパティ(フィールド)があります。こうして、本物の書籍に似たオブジェクトをすでに作っています。これは特に便利です。私たちのサイトのデータはフィールドを持つテーブルに保存するので、テーブル名をクラスとして、テーブルのフィールドをクラスのプロパティ名として設定できるからです。その結果、データの操作が大幅に簡素化されます。
配列とオブジェクトの違いは、例を通じてより詳しく見ていきます。
PHP 5.2 以上の機能:
私たちのオブジェクトのクラスの定義から始めましょう。クラスは単数形、オブジェクトは複数形であることに注目してください。これにより、1 つのクラスから多くのオブジェクトを作れることに注意を向けさせます:
<?php
class book{
}
?>
class という語を使って、これから作るオブジェクトのクラスを定義します。では、オブジェクトを作りましょう:
<?php
class book{
}
$book1 = new book;
$book2 = new book;
print_r($book1);
print_r($book2);
?>
キーワード new を使って、book クラスの新しいオブジェクトを作ります。クラスの各オブジェクトを、クラスのインスタンスと呼びます。こうして、book クラスの 2 つのインスタンス、すなわち $book1、$book2 ができました。さらに、変数 $book1、$book2 の内容を画面に出力するために print_r 関数を書き加え、次が見えます:

ご覧のとおり、PHP はオブジェクトがどのクラスから作られたかを示します。オブジェクトにデータがあれば、それも画面に出力されます。では、そのデータを追加しましょう。まず、クラスのプロパティを定義する必要があります。PHP 5.2 以降、プロパティは 3 つの語で定義されます。public、protected、private です(古い書き方の var もサポートされています。これについては後述)。Delphi や C++ を扱ったことがあれば、これらのキーワードはそれらのプログラミング言語とまったく同じように働くので、より簡単でしょう。
public — クラスの開かれた(公開の)プロパティとメソッドを定義します。これらのプロパティには、オブジェクトを作った後、そのオブジェクトを操作して参照できます。この語を使って、書籍のデータのすべてのプロパティを設定します。
protected — クラスの保護されたプロパティとメソッドを定義します。そのようなプロパティとメソッドは、クラス内部と、拡張された(子の、継承した)クラス内部からのみ呼び出せます。オブジェクトを作り、protected として定義されたメソッドを参照すると、エラーが発生します。クラスの継承を学ぶ際に、protected をより詳しく見ていきます。
private — 閉じた(私的な)プロパティとメソッドを定義します。そのようなプロパティとメソッドは、クラス内部でのみ呼び出せ、それだけです。他のどこでも、子クラスでさえ、これらのプロパティとメソッドは働きません。
ひとまず protected と private には煩わされず、すべてのプロパティとメソッドを public として記述しましょう。
class book{
public $author;
public $numberOfPages;
public $year;
public $publishingOffice;
public $editionNumber;
}
これでクラスにプロパティができ、クラスのインスタンスでプロパティを参照できます:
class book{
public $author;
public $numberOfPages;
public $year;
public $publishingOffice;
public $editionNumber;
}
$book1 = new book;
$book1->author = 'Abramenko I.A';
$book1->numberOfPages = 192;
$book1->year = 2013;
$book1->publishingOffice = 'Moscow';
$book1->editionNumber = 1;
$book2 = new book;
print '<pre>';
print_r($book1);
print_r($book2);
print '</pre>';
その結果、値の入ったオブジェクトが 1 つと空のオブジェクトが 1 つできます:

これで、オブジェクトに必要なデータをどう記録するか分かったと思います。では、クラスのメソッドについても見ていきましょう。先に書いたように、メソッドはオブジェクトやクラスの名前から呼び出される通常の関数です。クラスの各プロパティを出力するメソッドを追加しましょう:
<?php
class book{
public $author;
public $numberOfPages;
public $year;
public $publishingOffice;
public $editionNumber;
public function getAuthor(){
return $this->author;
}
public function getNumberOfPages(){
return $this->numberOfPages;
}
public function getYear(){
return $this->year;
}
public function getPublishingOffice(){
return $this->publishingOffice;
}
public function getEditionNumber(){
return $this->editionNumber;
}
}
$book1 = new book;
$book1->author = 'Abramenko I.A';
$book1->numberOfPages = 192;
$book1->year = 2013;
$book1->publishingOffice = 'Moscow';
$book1->editionNumber = 1;
$book2 = new book;
print '<pre>';
print $book1->getAuthor() . '<br />';
print $book1->getNumberOfPages() . '<br />';
print $book1->getYear() . '<br />';
print $book1->getPublishingOffice() . '<br />';
print $book1->getEditionNumber() . '<br />';
print '</pre>';
?>
こうして、プロパティではなくメソッドを使ってオブジェクトを参照できます。変数 $this に注目してください。その値は、現在のクラスのインスタンスの値と一致します。つまり、インスタンスが $book1 なら、$book1 のすべての値がクラス内部の $this の値にあります。こうして、変数 $this を通じて、クラス内部での変数の操作を設定できます。クラスの外では、変数 $this はもう働きません。では、書籍をテーブルと div のさまざまな方法で出力する 2 つのメソッドをさらに設定しましょう。
class book{
public $author;
public $numberOfPages;
public $year;
public $publishingOffice;
public $editionNumber;
public function getAuthor(){
return $this->author;
}
public function getNumberOfPages(){
return $this->numberOfPages;
}
public function getYear(){
return $this->year;
}
public function getPublishingOffice(){
return $this->publishingOffice;
}
public function getEditionNumber(){
return $this->editionNumber;
}
public function displayTable(){
$content = '<table style="border: 1px solid #000">';
$content .= '<tr><td>Author</td><td>'. $this->getAuthor() . '</td></tr>';
$content .= '<tr><td>Number of pages</td><td>'. $this->getNumberOfPages() . '</td></tr>';
$content .= '<tr><td>Year</td><td>'. $this->getYear() . '</td></tr>';
$content .= '<tr><td>Publishing office</td><td>'. $this->getPublishingOffice() . '</td></tr>';
$content .= '<tr><td>Edition number</td><td>'. $this->getEditionNumber() . '</td></tr>';
$content .= '</table>';
return $content;
}
public function displayUnformatted(){
$content = '<div style="border:1px solid #ddd">';
$content .= '<label>Author:</label><div>'. $this->getAuthor() . '</div>';
$content .= '<label>Number of pages:</label><div>'. $this->getNumberOfPages() . '</div>';
$content .= '<label>Year:</label><div>'. $this->getYear() . '</div>';
$content .= '<label>Publishing office:</label><div>'. $this->getPublishingOffice() . '</div>';
$content .= '<label>Edition number:</label><div>'. $this->getEditionNumber() . '</div>';
$content .= '</div>';
return $content;
}
}
$book1 = new book;
$book1->author = 'Abramenko I.A';
$book1->numberOfPages = 192;
$book1->year = 2013;
$book1->publishingOffice = 'Moscow';
$book1->editionNumber = 1;
$book2 = new book;
$book2->author = 'Leshkina S.O.';
$book2->numberOfPages = 200;
$book2->year = 2013;
$book2->publishinOffice = 'Moscow';
$book2->editionNumber = 1;
print $book1->displayTable();
print $book2->displayUnformatted();
すでに一握りのコードが集まりましたが、ご覧のとおりかなり簡単に読めます。ああ、書籍の名前をすっかり忘れていました。もう 1 つ name フィールドと、その出力のためのメソッドを追加しましょう:
<?php
class book{
public $author;
public $name; // arghh, I forgot about it.
public $numberOfPages;
public $year;
public $publishingOffice;
public $editionNumber;
public function getAuthor(){
return $this->author;
}
public function getName(){
return $this->name;
}
public function getNumberOfPages(){
return $this->numberOfPages;
}
public function getYear(){
return $this->year;
}
public function getPublishingOffice(){
return $this->publishingOffice;
}
public function getEditionNumber(){
return $this->editionNumber;
}
public function displayTable(){
$content = '<table style="border: 1px solid #000">';
$content .= '<tr><td>Author</td><td>'. $this->getAuthor() . '</td></tr>';
$content .= '<tr><td>Name</td><td>'. $this->getName() . '</td></tr>';
$content .= '<tr><td>Number of pages</td><td>'. $this->getNumberOfPages() . '</td></tr>';
$content .= '<tr><td>Year</td><td>'. $this->getYear() . '</td></tr>';
$content .= '<tr><td>Publishing office</td><td>'. $this->getPublishingOffice() . '</td></tr>';
$content .= '<tr><td>Edition number</td><td>'. $this->getEditionNumber() . '</td></tr>';
$content .= '</table>';
return $content;
}
public function displayUnformatted(){
$content = '<div style="border:1px solid #ddd">';
$content .= '<label>Author:</label><div>'. $this->getAuthor() . '</div>';
$content .= '<label>Name:</label><div>'. $this->getName() . '</div>';
$content .= '<label>Number of pages:</label><div>'. $this->getNumberOfPages() . '</div>';
$content .= '<label>Year:</label><div>'. $this->getYear() . '</div>';
$content .= '<label>Publishing office:</label><div>'. $this->getPublishingOffice() . '</div>';
$content .= '<label>Edition number:</label><div>'. $this->getEditionNumber() . '</div>';
$content .= '</table>';
return $content;
}
}
$book1 = new book;
$book1->author = 'Abramenko I.A';
$book1->name = 'Drupal: User guide';
$book1->numberOfPages = 192;
$book1->year = 2013;
$book1->publishingOffice = 'Moscow';
$book1->editionNumber = 1;
$book2 = new book;
$book2->author = 'Leshkina S.O.';
$book2->name = 'Drupal: Fields, Images, Views';
$book2->numberOfPages = 200;
$book2->year = 2013;
$book2->publishinOffice = 'Moscow';
$book2->editionNumber = 1;
print $book1->displayTable();
print $book2->displayUnformatted();
?>

PHP 5.1.3 以下の機能:
クラス内部でプロパティ(変数)を定義したい場合は、var という語を使います。
<?php
class myClass{
}
?>
配列と関数の例を見てみましょう:
<?php
$newArray =array(1,2,3,4,5);
function _increment_all_elements($customArray){
foreach($customArray as $key){
$customArray[] = $key+1;
}
return $customArray;
}
$newArray = _increment_all_elements($newArray);
print_r($newArray);
?>
この例自体に特別な意味はなく、既存の配列に、それと同じ要素を 1 ずつ増やしたものを追加しただけです。しかし肝心なのは、それをどうやったかです。私たちはそれを関数を使って行いました。関数は、すでにご存じのとおり、PHP の関数型プログラミングの基礎にあるものです。一方、オブジェクトは特別な役割を占め、それを使ったプログラミングをオブジェクト指向と呼びます。
後でこの例に戻り、オブジェクトを使って再現しますが、ひとまずオブジェクトとは何かを見ておく必要があります。
オブジェクトは関数と同じように、関数やデータを結びつけられますが、それはより現実世界に近い形をしています。例えば、商品オブジェクトは価格、重量、幅、長さといったプロパティを持てます。現実世界への近さが、コードの理解を助けます。
オブジェクトは(通常)何らかのクラスのインスタンスとして作られます。例えば、「ボール」オブジェクトは「商品」クラスのインスタンスであり得ます。クラスはプロパティと関数の記述を伴う一般化であり、オブジェクトはクラスの実現です。
では、最初のクラスとオブジェクトを作りましょう:
<?php
class WorkWithArray{
}
?>
class という語を使ってクラスを作り、波括弧の中にクラスのプロパティと関数を書きます。ちなみに、クラスでは関数をメソッドと呼ぶので、以降は関数という語の代わりにクラスのメソッドと言います。変数についても同じで、クラスに記述する変数はクラスのプロパティと呼びます。
では、私たちのオブジェクトに最初のプロパティを追加しましょう。具体的には、配列を結びつけます。
<?php
class WorkWithArray{
var $myArray = array();
}
?>
これで、配列を扱う私たちのクラスは配列プロパティを持ちます。クラスのプロパティは、インスタンス変数やデータメンバーとも呼ばれます。クラスの変数(プロパティ)は var という語を使って追加します。
そして、私たちのクラスに関数を追加するのも良いでしょう:
<?php
class WorkWithArray{
var $myArray = array();
function increment_all_elements($newArray){
foreach($newArray as $key){
$newArray[] = $key+1;
}
return $newArray;
}
}
?>
ご覧のとおり、関数も通常の関数のようにクラスに追加され、特別なことは何もありません。さて、クラスを作ったので、それを参照してオブジェクトを作りましょう。
<?php
$myObject = new WorkWithArray;
print_r($myObject);
?>
配列でそうしたように、print_r() 関数を使ってオブジェクトを画面に出力します。オブジェクトを作ったので、それを参照しましょう。クラスの宣言によれば、1 つのプロパティ myArray と 1 つのメソッド increment_all_elements() があります:
<?php
class WorkWithArray{
var $myArray = array();
function increment_all_elements($newArray){
foreach($newArray as $key){
$newArray[] = $key+1;
}
return $newArray;
}
}
$myObject = new WorkWithArray;
$myObject->myArray = array(2,4,6,7);
print_r($myObject);
?>
こうして、帰属の記号をなす、ダッシュと大なり記号の 2 文字を通じてオブジェクトのプロパティを参照します。これは「myObject オブジェクトは myArray プロパティを持つ」と読めます。
これで、設定したメソッドを使ってこのプロパティを操作できます:
<?php
class WorkWithArray{
var $myArray = array();
function increment_all_elements($newArray){
foreach($newArray as $key){
$newArray[] = $key+1;
}
return $newArray;
}
}
$myObject = new WorkWithArray;
$myObject->myArray = array(2,4,6,7);
$myObject->myArray = $myObject->increment_all_elements($myObject->myArray);
print_r($myObject);
?>
オブジェクトのメソッドは、そのプロパティと同じように -> の記号を通じて参照します。メソッドの参照は、関数の参照と同じく「呼び出し」と呼ばれます。increment_all_elements メソッドを呼び出し、パラメーターとしてオブジェクトのプロパティを指定し、その同じプロパティを後で上書きしました。その結果、変更されたデータの配列を出力しました。
これは、オブジェクトがどう作られ、そのプロパティとメソッドをどう呼び出せるかを示す、かなり簡単な例です。この後、オブジェクト指向プログラミングをより深く見ていきます。