buffalokiwi/buffalotools_types

PHP 7.4 的枚举和集合

v1.0.15 2022-05-14 21:28 UTC

This package is auto-updated.

Last update: 2024-09-04 20:32:11 UTC


README

一个极其有用的包,包含用于 PHP 7.4 的 Enum、BitSet、Set 和 BigSet 类型。

MIT 许可证

目录

  1. 安装
  2. 枚举 / 状态机
  3. 位集合
  4. 集合
  5. 大集合

安装

composer require buffalokiwi/buffalotools_types

PHP 7.4 的枚举状态机

在不使用扩展的情况下,PHP 缺少枚举类型。这是一个快速的枚举实现,同时也是一个状态机。

枚举是抽象的,必须扩展以创建枚举。可以使用 RuntimeEnum 在运行时创建 Enum 对象。

以下是一个创建枚举实现的示例。

  1. 创建一个扩展 Enum 的类。
  2. 将枚举值作为类常量添加,或者将枚举值列在一个名为 $enum 的受保护数组属性中。
class EnumImpl extends Enum
{
  //..Optional class constants containing enum values 
  const KEY1 = 'key1';
  const KEY2 = 'key2';

  //..Optional $enum array property containing all possible enum values
  protected array $enum = [
    self::KEY1,
    self::KEY2
  ];

  //..Optional default value
  protected string $value = self::KEY1;

  //..Optional change event can be used
  protected function onChange( $oldVal, $newVal ) : void
  {
    //..Do something on change
  } 
}
//..Creating an enum using only constants 
class EnumImpl extends Enum
{
  const KEY1 = 'key1';
  const KEY2 = 'key2';
}
//..Creating an enum using the $enum property
class EnumImpl extends Enum
{
  protected array $enum = [
    'key1',
    'key2'
  ];
}

枚举还可以包含额外的值(可选)

class ValuedEnumImpl extends Enum
{
  //..Optional class constants containing enum values 
  const KEY1 = 'key1';
  const KEY2 = 'key2';

  //..Required $enum array property containing all possible enum values.
  protected array $enum = [
    self::KEY1 => 'stored value 1',
    self::KEY2 => 'stored value 2'
  ];

  //..Optional default value
  protected string $value = self::KEY1;

  //..Optional change event can be used
  protected function onChange( $oldVal, $newVal ) : void
  {
    //..Do something on change
  } 
}

枚举使用

创建枚举实例并将其初始化为 KEY1

$enum = EnumImpl::KEY1();

创建枚举实例并将其初始化为 KEY1

$enum = new EnumImpl( EnumImpl::KEY1 );

创建枚举实例并将其初始化为 KEY1

$enum = new EnumImpl();
$enum->KEY1;

//..Or 
$enum->setValue( EnumImpl::KEY1 );

测试枚举是否等于某个值

if ( $enum->KEY1()) {
  // do something
}

if ( $enum->is( EnumImpl::KEY1 )) {
  // do something
}

获取枚举值

$enum->value();  //..returns 'key1'

//..Casting enum to a string will return a string equal to the current enum value:
echo (string)$enum;  //..Prints 'key1'

设置枚举值

$enum->KEY2;  //..The enum now has a value of "key2"
$enum->setValue( EnumImpl::KEY2 );

测试成员是否有效

if ( $enum->isValid( EnumImpl::KEY2 )) {
  // this is valid
}

测试枚举是否等于同一类类型的另一个枚举

if ( $enum->equals( $enum2 )) {  
  //..$enum2 is of the same type and has the same value as $enum
}

检索枚举常量到值的映射

//..Outputs ['KEY1' => 'key1', 'KEY2' => 'key2']
$constants = $enum->constants();

检索常量列表

//..Outputs: ['KEY1','KEY2']
$constants = $enum->keys();

列出所有可用的枚举值

//..Outputs: ['key1','key2'];
$values = $enum->values();

对 IEnum 列表进行排序

usort( $enumList, function( IEnum $a, IEnum $b ) {
  return $a->compare( $b );
});

带值的枚举

可以将任意值附加到枚举成员上。这是通过初始化 $enum 数组属性为映射来实现的,其中键是枚举键,值是任意值。

使用

$enum = new ValuedEnumImpl( ValuedEnumImpl::KEY1 ); //..Create a new valued enum equal to 'key1'
$enum->getStoredValue(); //..Returns 'stored value 1'

//..If you want to retrieve the enum value by stored value:
$enum->getByStoredValue( 'stored value 1' ); //..returns 'key1'


