ryunosuke / utility-attribute
utility attributes
v1.1.2
2024-08-29 13:33 UTC
Requires
- php: >=8.0
Requires (Dev)
README
Description
This package provides generic/useful attribute.
Install
{ "require": { "ryunosuke/utility-attribute": "*" } }
Usage
see below.
<?php require_once __DIR__ . '/vendor/autoload.php'; use ryunosuke\utility\attribute\Attribute\AbstractAttribute; use ryunosuke\utility\attribute\Attribute\DebugInfo; use ryunosuke\utility\attribute\Attribute\Friend; use ryunosuke\utility\attribute\Attribute\Json; use ryunosuke\utility\attribute\ClassTrait\DebugInfoTrait; use ryunosuke\utility\attribute\ClassTrait\FriendTrait; use ryunosuke\utility\attribute\ClassTrait\JsonTrait; use ryunosuke\utility\attribute\ReflectionAttribute; #[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)] class SampleAttribute1 extends AbstractAttribute { public function __construct(int $a, $b = 2, $c = 3) { } } #[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)] class SampleAttribute2 extends AbstractAttribute { } /** * below comment is autogenerated by annotate. * * @auto-document-Friend:begin * @property int $privateField * @auto-document-Friend:end */ #[SampleAttribute1(1, c: 9)] class SampleClass implements JsonSerializable { use DebugInfoTrait; use FriendTrait; use JsonTrait; #[Friend] #[DebugInfo(false), Json(true)] private int $privateField = 123; #[Json(false)] public int $publicField = 456; #[SampleAttribute1(1)] public function method() { } } #[SampleAttribute2] class SampleSubClass extends SampleClass { public function method() { return parent::method(); } } $sample = new SampleClass(); $subsample = new SampleSubClass(); # Get attribute by following inheritance tree var_dump(SampleAttribute1::of($subsample->method(...), 0)); /*= NULL */ var_dump(SampleAttribute1::of($subsample->method(...), ReflectionAttribute::FOLLOW_INHERITANCE)); /*= object(ryunosuke\utility\attribute\ReflectionAttribute)#12 (1) { ["SampleAttribute1"]=> array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) } } */ # Get attribute by seeing class also var_dump(SampleAttribute2::of($subsample->method(...), 0)); /*= NULL */ var_dump(SampleAttribute2::of($subsample->method(...), ReflectionAttribute::SEE_ALSO_CLASS)); /*= object(ryunosuke\utility\attribute\ReflectionAttribute)#13 (1) { ["SampleAttribute2"]=> array(0) { } } */ # Get attribute by following inheritance tree & seeing class also & merge repeatable var_dump(SampleAttribute1::arrayOf($sample->method(...), ReflectionAttribute::ALL)); /*= array(2) { [0]=> object(ryunosuke\utility\attribute\ReflectionAttribute)#10 (1) { ["SampleAttribute1"]=> array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) } } [1]=> object(ryunosuke\utility\attribute\ReflectionAttribute)#11 (1) { ["SampleAttribute1"]=> array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(9) } } } */ # Get attribute without Reflection var_dump(SampleAttribute1::arrayOf($sample)); /*= array(1) { [0]=> object(ryunosuke\utility\attribute\ReflectionAttribute)#12 (1) { ["SampleAttribute1"]=> array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(9) } } } */ # Single version above $attr = SampleAttribute1::of($sample); # ReflectionAttribute implements getReflection var_dump($attr->getReflection()); /*= object(ReflectionObject)#12 (1) { ["name"]=> string(11) "SampleClass" } */ # ReflectionAttribute implements getNamedArguments var_dump($attr->getNamedArguments()); /*= array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(9) } */ # Compare getArguments and above var_dump($attr->getArguments()); /*= array(2) { [0]=> int(1) ["c"]=> int(9) } */ # ReflectionAttribute implements getNamedArgument var_dump($attr->getNamedArgument('c')); /*= int(9) */ # Same as $params = $attr->getArguments(); var_dump(array_key_exists(2, $params) ? $params[2] : (array_key_exists('c', $params) ? $params['c'] : null)); /*= int(9) */ # But native doesn't do this var_dump($attr->getNamedArgument('b')); /*= int(2) */ # Access private field by Friend var_dump($sample->privateField); /*= int(123) */ var_dump($subsample->privateField); /*= int(123) */ # $privateField is invisible at var_dump by DebugInfo var_dump($sample); /*= object(SampleClass)#5 (1) { ["publicField"]=> int(456) } */ # $privateField is visible/$publicField is invisible at json_encode by Json var_dump(json_encode($sample, JSON_PRETTY_PRINT)); /*= string(27) "{ "privateField": 123 }" */ # Rewrite SampleClass DocComment $sample->annotate(); ?>
Note
個人的に php 標準の属性取得は下記の点で使いにくいです。
- わざわざリフレクションを経由したくない
- 手元にある class-string やインスタンスでダイレクトに取得したい
- name 未指定で取得することはまずない
- それなら Attribute クラスを主体にして取れた方が便利
- repeat な属性より single な属性の方が用途が多い
- それなら配列で返さず、?Attribute で返ってくる方が使いやすい
- getArguments が本当に arguments を返すので使いづらい
- 上記のように妙な分岐を入れざるを得ないので名前付き引数で統一して取得したい
- クラスの属性をメンバーの属性のデフォルトとして使いたい
- 例えば PHPUnit の
@group
とかそういうやつ。シチュエーションは結構多い
- 例えば PHPUnit の
- メソッドを委譲するだけで属性が消えてしまう
- 完全上書きなら元のメソッドの属性が欲しいことが多い
Release
Versioning is romantic versioning(no semantic versioning).
- major: large BC break. e.g. change architecture, package, class etc
- minor: small BC break. e.g. change arguments, return type etc
- patch: no BC break. e.g. fix bug, add optional arguments, code format etc
1.1.2
- [fixbug] 可変長引数の場合に値が取れない不具合
1.1.1
- [feature] php8.2 のエラーを修正
1.1.0
- [change] php>=8.0
1.0.2
- [feature] MERGE_REPEATABLE を追加
1.0.1
- fix demo
1.0.0
- publish
License
MIT