Codeworks Notes

 2021-01-15

 2021-01-19

PHP
PHP応用

PHPオブジェクト指向

オブジェクト指向とは

もの(オブジェクト)を中心にした考え方
「オブジェクト指向型プログラミング」は上から順番に処理を行う「手続き型プログラミング」の対極にあるものです。

現実のショッピングで商品、カート、購入者が売り場にいるように、ECサイトにも「商品」「ユーザー」「カート」があります。

逆方向から見てみましょう。
「商品」「ユーザー」「カート」という3つのモノ(オブジェクト)を組み合わせると、ECサイトができると考えられます。

処理を順番に書くのではなく、商品を管理するコード、ユーザーを管理するコード、カートの中身を処理するコードを別々に設計し、必要な時に設計図から実体を作って組み合わせるだけでさまざまなWebページを作ることができます。

オブジェクト指向では「クラス」というオブジェクトの設計図を作成し、クラス(設計図)を元にオブジェクト(実体)を生成します。
このオブジェクトのことをインスタンスといい、インスタンスが持つプロパティにアクセスしたり、メソッドを呼び出して実行するのに使います。

それぞれ何のことを言ってるか触れていきます。

クラス

クラスは「 class クラス名」と定義し「 { } 」の間に内容を書きます。
クラス名は何でも良いですが先頭を大文字にしましょう。
まずはここまででいいです。

class Fruit { //とりあえず Fruit にした
	// クラスの内容を書く
}

インスタンス

クラスを元に生成された実体(オブジェクト)を「インスタンス」と呼びます。
インスタンスは new 演算子を使って「 new クラス名() 」で生成できます。

「 $変数名 = new クラス名() 」のようにすることで生成したインスタンスを変数に代入しています。
なお、インスタンスの生成はクラスの外で行います。

class Fruit {
}
$fruit1 = new Fruit();
$fruit2 = new Fruit();
$fruit3 = new Fruit();

クラスに入れるもの

クラスにはプロパティとメソッドが入ります。

プロパティ

クラスは「設計図」なので、何らかの設計を定義しないといけません。
たとえば「 Fruit 」というクラスが商品のデータだとしましょう。

商品データには「商品名」が必要です。
「価格」も必要かもしれません。

そう言った必要なものを定義するところがプロパティです。

class Fruit {
	public $name;
	public $price
}

$peach = new Fruit();
$peach->name = 'もも';	// プロパティ「 name 」に値をセット
$peach->price = '458';	// プロパティ「 price 」に値をセット

echo $peach->name; // セットした値にアクセス
echo "<br />";
echo $peach->price . "円"; // セットした値にアクセス

public は「誰でもアクセスできる」というアクセス権です。
逆に誰もアクセスできないのは private 。後述するカプセル化に使います。
プロパティにアクセスするには、インスタンスの入った変数アロー演算子「 -> 」、プロパティ名とつなげます。

実行結果

もも
458円

メソッド

メソッドとはそれぞれのインスタンスが持つ関数のことです。クラス内で「 public function メソッド名() 」のように定義します。また、「 インスタンス->メソッド名() 」のように呼び出すことができます。

記述例

class Fruit {
	public function method() {
		echo 'くだものおいしい';
	}
}
$peach = new Fruit();
$peach->method();

出力結果

くだものおいしい

変数「 $this 」

$this という特殊な変数があって、メソッド内で自分自身のインスタンスのプロパティやメソッドにアクセスする時に使えます。クラス内のメソッドの定義の中でのみ使え、メソッドが呼ばれた時にメソッドを呼び出しているインスタンスに置き換えられる性質を持っています。

class Fruit {
	public $name;
	public $price;
	public function itemInfo() {
		echo $this->name.'の値段は'.$this->price.'円です';
	}
}
$peach = new Fruit();
$peach->name = 'もも';
$peach->price = 458;
$peach->itemInfo();

出力結果

ももの値段は458円です

カプセル化

