PHP5で定数の定義はどうするべきか

今まではPHPで定数といえば普通に大文字の変数名で定義しておくか、define関数を使って定義するかだったと思います。

変数の定義はのメリットとにかく手軽ということが挙げられます。制約などを考えずにいつもの変数通りに使えます。
逆にデメリットは変数であるということ。簡単に上書きできてしまいますし、グローバルな名前空間も汚してしまいます。「変」数なんです。「定」数ではありません。

//定数絡みの命名はよく大文字アンダーバー区切りが用いられる
$APPLE = 'リンゴ';
$ORANGE = 'オレンジ';

//もちろん配列や関数などによる定義も自由に可能
$FROUIT_LIST = array($APPLE, $ORANGE);

//上書きされる可能性もつきまとう
$ORANGE = 'グリーン';

もう一つのdefine関数の定義はしっかり定数です。define関数で定義する時、通常通り式による定義も出来ます。
デメリットとしては名前空間がグローバルなこと。変数とは名前は別扱いですがいろんなファイルやライブラリでどんどん定数を定義していくと重複の可能性も出てきますし、近年の名前空間を取り入れる流れからするとあんまりよくないです。PHPであらかじめ定義されている定数も結構な数あるためちょっと使いにくい。

define('ECODE_NOT_FOUND', 100);
define('ECODE_BAD_REQUEST', 200);
//式や関数による定義も可能
define('NEXT_MONTH', date('n', mktime(0, 0, 0, date('n')+1, date('j'), date('Y'))));

if( !file_exists('C:\define.txt') ) {
    //通常の変数と同じように値の参照が可能
    echo 'エラーコード:' . ECODE_NOT_FOUND
    die();
}

PHP5ではオブジェクト指向の機能が本格的に強化されJavaのような要素もじゃんじゃん取り入れられています。
この記事を書いている現在はPHP5.3が主流かと思います。オブジェクト指向化の中でクラス定数というものも出てきました。位置付けとしてはクラスに属する定数です。クラス定数はクラスの中でconstキーワードを用いて定義します。こんな感じで使えます。

//クラス定数を定義する専用のクラスを作るのもあり
class CONFIG {
    const TOPPAGE_URL = 'http://https://sousaku-memo.net/';
    const DEBUG_MODE = true;
    //以下の定義はエラーが発生するので注意
    //配列や式(Expression)による柔軟な定義が出来ないのが痛い…
    //const FAILD_ARRAY_DEFINE = array(10,20,4);
}

//参照するときは::(ダブルコロン)演算子を用いる。
//クラス名の接頭辞が付くことにより名前空間を汚さないのはGood
echo sprintf('創作メモ帳トップページ', CONFIG::TOPPAGE_URL);

んー、どれもメリットデメリットがあります…
PHP6にはクラス定数の強化を望みます。

自分はどうしているかというと、クラス定数はやっぱり式による定義が出来ないのがつらいのでdefine関数で定義をしています。また、あちこちで定義すると分からなくなるのでまとめてコメント付きで定義しています。

短縮URLなどのランダム文字列を作りたい

Youtube だったり bit.ly を見ていると URL の後ろに英数字の文字列がついていますよね。

色々方法はあると思いますが、そのままランダムな文字列をキーとしてデータベースに登録しても、テーブルに登録される情報が膨大になってくるとどうしても速度的な問題が発生してきます。

文字列をキーとして扱うのはデータベースにとって都合が悪いことが多いのです。

やはり高速なのは数値型の整数フィールドを主キーとして、その主キーを WHERE 句で指定して絞り込む方法です。

そこで、調べていていいなと思ったのは基数変換を使った方法。

例えば32進数や63進数、64進数などを使って、URL にはその N 進数の文字列を使ってアクセスします。

例: http://example.com/index.php?key=aY8rszM

データベースにはこのキー文字列を10進数に変換した値を主キーとして保存します。この主キーはデータベースのオートインクリメント機能を使って連番で割り振っていきます。

つまり、見た目はランダムな英数字の羅列ですが内部的には10進数の連番というわけです。Youtube の動画とかもランダムに英数字を割り当ててるように見えて実は auto_increment の整数らしいです。

PHP などでも N 進数と10進数の相互基数変換さえ出来てしまえば実現できるはず。

ちなみにこの方法で本当にランダムな順番で割り振るには、例えば主キーの管理テーブルを作る方法があります。

レコード ID と使用フラグを管理する使用表のテーブルを作って、あらかじめ大量のレコード数を格納しておきます。

key_master
record_id(int) used_flag(bool)
1 0
2 0
3 0
4 0
5 0

そして新たなレコードを挿入する度にこのテーブルから未使用の番号を一件ランダムで取り出し、それをキーとしてデータを挿入します。

以下はランダムに取り出すときの MySQL のサンプルクエリです。

SELECT `record_id` FROM `key_master`
WHERE `used_flag` = 0
ORDER BY rand() LIMIT 1;

この取り出した ID のレコードを挿入して、それを基数変換した文字列のページを生成します。

使用後は忘れずに used_flag にフラグを立てて更新します。同じトランザクション内で処理するのが安全だと思います。

他にもっといい方法ありそうですが。何かあったらまた更新します。

PHPのforeach構文による配列のアクセス順

PHPの場合はforeach構文で配列にアクセスする場合、
要素の追加順にアクセスされます。

$letters[0] = 'A';
$letters[1] = 'B';
$letters[3] = 'D';
$letters[2] = 'C';

foreach ($letters as $letter) {
    print $letter;
}

これで出力するとABDCとプリントされます。
数値キー順に順番にアクセスされるわけではないということです。

なので数値順にアクセスすることを保証したい場合
for構文を利用すること。