ryunosuke/utility-attribute

v1.1.2 2024-08-29 13:33 UTC

This package is auto-updated.

Last update: 2024-09-29 13:49:33 UTC


README

描述

此包提供通用/有用的属性。

安装

{
  "require": {
    "ryunosuke/utility-attribute": "*"
  }
}

用法

见下文。

<?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();
?>

注意

个人感觉,使用PHP标准属性获取存在以下不便之处。

  • 不想通过反射来获取
    • 希望直接使用手头的class-string或实例来获取
  • 通常不会不指定name来获取
    • 那么,以Attribute类为主体来获取会方便一些
  • 重复属性比单次属性用途更广
    • 那么,不如用数组返回,而是用?Attribute返回,这样更方便使用
  • getArguments实际上返回arguments,使用起来不太方便
    • 因此不得不添加一些奇怪的分支,希望统一使用命名参数来获取
  • 希望将类的属性用作成员属性的默认值
    • 例如,PHPUnit的@group之类的。这种场景相当多
  • 仅仅委托方法就会导致属性消失
    • 如果完全覆盖,通常需要原始方法的属性

发布

版本控制是浪漫版本控制(非语义版本控制)。

  • major:大的BC(向后兼容性)破坏。例如,更改架构、包、类等
  • minor:小的BC破坏。例如,更改参数、返回类型等
  • patch:没有BC破坏。例如,修复错误、添加可选参数、代码格式等

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

  • 修复示例

1.0.0

  • 发布

许可证

MIT