Edited at

ウコンの力でsetter/getter生やし放題

More than 3 years have passed since last update.

turmeric-spice というphpでオブジェクト作成をサポートするライブラリを作ったので書きます。このライブラリは MagicSpice という先達のライブラリにインスパイアされたものです。


基本的な使い方

packagist からインストールできます

composer require gong023/turmeric-spice

以下のようなクラス定義を行います。

use TurmericSpice\ReadableAttributes;

class User
{
use ReadableAttributes {
mayHaveAsInt
as public getId;
mayHaveAsString as public getName;
mayHaveAsFloat as public getBalance;
mayHaveAsBoolean as public getRestricted;
mayHaveAsArray as public getFriendIds;
}
}

このような定義をすると、以下の getter メソッドを使うことができます。なお、これらの getter メソッドはIDEの補完でちゃんと見ることができます。

$user = new User([

'id' => 1,
'name' => 'Taro',
'balance' => 100.0,
'restricted' => false,
'friend_ids' => [2, 3, 4],
]);

$user->getId(); // 1
$user->getName(); // 'Taro'
$user->getBalance(); // 100.0
$user->getRestricted; // false
$user->getFriendIds(); // [2, 3, 4]
$user->toArray(); // コンストラクタに渡した時のような配列を返します

setter を使いたいときは、TurmericSpice\ReadWriteAttributes トレイトを使用します。

use TurmericSpice\ReadWriteAttributes;

class User
{
use ReadWriteAttributes {
setValue
as public setId;
setValue as public setName;
mayHaveAsInt as public getId;
mustHaveAsString as public getName;
}
}

$user = new User(['id' => null]);
$user->setId(1);
$user->getId(); // 1
$user->setName('Taro');
$user->getName(); // 'Taro'


may / must DSL

turmeric-spice は maymust という二種類のDSLを持っています。

どちらも getter メソッド呼び出し時に適切な型へのキャストを行ってくれますが、キーが未定義だった場合や値が null だった場合の挙動が変化します。may は何も言わずにキャストし、must\TurmericSpice\Container\InvalidAttributeException という例外を投げます。

use TurmericSpice\ReadableAttributes;

class User
{
use ReadableAttributes {
mayHaveAsString
as public getName;
mayHaveAsFloat as public getBalance;
mustHaveAsInt as public getId;
mustHaveAsArray as public getFriendIds;
mustHaveAsBoolean as public getRestricted;
}
}

$user = new User([
'name' => null,
'balance' => 100,
'id' => null,
'restricted' => 'false',
]);

$user->getName(); // null が与えられていますがキャストされ '' が返ります。
$user->getBalance(); // int の 100 が与えられていますがキャストされ 100.0 が返ります。
$user->getId(); // null が与えられているため InvalidAttributeException が投げられます。
$user->getFriendIds(); // キーが未定義のため InvalidAttributeException が投げられます。
$user->getRestricted(); // 'false' が与えられていますが false が返ります。


発展的な使い方

turmeric-spice のトレイトをインポートすると attributes というプロパティが付き、それに直接アクセスすることでより多彩な挙動を使えます。

use TurmericSpice\ReadableAttributes;

class User
{
use ReadableAttributes;

public function getNameTaro()
{
$validate = function ($value) { return $value === 'Taro'; };

return $this->attributes->mustHave('name')->asString($validate);
}

public function getCreated()
{
return $this->attributes->mustHave('created')->asInstanceOf('\Datetime');
}

public function getUpdated()
{
return $this->attributes->mayHave('updated')->asInstanceOf('\Datetime');
}

public function getUpdatedRaw()
{
return $this->attributes->mayHave('updated')->value();
}

public function getRawArray()
{
return $this->attributes->getRaw();
}
}

$user = new User([
'name' => 'Jiro',
'created' => '2015-01-01 00:00:00',
'updated' => '2015-01-01 00:00:00',
]);

$user->getNameTaro(); // バリデーションに引っかかるので InvalidAttributeException が投げられます。
$user->getCreated(); // \Datetime のインスタンスではないので InvalidAttributeException が投げられます。
$user->getUpdated(); // new \Datetime('2015-01-01 00:00:00'); が返ります
$user->getUpdatedRaw(); // '2015-01-01 00:00:00' が返ります
$user->getRawArray(); // コンストラクタに渡した配列が返ります。toArray とは違い値のキャストなどは行われません。

version 0.2.0 以降では int[], float[], string[], bool[], YourClass[] も指定できるようになっています

use TurmericSpice\ReadableAttributes;

class User
{
use ReadableAttributes {
mayHaveAsIntArray
as public getFriendIds;
mayHaveStringArray as public getFriendNames;
mustHaveAsFloatArray as public getBalanceHistories;
}

public function getUpdatedHistories()
{
return $this->attributes->mayHave('updated_histories')->asInstanceArray('\\Datetime');
}
}

$user = new User([
'friend_ids' => ['1', '2', '3'],
'friend_names' => ['name1', 'name2', null],
'balance_histories' => [10.0, 20.0, null],
'updated_histories' => ['2015-01-01 00:00:00', '2016-01-01 00:00:00'],
]);

$user->getFriendIds; // int にキャストされるので [1, 2, 3] が返ります
$user->getFriendNames; // null が含まれていますが ['name1', 'name2', ''] が返ります
$user->balanceHistories(); // null が含まれているので exception が投げられます
$user->getUpdatedHistories(); // \Datetime にキャストされるので [new Datetime('2015-01-01 00:00:00'), new Datetime('2016-01-01 00:00:00')] が返ります


混みいった型周りの挙動とかを確かめるには実装みてもらうのがいいと思います。

ちなみに、MagicSpice は関数呼び出しを極力減らそうという意図が感じられるのに対し、turmeric-spice はその点でそこまでストイックに実装していません。では遅いかというとそんなことはない気がしていて、一番ボトルネックになりそうなこの辺のキャッシュ はしているし、MagicSpice では関数呼び出しが少ない代わりに Reflection class が使われていたりするので、まあパフォーマンスはちゃんと測ってみないとなんとも言えないなと思っています。

あと名前の由来はメタプログラミングっぽいところがあるので語呂をとって turmeric、MagiSpice からインスパイアされたものなので spice です。