[PHP]ビット演算を使ったフラグ処理(2進数のビット操作)

PHP を使った動的なサイトでは、「はい(true)」か「いいえ(false)」で表すことの出来るビットフラグを扱うことは多いと思います。
仮にオプション A~E があり、そのうち A, C, E だけ有効になっている状態を表す時、2進数を使ったほうが簡単な場合が有ります。

bit

<?php
// 定数の設定
class OptionInfo {
	const NONE = 0b0;
	const OPTION_A = 0b00001;
	const OPTION_B = 0b00010;
	const OPTION_C = 0b00100;
	const OPTION_D = 0b01000;
	const OPTION_E = 0b10000;
}

$option = OptionInfo::NONE; // 初期値

// フラグを設定
$option |= OptionInfo::OPTION_A;	// Aを有効にする
$option |= OptionInfo::OPTION_C;	// Cを有効にする
$option |= OptionInfo::OPTION_E;	// Eを有効にする

// 有効になっているフラグを表示
if($option == OptionInfo::NONE) echo 'NONE';
if($option & OptionInfo::OPTION_A) echo 'A';
if($option & OptionInfo::OPTION_B) echo 'B';
if($option & OptionInfo::OPTION_C) echo 'C';
if($option & OptionInfo::OPTION_D) echo 'D';
if($option & OptionInfo::OPTION_E) echo 'E';
出力結果: ACE

オプションの A, C, E が有効の時、各ビットが「EDCBA」の順で並んでいるので2進数表記では「10101」のようになります。
それぞれ A=00001, B=00010, C=00100, D=01000, E=100000 という割り当てをしています。
わかりやすくするために定数としてクラスの形で名前をつけています。数字の頭に「0b」をつけるとその数字は2進数のものとして扱われます。

例ではわかりやすくするために直接2進数を使っていますが、現実的にはビットシフト「<<」「>>」を使うほうが扱いやすいかもしれません。(結果は同じです。)

class OptionInfo {
	const NONE = 0b0;
	const OPTION_A = 0b1;
	const OPTION_B = 0b1 << 1;
	const OPTION_C = 0b1 << 2;
	const OPTION_D = 0b1 << 3;
	const OPTION_E = 0b1 << 4;
}

「0b1」は2進数での「1」です。「0b1 << 3」とすると左に 3 ビットシフトして「1000」(2進数表記)となります。全体に3桁左にずれることになります。(厳密には2を3回かけています)

特定のビットの 有効/無効 を切り替えるには次のようにします。

・指定したフラグをオン

$option |= OptionInfo::OPTION_A;

・指定したフラグをオフ
※右辺の「~」に注意

$option &= ~OptionInfo::OPTION_A;

・指定したフラグを反転(XOR)

$option ^= OptionInfo::OPTION_A;

フラグがオンになっているかを調べる場合次のようにします。

if($option & OptionInfo::OPTION_A)

[PHP]正規表現の全体一致は「^~$」だけでは不十分

preg_match() などの正規表現で「abcd」に一致するパターンの書き方は「^abcd$」というのが一般的ですが、ラインフィードなどの記号が混じっていた場合すり抜けてしまうケースが有るようです。

[参考サイト]
「正規表現によるバリデーションでは ^ と $ ではなく \A と \z を使おう」
http://blog.tokumaru.org/2014/03/z.html

<?php
$value = "abcd\n";
echo strlen($value);

if( preg_match('/^abcd$/', $value) ){
	echo 'OK';
} else {
	echo 'NG';
}

if( preg_match('/\Aabcd\z/', $value) ){
	echo 'OK';
} else {
	echo 'NG';
}

上が「^~$」を使った通常のパターンで、下は「\A~\z」を使ったパターンです。
通常のパターンではチェックをすり抜けて「OK」となってしまいますが、改行コードが末尾に付いているため文字数をカウントすると5文字になっています。
これは GET で「index.php?value=abcd%0a」のように受け取る場合でも同様です。

[PHP]htmlspecialchars()を行った上でprintf()する

PHP で html を出力するする際、htmlspecialchars() を使ってクロスサイトスクリプティング(XSS)対策を行うのは基本的ですが、printf() や sprintf() で出力をする場合全ての値に htmlspecialchars() を行うと可読性が著しく下がってしまうので、特殊文字を変換した上で printf()、sprintf() をするための関数を作ってみました。

<?php
printfh('<a href="%s" class="%s">%s</a>',
	'https://php-archive.net','sample', '<test>');

function printfh(){
	$args = func_get_args();
	$text = array_shift($args);
	$args = array_map('h', $args);
	vprintf($text, $args);
}

function sprintfh(){
	$args = func_get_args();
	$text = array_shift($args);
	$args = array_map('h', $args);
	return vsprintf($text, $args);
}

function h($str, $encoding=null){
	if(empty($encoding)) $encoding = mb_internal_encoding();
	$str = htmlspecialchars($str, ENT_QUOTES, $encoding);
	return $str;
}
実行結果: 
<a href="https://php-archive.net" class="sample">&lt;test&gt;</a>

使い方は printf() などとほぼ同じです。
第一引数にフォーマットを記入し、第二引数以降に代入する文字を追加していきます。(可変長引数)