WEBシステムの開発プラットフォーム LIB PHP Framework! 高速でセキュアなフルスクラッチ開発を今すぐ開始できます。

PHP基礎学習

Chapter5. Classとオブジェクト指向

Class(クラス)は変数と関数のセットです。 本章では、Classを使用したオブジェクト指向プログラミングについて学習します。
オブジェクト指向については、論理的な視点や言語仕様で解釈が異なりますが、 ここでは、PHPの実践的な視点で解説しています。

1. Class(クラス)とは

Classとは、変数と関数の集まりです。 Classは下記のような書式で定義します。

class Order {
public $price;
public $count;
public $tax;

public function sum() {
return $this->price * $this->count;
}
public function amount() {
$unit = $this->price + ($this->price * $this->tax / 100);
return $unit * $this->count;
}
}

Orderクラスには、3つの変数と、2つの関数を定義しています。
これらの変数はプロパティと言い、関数をメソッドと言います。

プロパティやメソッドの前にある、「public」はアクセス修飾子と呼ばれるもので、後述する「カプセル化」を実現するためのものですが、
アクセス権を意識しない場合は、「public」としておきます。

定義したクラスは、下記のようにして使用します。

$order1 = new Order();
$order1->price = 1000;
$order1->count = 2;
$order1->tax = 10;

$order2 = new Order();
$order2->price = 500;
$order2->count = 1;
$order2->tax = 8;

変数$order1や$order2に代入する際に「new」キーワードを指定します。 これで、$order1や$order2にOrderクラスの構造が展開され、オブジェクト型のデータになります。
このようにして、作成されたクラス構造のデータ(オブジェクト型データ)をクラスのインスタンス(実体)と言います。

プロパティにアクセスするにはアロー演算子「->」を使用します。
上記のように、$order1オブジェクトと$order2オブジェクトは、Orderクラスの異なるインスタンスですので、それぞれのプロパティには別の値を代入することができます。

ここまででは、複数の値をまとめて保持する配列となんら変わりはありませんが、クラスにはメソッドがあります。
$order1と$order2のメソッドを呼び出してみましょう。

$sum1 = $order1->sum();
var_dump($sum1);

$sum2 = $order2->sum();
var_dump($sum2);

$sum1には「2000」が、$sum2には「500」が代入されます。
メソッドにアクセスするには、プロパティと同様にアロー演算子「->」を使用します。
sum()メソッド内部では、 疑似変数と呼ばれる 「$this」を使用して、インスタンス内部のプロパティへアクセスし、計算結果を返しています。

下記コードでは、amount()メソッドを呼び出して、インスタンスごとの金額を算出しています。
$total1「4400」が、$total2「540」が代入されます。

$total1 = $order1->amount();
var_dump($total1);

$total2 = $order2->amount();
var_dump($total2);

このように、Classではデータと機能をセットにして定義することができます。

2. コンストラクタ

コンストラクタとは、「new」キーワードによってクラスのインスタンスが作成されるときに呼び出される特殊なメソッドです。
コンストラクタでは主に、プロパティーの初期化を定義します。 前述のOrderクラスにコンストラクタを追加すると次のようになります。

class Order {
public $price;
public $count;
public $tax;

public function __constract($price, $count, $tax) {
$this->price = $price;
$this->count = $count;
$this->tax = $tax;
}
public function sum() {
return $this->price * $this->count;
}
public function amount() {
$unit = $this->price + ($this->price * $this->tax / 100);
return $unit * $this->count;
}
}

コンストラクタを定義した場合、下記のようにして初期データをコンストラクタに渡します。
こうすることで、作成されたインスタンスのプロパティにはデータが代入された状態になります。

$order1 = new Order(1000, 2, 10);
$order2 = new Order(500, 1, 8);

$sum1 = $order1->sum();
$sum2 = $order2->sum();

3. アクセス権とカプセル化

クラスのインスタンスはオブジェクト(object)と言う複合型のデータ型です。
オブジェクトに含まれる複数のデータ(プロパティ)にはアクセス権を指定し、インスタンス変数からの呼び出しを制約することができます。

例えば、カタログをページ分割する場合などで使用する、ページングクラスを定義してみます。

