シンプルなPHPのクラスがあったとするよね。
<?php
namespace YourSystem\Model;
class User
{
/** @var int */
public $id;
/** @var string */
public $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
}
<?php
use YourSystem\Model\User;
$user = new User(39, "Miku");
echo $user->id;
// => 39
echo $user->name;
// => "Miku"
このクラスの問題点は、プロパティを外部から読み出したいがために、可視性をpublic
にしてしまってること。
しかし、予期しないデータの書き込みは防ぎたい…
解決策
ゲッターメソッドを定義する
プロパティの可視性をprivate
またはprotected
に変更し、プロパティ名と共通する規則でメソッドを定義する。
<?php
namespace YourSystem\Model;
class User
{
/** @var int */
private $id;
/** @var string */
private $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
/**
* @return int|null
*/
public function getId()
{
return $this->id;
}
/**
* @return string|null
*/
public function getName()
{
return $this->name;
}
}
<?php
use YourSystem\Model\User;
$user = new User(39, "Miku");
echo $user->getId();
// => 39
echo $user->getName();
// => "Miku"
- 良いところ
- 明示的
- 悪いところ
- クラス×プロパティごとにメソッド明示しなきゃいけないのだるい
__get()
を定義する
原理としては、参照不可能なプロパティ(private
/protected
などの可視性を含む)を参照しようとすると__get()
が呼ばれ、__get()
は(当然)オブジェクトに属するメソッドなのでprivate
/protected
なプロパティを参照して返り値として返すことができる、といった具合です。
<?php
namespace YourSystem\Model;
/**
* @property-read int $id
* @property-read string $name
*/
class User
{
/** @var int */
private $id;
/** @var string */
private $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function __get($name)
{
return $this->$name;
}
public function __isset($name)
{
return isset($this->$name);
}
}
- 良いところ
- クラスの外部にプロパティとして公開できる
- 悪いところ
- クラスごとに同じ
__get()
と__isset()
を定義するのだるい - PhpStormなどのIDEで外部から参照させるためには
@property-read
を記述する必要がある
- クラスごとに同じ
提案手法
BaguettePHP/objectsystemってライブラリにtrait PrivateGetter
を用意いたしました。
実装としては、上記の__get()
と__isset()
が定義されてるだけです。おてがる。
<?php
namespace YourSystem\Model;
/**
* @property-read int $id
* @property-read string $name
*/
class User
{
use \Teto\Object\PrivateGetter;
/** @var int */
private $id;
/** @var string */
private $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
}
トレイトでGetterが生やしてボイラープレートが一気に減ったぞ!
- 良いところ
- クラスの外部にプロパティとして公開できる
- クラスに一行書くだけでゲッターが生えてくる
- 悪いところ
- トレイトは継承と組合せると、ちょっと想像しにくい動作をする
- PhpStormなどのIDEで外部から参照させるためには
@property-read
を記述する必要がある
PHPでの継承は基本的にバッドプラクティスだと思ってるので推奨はしませんが、動作が気になる型はPrivateGetterTest.phpをお読みください。
まとめ
いはゆるPOPO(Plain Old Java Object)で利用すると良いのでは。
(この記事のサンプルコードは説明を簡単にするためにプロパティ定義とコンストラクタしかありませんが、単なる構造体オブジェクトをPOJOと呼ぶ1、といった意図は毛頭ございません)