Closed TuxCoder closed 9 years ago
I like the basic idea to have a getter and setter for the internal bitset.
The issue what I have is that it's currently impossible to have a set of more than 32/64 enumerators because of the integer limitations. I was hoping that this will be abolished by https://wiki.php.net/rfc/bigint but as Andrea Faults has been left the community the RFC was never voted and it's too late for PHP7. So the limitations will exist for at least the next years.
So I'm wrapping my head about other ways solving this like with a bitset based on a binary string but PHP will cast binary strings to integers on left/right shift and the RFC solving this is looks dead. too. (https://wiki.php.net/rfc/string-bitwise-shifts)
Because I don't like to introduce a BC break if I find a good way resolving the integer bitset limitations I'm a bit deliberate on this change.
Do you have any other ideas?
ps. nice enum classes, thx for it.
Thanks for the compliments
I think it's not easy to implement it with more than int.
One way could be to look to the size of the enum an if its bigger than the size of int, use gmp for all operations. (https://php.net/manual/en/book.gmp.php)
The problem with this solution is that a php-extension is required and I don't know about the performance.
I was thinking something like that:
<?php
class EnumSet {
private $bitSize = 64; // machine depending
private $setBitFunc = 'setIntegerBit'; // enumeration and machine depending
// ...
public function attach($enumerator) {
$enumeration = $this->enumeration;
$this->{$this->setBitFunc}($enumeration::get($enumerator)->getOrdinal());
}
private function setIntegerBit($ordinal) {
$this->bitset |= 1 << $ordinal;
}
private function setBinaryBit($ordinal) {
$bit = pack('N', 1 << ($ordinal % $this->bitSize)) . str_repeat("\0", floor($ordinal / $this->bitSize));
$this->bitset |= $bit;
}
// ...
}
my suggestion is to implement it in a external interface/classes, so its easier to test and replace
interface BitSetManipulator{
/**
* @param $bitset mixed current bitset
* @param $bit number of bit to manipulate
* @return mixed new bitset
*/
public static function setBit($bitset,$bit);
/**
* @param $bitset mixed current bitset
* @param $bit number of bit to get
* @return boolean
*/
public static function getBit($bitset,$bit);
/**
* @param $bitset mixed current bitset
* @param $bit number of bit to set to false
* @return mixed new bitset
*/
public static function resetBit($bitset,$bit);
/**
* @param $bitset mixed current bitset
* @return string a binary string with the bitset
*/
public static function saveBitset($bitset);
/**
* @param $bitset string a binary string with the bitset
* @param $size int nummber of bits to save
* @return mixed bitset in class specific format
*/
public static function loadBitset($bitset,$size);
}
loadBitset(""); could be used to initialize the $bitset
can be implemented as int, array, gmp or otherwise.
one way is also to combine array and int:
public static function setBit($bitset,$bit) {
$bitset[floor($bit/(PHP_INT_SIZE*8))] |= 1<<$bit%(PHP_INT_SIZE*8);
or use the array/int way only, should be enough and is not so much to implement.
In the getBitset and setBitset, use a foreach and pack them into a binary string.
interface BitSet extends Serializable {
public function set($bit);
public function isset($bit);
public function unset($bit);
// serialize to binary string / unserialize from binary string
public function serialize();
public function unserialize();
}
class IntegerBitSet implements BitSet { /* ... */ }
class BinaryBitSet implements BitSet { /* ... */ }
class GmpBitSet implements BitSet { /* ... */ }
use it in EnumSet:
class EnumSet {
// ...
public function __construct(/*...*/) {
// ...
if (PHP_INT_SIZE * 8 >= $size) {
$this->bitset = new IntegerBitSet();
} else {
$this->bitset = extension_loaded('gmp') ? new GmpBitSet(); : new BinaryBitSet($size);
}
}
// ...
}
// init from / load into MySQL-SET
$hex = $db->query('SELECT HEX(`col`) FROM t')->fetchColumn(0);
$enumSet->getBitSet()->unserialize(hex2bin($hex));
$hex = bin2hex($enumSet->getBitSet()->serialize());
I made a implementation of an array/int structure and it works well. https://github.com/TuxCoder/php-enum/tree/master-bitset take a look and say what you think of it? I haven't test it yet on 32 bit php, that is needed!
in this implementation you can use it like:
$hex = $db->query('SELECT HEX(`col`) FROM t')->fetchColumn(0);
$enumSet->setBitset(hex2bin($hex));
$hex = bin2hex($enumSet->getBitset());
After i saw what kind of binary function we need, gmp is a kind of overkill for this. And now i think its not necessary to have different backends.
Yea this is a good and simple enough approve :+1: Can you create a new PR for this one so we can discus details better.
Thanks!
Some notes first:
made new pullrequest #51
Hello,
Often it's nessessary to save an EnumSet in a database or somewhere else. My thougt was to only save only the bitset, serilize works also but need a hug amout of space, is not such easy searchable, and i don't think it works well if you upgrade your enum and load an old version from the database.(probably also not work with that solution)
What do you think about a getter and setter method for bitset?
ps. nice enum classes, thx for it.