PHP

すごい遠回りした。結論から書くと、「base64エンコード」がそれです。
Webアプリケーションで使用するなどでURLのクエリストリング内で使うような場合は「base64urlエンコード」を使いましょう。(base64エンコードしたものを更にurlencodeしても良いですが…)
これはPHP組み込み関数には存在しないので自分で定義。
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function base64url_decode($data) {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
(PHP: base64_encode - Manual のコメント欄より引用)
以上。

続きを読む
ここではPHPのurlencode()の話をするが、URLエンコードなので他の言語でも共通と思われる。
URLエンコードする時、半角数字やアルファベットなんかはそのまま返ってくるよね。あと、一部の記号もそのままスルーされるようだ。
この際、どの文字がそのままになるのか、調べてみよう。
実際に1文字ずつurlencode()に突っ込んで試してみた。
$str = '';
for ($i = 0; $i < 256; $i++) {
$c = chr($i);
if ($c === urlencode($c)) $str .= $c;
}
var_dump($str);
↓結果
string(65) "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"

数字、アルファベット大文字小文字と、記号3つ「-」「.」「_」。計65文字でした。
PHPのshuffle()が連想配列では使えない件について(@2011/10/7 0:12)

↓こんな連想配列があったとして、
$AmericanLeagueEastDivisionTeams = array(
'BAL' => array('city' => 'Baltimore', 'name' => 'Orioles'),
'BOS' => array('city' => 'Boston', 'name' => 'Red Sox'),
'NYY' => array('city' => 'New York', 'name' => 'Yankees'),
'TB' => array('city' => 'Tampa Bay', 'name' => 'Rays'),
'TOR' => array('city' => 'Toronto', 'name' => 'Blue Jays'),
);

shuffle()してみると…
shuffle($AmericanLeagueEastDivisionTeams);
var_export($AmericanLeagueEastDivisionTeams);

↓結果(見やすいように整形済み)
array (
0 => array ('city' => 'Boston', 'name' => 'Red Sox'),
1 => array ('city' => 'Toronto', 'name' => 'Blue Jays'),
2 => array ('city' => 'New York', 'name' => 'Yankees'),
3 => array ('city' => 'Tampa Bay', 'name' => 'Rays'),
4 => array ('city' => 'Baltimore', 'name' => 'Orioles')
)

はい。キーが破棄されて、数字キーに割り当て直されちゃいます。

じゃあ、array_rand()でランダム順のキーを取得しよう、と思った。

$len = count($AmericanLeagueEastDivisionTeams);
$abbrs = array_rand($AmericanLeagueEastDivisionTeams, $len);
var_export($abbrs);

↓結果
array (
0 => 'BAL',
1 => 'BOS',
2 => 'NYY',
3 => 'TB',
4 => 'TOR',
)

よし、この「キーがランダム順に入った配列」を使おう。
…あれ?並べ変わってなくね?
たまたまかな(^o^;

再実行→やっぱりおんなじ→再々実行→やっぱりおんなじ→(無限ループ)
全件取得する場合は並べ換えて紅の豚?
マニュアルに書いといてよ。そんなこと。。

まあ、array_keys()を使ってそれをshuffle()すれば同じ事はできるか。

$abbrs = array_keys($AmericanLeagueEastDivisionTeams);
shuffle($abbrs);
var_export($abbrs);

↓結果
array (
0 => 'NYY',
1 => 'BOS',
2 => 'TB',
3 => 'BAL',
4 => 'TOR',
)

当然ながらうまくいく。再実行すればまた並び変わる。

せっかくだから、関数化しておくか。
PHPオリジナルの命名則にあわせて、ashuffle()と名付ける(※)。
オリジナルのshuffle()が並べ替えが成功したかどうかを返しているので、とりあえずtrueを返すことにする。

function ashuffle(&$array) {
$keys = array_keys($array);
shuffle($keys);

$res = array();
for ($i = 0, $_l = count($keys); $i < $_l; $i++) {
$res[$keys[$i]] = $array[$keys[$i]];
}

$array = $res;
return true;
}

これで、

ashuffle($AmericanLeagueEastDivisionTeams);

$rank = 0;
foreach ($AmericanLeagueEastDivisionTeams as $abbr => $team) {
printf("%d: %s %s (%s)\n", ++$rank, $team['city'], $team['name'], $abbr);
}

↓結果
1: New York Yankees (NYY)
2: Baltimore Orioles (BAL)
3: Tampa Bay Rays (TB)
4: Boston Red Sox (BOS)
5: Toronto Blue Jays (TOR)

成功。


※:蛇足。
この記事を書く時に、そういえば、もうすでに誰かが作ってるよな。"ashuffle()"って名前で。
っと思ってググってみたら、どうやら無いみたい。。
ってことは、このページがひっかかるようになるんだ。ウシシ( ̄ー ̄)
PHP: 新機能 - Manual (PHP 5.6.x から PHP 7.0.x への移行)

上記ページの「Null 合体演算子」の項を読んでみる。
$username = $_GET['user'] ?? 'nobody';
// 上のコードは、次のコードと同じ意味です。
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

$baz = 3;
$qux = 4;
echo $foo ?? $bar ?? $baz ?? $qux; // 出力は 3 です

めっちゃ便利やんけっ!

実はこの記事を下書き保存して半月以上経過したのだが、いまだ使用機会ナシ。
だって、例えば
$bar = false;
$baz = 3;
$qux = 4;
var_dump($foo ?? $bar ?? $baz ?? $qux); // 出力は bool(false) です

のように、falseと評価される値が入っていた場合の扱いが、希望にそぐわなかった。
こんな感じで「エラー制御演算子」と併せて「三項演算子の短縮形」を使用しましたとさ。
$bar = false;
$baz = 3;
$qux = 4;
var_dump(@$foo ?: @$bar ?: @$baz ?: @$qux); // 出力は int(3)です


「三項演算子の短縮形」には新しい名前をつけてあげなかったんだね。
まあ、『三項演算子を短縮したもの』だからねぇ。
「Null 合体演算子」のほうはそんな表現ができない、新しい演算だから、新しい名前が必要だった、ということか。


ところで、次の項にある「宇宙船演算子」って。。
「spaceship operator」の和訳なんだねー。
<=> この形が宇宙船に見えたんだろうねー。
カッコいいねー。
でも括弧じゃないねー。不等号だねー。

…なんだこの記事(-_-;)
ログを取るため、データベースにオブジェクトをserializeして格納した。

テスト用のプログラムでデータベースから取り出したものを読み込んで、unserializeした。

おー、ちゃんと復元できとるやん!
ん?待てよ。なんか中途半端に復元してないか?
元のプログラムで、serializeしたものをその場でunserializeして表示してみたら、ちゃんと復元できる。

※ここから間違い
あー、データベースに格納する時、テキスト型で格納したからうまくいかなかったのか!
※あとから思えば、もしそうだったらunserializeがエラーになるはずやろ。。。

よーし、blob型にしてもう一度挑戦だ!

やっぱりダメだ(T_T)

復元したものを改めてよく見ると、なんか「__PHP_Incomplete_Class」なるクラスのインスタンスとして復元されとるやん。

それググれ!
うーん、なんか、unserializeの前にインクルードしましょう、的なヤツしかヒットせぇへん。
わしが欲しいんは、データベースに格納したら壊れるゆう話なんや!
※狂っとる(-_-;)

数十分格闘後、
あれ?そういえばクラス定義されてなくない?
そら復元できるわけあらへんやろがな…orz

その後、composer関連でのミスもあってすんなりいかなかったが、結局、、、
ググって出てきた『unserializeの前にインクルードしましょう、的なヤツ』が正解でしたやん。。。

PEBCAKは続くよどこまでも………………
(鬱の呼吸壱の型三点リーダー六連!)