これまで public で定義されてきたプロパティは誰でも自由にアクセスできるため、価格を変更することも容易にできます。そこで、private で定義することでクラスのプロパティとメソッドへのアクセスを制限します。

こうして使い手に必要ないものを隠すことをカプセル化といいます。
基本的にプロパティは private で記述していきましょう。

class Fruit {
	private $name;
	private $price;
}
$peach = new Fruit();		// インスタンスは作れる
$peach->name = 'もも';		// エラーになる
$peach->price = 458;		// エラーになる

echo $peach->name;			// エラーになる
echo $peach->price;			// エラーになる

ゲッターとセッター

上のコードだとインスタンス作ってプロパティの代入も出力もエラーになるので、安全に値の書き込みや取り出しをするために Getter / Setter パターンというものを使います。

まずセッターから

セッター ( Setter ) は値をセットするメソッドのこと。

class Fruit {
	private $name;
	//セッター
	public function setName($name) {
		$this->name = $name;
	}
}

$peach = new Fruit();
$peach->name = 'もも';	// これはエラー
$peach->setName('もも'); // $peach->name に「もも」がセットされる

つぎにゲッター

ゲッター ( Getter ) はプロパティに代入された値を取り出すメソッドのこと。

class Fruit {
	private $name;
	public function setName($name) {
		$this->name = $name;
	}
	// ゲッター
	public function getName() {
		return $this->$name;	// $name を返り値にする
	}
}

$peach = new Fruit();
$peach->setName('もも'); // セットする

echo $peach->name;	// これはエラー
echo $peach->getName();	// ゲッターで呼び出す

出力結果

もも

この Getter / Setter パターンは何においてもオブジェクト指向の基本になります。

コンストラクタと引数

ここまでくると「インスタンスを生成するたびに値をセットするのが手間」のような感覚に陥るので、生成と同時にセットができれば一番ラクです。
それを実現するものをコンストラクタといい、__construct() という特別なメソッドを使います。

class Fruit {
	private $name;
	public function setName($name) {
		$this->name = $name;
	}
	public function getName() {
		return $this->name;
	}
	// コンストラクタ
	public function __construct($name) {
		$this->setName($name);
		echo $this->getName($name);
	}
}

$peach = new Fruit('もも');

出力結果

もも

new Fruit() の引数に入れられた 'もも' はコンストラクタ内の $name に渡され、Setter でセットされて Getter の戻り値が echo されるところまでインスタンス生成時に実行されます。

ただ「もも」って書いただけじゃん、ってなりそうなのでもう少しプロパティを増やして書いてみます。

コンストラクタに複数の引数を渡す

複数の引数を渡すことで複数のプロパティに値をセットすることができます。

class Fruit {

	// プロパティの定義
	private $name;
	private $origin;
	private $price;

	// セッターの定義
	public function setName($name) {
		$this->name = (string)filter_var($name);
	}
	public function setOrigin($origin) {
		$this->origin = (string)filter_var($origin);
	}
	public function setPrice($price) {
		$this->price = (int)filter_var($price);
	}

	// ゲッターの定義
	public function getName() {
		return $this->name;
	}
	public function getOrigin() {
		return $this->origin;
	}
	public function getPrice() {
		return $this->price;
	}

	// コンストラクタでやらせること
	public function __construct($name,$origin,$price) {
		$this->setName($name);
		$this->setOrigin($origin);
		$this->setPrice($price);
		echo $this->getName($name).'('.$this->getOrigin($origin).'産):'.$this->getPrice($price).'円';
		echo '<br />';
	}
}

// インスタンスの生成
$peach = new Fruit('もも','岡山県',458);
$berry = new Fruit('いちご','福岡県',798);
$apple = new Fruit('りんご','青森県', 258);

出力結果

もも(岡山県産):458円
いちご(福岡県産):798円
りんご(青森県産):258円

$image というプロパティを作って引数に画像URLを格納することもできるし、たったこれだけで画像つきの商品枠をガンガン作れたりします。

「商品」という枠だけでモノ(オブジェクト)を設計する例でした。