buffalokiwi / buffalotools_types
PHP 7.4 的枚举和集合
Requires
- php: >=7.4.0
Requires (Dev)
- phpunit/php-invoker: ^2.0
- phpunit/phpunit: ^8
README
一个极其有用的包,包含用于 PHP 7.4 的 Enum、BitSet、Set 和 BigSet 类型。
MIT 许可证
目录
安装
composer require buffalokiwi/buffalotools_types
PHP 7.4 的枚举状态机
在不使用扩展的情况下,PHP 缺少枚举类型。这是一个快速的枚举实现,同时也是一个状态机。
枚举是抽象的,必须扩展以创建枚举。可以使用 RuntimeEnum 在运行时创建 Enum 对象。
以下是一个创建枚举实现的示例。
- 创建一个扩展 Enum 的类。
- 将枚举值作为类常量添加,或者将枚举值列在一个名为 $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中的集合列类型一起工作,现在它仍然可以用于此!
以下是它的工作原理
- 创建一个新的类,该类继承自Set
- 为每个位添加类常量,并将值设置为某些字符串值。
- 将这些常量添加到名为$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实例列表,大集合的每个成员映射到内部集合中的一个位。