質問
ビットとビットマスクは、私がしばらく理解するのに苦労してきたものですが、PHPの設定やそのようなものにそれらを使用する方法を学びたいと思います。
私はついにそれを正確に行うと主張するクラスを見つけました、そして、私が言うことができるように、それはうまくいくようですが、それがこれを行う最良の方法であるかどうかはわかりません。以下のサンプルコードを備えたクラスファイルを順番に表示します。
経験がある場合は、パフォーマンスなどのために改善できるかどうかを教えてください。私は本当にこれを学びたいと思っています、そして私はそれを読んでいますが、これまで私が把握するのは難しいです。
クラス...
<?php
class bitmask
{
/**
* This array is used to represent the users permission in usable format.
*
* You can change remove or add valuesto suit your needs.
* Just ensure that each element defaults to false. Once you have started storing
* users permsisions a change to the order of this array will cause the
* permissions to be incorectly interpreted.
*
* @type Associtive array
*/
public $permissions = array(
"read" => false,
"write" => false,
"delete" => false,
"change_permissions" => false,
"admin" => false
);
/**
* This function will use an integer bitmask (as created by toBitmask())
* to populate the class vaiable
* $this->permissions with the users permissions as boolean values.
* @param int $bitmask an integer representation of the users permisions.
* This integer is created by toBitmask();
*
* @return an associatve array with the users permissions.
*/
public function getPermissions($bitMask = 0)
{
$i = 0;
foreach ($this->permissions as $key => $value)
{
$this->permissions[$key] = (($bitMask & pow(2, $i)) != 0) ? true : false;
// Uncomment the next line if you would like to see what is happening.
//echo $key . " i= ".strval($i)." power=" . strval(pow(2,$i)). "bitwise & = " . strval($bitMask & pow(2,$i))."<br>";
$i++;
}
return $this->permissions;
}
/**
* This function will create and return and integer bitmask based on the permission values set in
* the class variable $permissions. To use you would want to set the fields in $permissions to true for the permissions you want to grant.
* Then call toBitmask() and store the integer value. Later you can pass that integer into getPermissions() to convert it back to an assoicative
* array.
*
* @return int an integer bitmask represeting the users permission set.
*/
function toBitmask()
{
$bitmask = 0;
$i = 0;
foreach ($this->permissions as $key => $value)
{
if ($value)
{
$bitmask += pow(2, $i);
}
$i++;
}
return $bitmask;
}
}
?>
ビットマスク値として権限を設定/保存するにはどうすればよいですか?
<?php
/**
* Example usage
* initiate new bitmask object
*/
$perms = new bitmask();
/**
* How to set permissions for a user
*/
$perms->permissions["read"] = true;
$perms->permissions["write"] = true;
$perms->permissions["delete"] = true;
$perms->permissions["change_permissions"] = true;
$perms->permissions["admin"] = false;
// Converts to bitmask value to store in database or wherever
$bitmask = $perms->toBitmask(); //in this example it is 15
$sql = "insert into user_permissions (userid,permission) values(1,$bitmask)";
echo $sql; //you would then execute code to insert your sql.
?>
ビット値に基づいてビットマスク値を取得し、各配列項目の真/偽を返す例....
<?php
/**
* Example usage to get the bitmask value from database or session/cache.... then put it to use.
* $permarr returns an array with true/false for each array value based on the bit value
*/
$permarr = $perms->getPermissions($bitmask);
if ($permarr["read"])
{
echo 'user can read: <font color="green">TRUE</font>';
}
else {
echo 'user can read: <font color="red">FALSE</font>';
}
//user can WRITE permission
if ($permarr["write"])
{
echo '<br>user can write: <font color="green">TRUE</font>';
}
else {
echo '<br>user can write: <font color="red">FALSE</font>';
}
?>
解決
ビットフィールドは、フラグやブール値のセットを一般的に処理するための非常に便利で効率的なツールです。
それらを理解するには、最初にバイナリ番号がどのように機能するかを知る必要があります。その後、マニュアルエントリをチェックアウトする必要があります ビットワイズ演算子 そして、あなたがどのように少し、または左/右シフトがどのように機能するかを知っていることを確認してください。
少しフィールドは整数値にすぎません。ビットフィールドのサイズが固定されていて、1つのバイトのみであると仮定しましょう。コンピューターはバイナリ番号で動作するので、私たちの数字の値が 29
, 、あなたは実際に見つけるでしょう 0001 1101
記憶の中で。
ビットワイズを使用して(&
)そしてビットワイズまたは(|
)番号の各ビットを個別に読み取り、設定できます。どちらも2つの整数を入力として取り、各ビットで個別に実行します。
あなたの番号の最初のビットを読むために、あなたは次のようなことをすることができます:
0001 1101 (=29, our number)
& 0000 0001 (=1, bit mask)
= 0000 0001 (=1, result)
ご覧のとおり、私たちが興味を持っているビットだけが設定されている特別な番号が必要です。それはいわゆる「ビットマスク」です。私たちの場合、それはそうです 1
. 。 2番目のビットを読み取るには、ビットマスクの1桁を左に「プッシュ」する必要があります。左シフト演算子でそれを行うことができます($number << 1
)または2つを掛けることによって。
0001 1101
& 0000 0010
= 0000 0000 (=0, result)
あなたは私たちの番号のすべてのビットでそれをすることができます。バイナリと私たちの数とビットマスクはゼロにつながります。つまり、ビットは「設定」されていないか、ゼロ以外の整数につながります。これは、ビットが設定されたことを意味します。
ビットのいずれかを設定する場合は、ビットごとに使用できます。
0001 1101
| 0010 0000 (=32, bit mask)
= 0011 1101 (=29+32)
ただし、少し「クリア」したい場合は、別の方法で行かなければなりません。
より一般的なアプローチは次のとおりです。
// To get bit n
$bit_n = ($number & (1 << $n)) != 0
// Alternative
$bit_n = ($number & (1 << $n)) >> $n
// Set bit n of number to new_bit
$number = ($number & ~(1 << $n)) | ($new_bit << $n)
最初は少し不可解に見えるかもしれませんが、実際には非常に簡単です。
今では、おそらくビットフィールドが非常に低レベルのテクニックであることがわかりました。だから私はそれらをPHPまたはデータベース内で使用しないことをお勧めします。
あなたが投稿したクラスは私には少し特別に見えます。たとえば、ようなもの ... ? true : false
悪い練習です。ビットフィールドを使用する場合は、おそらく定数を定義して上記の方法を使用する方が良いでしょう。簡単なクラスを思い付くのは難しくありません。
define('PERM_READ', 0);
define('PERM_WRITE', 1);
class BitField {
private $value;
public function __construct($value=0) {
$this->value = $value;
}
public function getValue() {
return $this->value;
}
public function get($n) {
return ($this->value & (1 << $n)) != 0;
}
public function set($n, $new=true) {
$this->value = ($this->value & ~(1 << $n)) | ($new << $n);
}
public function clear($n) {
$this->set($n, false);
}
}
$bf = new BitField($user->permissions);
if ($bf->get(PERM_READ)) {
// can read
}
$bf->set(PERM_WRITE, true);
$user->permissions = $bf->getValue();
$user->save();
私はこの答えのコードを試していませんでしたが、箱から出していない場合でも、それを始める必要があります。
ビットフィールドごとに32の値に制限されていることに注意してください。
他のヒント
ビットマスクを定義する方法は次のとおりです。
// the first mask. In binary, it's 00000001
define('BITWISE_MASK_1', 1 << 0); // 1 << 0 is the same as 1
// the second mask. In binary, it's 00000010
define('BITWISE_MASK_2', 1 << 1);
// the third mask. In binary, it's 00000100
define('BITWISE_MASK_3', 1 << 2);
ビットマスクが存在するかどうかを確認するには(この場合は関数引数で)、ビットワイズと演算子を使用します。
function computeMasks($masks) {
$masksPresent = array();
if ($masks & BITWISE_MASK_1)
$masksPresent[] = 'BITWISE_MASK_1';
if ($masks & BITWISE_MASK_2)
$masksPresent[] = 'BITWISE_MASK_2';
if ($masks & BITWISE_MASK_3)
$masksPresent[] = 'BITWISE_MASK_3';
return implode(' and ', $masksPresent);
}
これは、あなたが1つのバイト(たとえば、 00000001
と 00010000
)、あなたは2つを一緒にします: 00010001
. 。あなたと結果と元のマスク(00010001
そして、言います、 00000001
)、マスクが存在する場合はマスクを取得します(この場合は 00000001
)。それ以外の場合は、ゼロになります。