class Pager {
private $count;
private $limit;
private $pagemax;
private $page;
private $offset;

public function __set(string $name, $value) {
switch ($name) {
case "limit":
$this->limit = $value;
break;
case "count":
$max = (int) ($value / $this->limit);
if (($value % $this->limit) > 0) $max++;
if ($max == 0) $max = 1;

$this->count = $value;
$this->pagemax = $max;
break;
case "page":
$this->page = $value;
$this->offset = ($value - 1) * $this->limit;
break;
}
}
public function __get(string $name) {
return $this->$name;
}
}

「private」修飾子で宣言されたプロパティは通常、メソッド内部からしかアクセスできませんが、
__set()メソッドと__get()メソッドを定義することで、特定のprivateプロパティへのアクセスを許可することができます。

上記コードでは、$limit、$page、$countの読み込みと書き込みを許可し、$pagemax、$offsetの読み込みのみを許可しています。

$pager1 = new Pager();
$pager1->limit = 10;
$pager1->count = 100;
var_dump($pager1->pagemax);

$pager1->page = 2;
var_dump($pager1->offset);

__set()メソッドでは、$pager1オブジェクトの$limitプロパティへは「10」を代入するのみですが、$countプロパティへの代入時には分割ページ数を計算し、
$pagemaxプロパティにも計算結果を代入しています。
$pageプロパティへの代入時にはオフセットを計算し、$offsetプロパティにも計算結果を代入しています。

__get()メソッドでは、すべてのprivateプロパティの読み込みを許可しています。
$pagemaxプロパティや$offsetプロパティに直接書き込みされては、オブジェクト内のデータに不正合が発生してしまいますので、リードオンリーにしているわけです。

このように、アクセス権を制御しデータや機能を保護することを「カプセル化」と言います。

4. 継承

継承とは、定義済みのクラスのデータや機能を受け継ぎ、拡張させたクラスを定義することです。

フレームワーク実践講習の「Chapter3. ロードイベントとセッション管理」で、HTMLファイルとクラスファイルについて解説がありましたが、
このクラスファイルはページクラスと言って、Pageクラス(基底クラス)のデータや機能を受け継いだ派生クラスです。

class Page {
private $path;
protected $values;
protected $errors;

public function __constract() {
$this->path = $this->getPath();
$this->values = array();
$this->values = array();
}
public function location(string $url) {
header("Location: {$url}");
exit();
}
public function response($response, $succeed) {
$response["status"] = ($succeed) ? "SUCCEED" : "FAILURE";
$response["hasError"] = (!$succeed);
echo json_encode($response);
exit();
}
}
class Page_Index extends Page {
/**
* サインインイベント
*/
public function signin() {
$_SESSION["member.profile"]["email"] = $_POST["email"];
$_SESSION["member.signin"] = date("Y/m/d H:i:s");
$res["url"] = "/member/index.html";
$this->response($res, true);
}
}

Pageクラスには、HTMLページに共通して必要な、location()メソッドやresponse()メソッドなどの基本機能が定義してあります。
Page_Indexクラスは、Web Studioでページを追加した際に自動的に作成される派生クラスで、extendsキーワードによってPageクラスの機能を受け継ぎます。
サインインイベントで$this->response()メソッドを呼び出した際には、Page基底クラスで定義されている、response()メソッドが実行されます。

5. スタティック宣言

プロパティやメソッドをstatic(スタティック)として定義すると、インスタンスを作成せずに静的なアクセスができます。

class Filter {
public static function isExpire(string $date, int $time) {
$target = strtotime($date);
return ((time() - $target) > ($time * 60));
}
}

isExpire()メソッドは、指定時刻からの経過を判定するメソッドです。
このメソッドは、下記のようにして呼び出します。

$expire = Filter::isExpire($_SESSION["member.signin"], 20);
var_dump($expire);

スタティックメソッドは、スコープ演算子(::)を使用して呼び出します。
この場合、インスタンスを作成しませんので、疑似変数「$this」を使用して、プロパティや他のメソッドにアクセスできません。

6. オブジェクト指向

Classの定義には、プロパティー、メソッド、コンストラクタ、アクセス権、継承、スタティック宣言の他にもさまざまな定義のルールがありますが、
アクセス権によるカプセル化や継承などの基本概念をオブジェクト指向と言い、オブジェクト定義による効率的なシステム構築をオブジェクト指向プログラミングと言います。