//..Retrieve a list of all stored values:
$enum->getStoredValues(); //..Returns ['stored value 1', 'stored value 2']

将枚举用作状态机

枚举非常适合用作状态机。枚举实现包括几个用于此目的的方法。这实际上将枚举变成了一个索引数组,并有一个指向单个活动值的引用。

获取枚举值的索引

$index = $enum->indexOf( EnumImpl::KEY1 ); //..returns 0 

有了索引值,我们可以移动到下一个和上一个。如果已经到达末尾/开始位置,则调用 next 或 previous 时不会采取任何操作。

$enum = new EnumImpl( EnumImpl::KEY1 );
$enum->moveNext(); //..$enum now equals 'key2'
$enum->movePrevious(); //..$enum now equals 'key1'
$enum->movePrevious(); //..$enum still equals 'key1' and no exception is thrown

使用索引值实现大于和小于

$enum2 = new EnumImpl( EnumImpl::KEY2 );

//..$enum has a value of 'key1'
$enum->greaterThan( $enum2 ); //..returns false.  
$enum->lessThan( $enum2 ); //..return true

可以使用字符串进行比较

$enum->greaterThanValue( EnumImpl::KEY2 ); //..return false
$enum->lessThanValue( EnumImpl::KEY2 ); //..returns true

测试枚举是否从某个值更改为不同的值

$enum = new EnumImpl( EnumImpl::KEY1 );
$enum->changedFromTo( EnumImpl::KEY1, EnumImpl::KEY2 ); //..returns false 
$enum->setValue( EnumImpl::KEY2 );
$enum->changedFromTo( EnumImpl::KEY1, EnumImpl::KEY2 ); //..returns true 

如果您只想知道枚举是否在任何时候更改为某个状态,则调用 changedTo()。

$enum->changedTo( EnumImpl::KEY2 ); //..Returns true 

检索更改日志。这是枚举在其生命周期期间所经历的每一次更改的记录。

$enum->getChanges(); //..Returns [['key1' => 'key2']] when using above example

枚举事件

可以将更改事件附加到枚举对象或添加到从 Enum 继承的类中。

$enum = new EnumImpl( EnumImpl::KEY1 );

//..Add a change event.  Multiple events can be added.
$enum->setOnChange( function( IEnum $enum, string $old, string $newVal ) : void {
  //..Do something on change.

  //..Optionally, throw any exception to roll back the change.  
  //..The change log will not list failed changes.
  throw new \Exception( 'No change for you' );
});

try {
  //..An exception will be thrown here due to the change event.
  $enum->setValue( EnumImpl::KEY2 );
} catch( \Exception $e ) {
  //..Do nothing
}

$enum->value(); //..This will output 'key1' since the change event throws an exception.

如果枚举实现覆盖了 onChange(),则遵循上述相同的规则。抛出异常以回滚。

在运行时创建枚举。

这可以通过使用 RuntimeEnum 类来实现。

例如,假设我们想要创建一个具有两个可能值的枚举,并设置初始值。我们可以这样做

//..创建一个新的具有两个值并初始化为 'key1' 的枚举

$enum = new RuntimeEnum( ['key1', 'key2'], 'key1' );
 
$enum->value(); //..returns 'key1'

//..Change the value 
$enum->setValue( 'key2' ); 
$enum->value(); //..returns 'key2'

位集合

位集合是对单个整数的包装,然后可以将其用作 32 或 64 个独立的布尔值,并包含用于操作位的方法。

创建一个新的空位集合。这将根据安装的 PHP 版本具有 32 或 64 个可能值。

$b = new BitSet( 0 );  

启用位

$b = new BitSet( 0 );
$b->enable( 0x2 ); //..Bit 2 is now enabled 
$b->enableAt( 2 ); //..Bit 2 is enabled 
$b->setValue( 2 ); //..Bit 2 is enabled (This is the sum of all enabled bits)

禁用位

$b->disable( 0x2 ); //..Bit 2 is now disabled 
$b->disableAt( 2 ); //..Bit 2 disabled
$b->setValue( 0 ); //..All bits are disabled 

切换位

$b->enable( 0x2 ); //..Enable bit 2
$b->toggle( 0x2 ); //..Bit 2 is now disabled 

测试位是否启用

$b->enable( 0x2 );
$b->isEnabled( 0x2 ); //..Returns true
$b->isEnabledAt( 2 ); //..Returns true 

获取内部值 内部值是所有启用位的总和

$b->clear(); //..Disables all bits 
$b->getValue(); //.Returns zero
$b->enable( 0x1 ); //..Enable bit 1
$b->getValue(); //..Returns one
$b->enable( 0x2 ); //..Enable bit 2
$b->getValue(); //..Returns three
$b->disable( 0x1 ); //..Disable bit 1
$b->getValue(); //..Returns two 

集合

虽然位集很有用,但如果我们可以给位命名会更好吗?如果你喜欢命名事物,并且想让所有位都有名字,那么Set类就是你的选择!

Set实现是一个带有命名位的BitSet。

最初,这个设计是为了与MySQL中的集合列类型一起工作,现在它仍然可以用于此!

以下是它的工作原理

  1. 创建一个新的类,该类继承自Set
  2. 为每个位添加类常量,并将值设置为某些字符串值。
  3. 将这些常量添加到名为$members的保护数组属性中。
class SetImpl 
{
  const BIT1 = 'bit1';
  const BIT2 = 'bit2';

  protected array $members = [
    self::BIT1,
    self::BIT2
  ];
}

创建Set实例

创建一个新的集合并启用零位

$set = new SetImpl();

创建一个新的集合并启用两个位

$set = new SetImpl( SetImpl::BIT1, 'bit2' );

创建一个新的集合并启用两个位

$set = new SetImpl( ['bit1', 'bit2'] );

启用位

$set = new SetImpl();

$set->add( SetImpl::BIT1 ); //..Use the add method
$set->add( 0x1 ); //..Add method also accepts integers 
$set->BIT1 = true; //..Use magic.  The key is the class constant.  
$set->bit1 = true; //..Using magic, but with the bit's name and not the class constant 

禁用位

$set = new SetImpl();
$set->remove( SetImpl::BIT1 ); 
$set->remove( 0x1 );
$set->BIT1 = false;
$set->bit2 = false;

检索位值

$set = new SetImpl();
$set->BIT1; //..returns 1
$set->bit1; //..returns 1

测试位

测试所有指定的位是否为集合的有效成员

$set = new SetImpl();
$set->isMember( 'bit1' ); //..returns true 
$set->isMember( 'bit1', 'bit2' ); //..returns true

测试位是否启用

$set = new SetImpl();
$set->add( 'bit1' ); 
$set->hasVal( 'bit1' ); //..Returns true
$set->hasVal( 'bit1', 'bit2' ); //..Returns false 

测试集合是否没有启用位

$set = new SetImpl();
$set->isEmpty(); //..Returns true 

测试是否启用一个或多个值

$set = new SetImpl( 'bit1' );
$set->hasAny( 'bit1', 'bit2' ); //..Returns true since bit1 is enabled 

在运行时添加新的Set成员

假设你有一个包含2个成员的集合,但你忘记了实际上你需要3个成员。没问题!我们可以做这样疯狂的事情

$set = new SetImpl();
$set->addMember( 'bit3' ); //..Add a new member to the bit set.
$set->isMember( 'bit3' ); //..Returns true 
$set->bit3; //..returns 0x3 

检索所有可用位的名称列表

$set = new SetImpl();
$set->getMembers(); //..returns ['bit1', 'bit2'];

检索所有启用位名称的列表

$set = new SetImpl();
$set->add( 'bit1' );
$set->getActiveMembers();  //..Returns ['bit1'];

如果你想检索集合的总值(表示所有可能位的整数),可以调用getTotal()。

$set = new SetImpl();
$set->getTotal(); //..Returns 3

如果你想动态创建一个Set,而不想创建一个类,那么可以使用RuntimeSet。

示例

创建一个新的Set,包含两个名为bit1和bit2的位,并将bit1设置为启用

$set = new RuntimeSet( ['bit1', 'bit2'], 'bit1' );

$set->isMember( 'bit1' ); //..returns true
$set->hasVal( 'bit1' ); //..returns true 

还可以通过使用MapSet类为Set中的每个位添加一个值。这向ISet添加了get()方法,并可以用来检索附加到命名位的值。

使用

class MapSetImpl extends MapSet
{
  protected array $members = [
    'bit1' => 'value1',
    'bit2' => 'value2'
  ];
}

$set = new MapSetImpl();
$set->get( 'bit1' ); //..returns 'value1'

大集合

大集合是一个可以处理32或64个以上元素但失去执行位操作能力的集合,因为集合不再由单个整数支持。

这基本上与ISet相同,但不是由BitSet支持。

内部上,它维护一个ISet实例列表,大集合的每个成员映射到内部集合中的一个位。