以前にCakePHP3でそれっぽいものを作りましたが、今回はWordPressで作成してみました。
今回のサイトは4桁までの半角英数字の逆変換に対応しています。5桁目以降は迷っています。。
また2桁~4桁のMD5ハッシュデータを公開しています。
以下が作成したサイトになります。
MD5の逆変換について
WordPressのテーマをCocoonという最近人気のものにしたことでずいぶん見栄えが良くなった気がします。このブログも近いうちにCocoonに変えたいと思いました。
WordPressで任意のPHPを実行する方法について
MD5の変換、逆変換のページは本文内にショートコードからphpファイルをインクルードするという手法を取っています。セキュリティ的に結構怖い。
参考:プラグインを使わず WordPress の投稿ページで PHP の外部ファイルを呼び出す方法
参考:WordPress記事内でプラグインを使わずにPHP,JavaScriptプログラムを動かす
DBのテーブルについて
MD5のハッシュデータを格納するテーブルは256テーブルに分けています。MD5のハッシュデータは0~fの16進数なので16^2=256という計算になります。
4桁までのMD5ハッシュデータを網羅した状態でも1テーブルあたりは約58000レコードだったので適当にセレクトしてみても結果の取得にもたつくことはありませんでした。思い付きで作った割には成功だったのかなーと思っています。ちなみに1テーブルだった場合との比較は行っていません。
3文字目までテーブル名に含めようとすると16^3で4096テーブルも必要になってしまうのでさすがに辞めました。テーブル内のハッシュ文字列の先頭2文字はテーブル名に含まれてるためレコード内に格納する必要がないとも言えますが、後で見返したときによくわからなくなりそうなので入れときました。
3桁のデータと4桁のデータを作成したときの処理時間です。
0-9a-zA-Zの62進数を入力に使用しているので4桁のデータを作成する際の処理時間を概算したときは67.37*62で4176秒くらいかなと思ってましたが実際は5581秒でした。
テーブルごとのレコードが増えてきたからなのかも。
サイト内で今回作成したMD5のハッシュデータを公開しています。
解凍した際のファイルサイズは以下のような感じです。
ここまで作っておいてですが正直使い道ほとんどないと思います。
というより探せば8桁くらいまで対応したAPIとかあるんじゃないかなと思います。
今回の制作物はGitのリポジトリをパブリックにしようと思ってましたがセキュリティ上公開しない方がいいファイルまでプッシュしちゃってるのでやっぱりプライベートで。
辞書データを作成するプログラムについて
md5テーブルの初期化内で16^2テーブルを削除しているのですが、以下のようにinformation_schemaデータベースを使用することでちょっと良い感じになりました。
// 既存テーブルの削除 $query = <<<EOD SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = database() AND TABLE_NAME LIKE 'md5_%'; EOD; $del_count = 0; $results = $wpdb->get_results($query); if ($results != null && count($results) > 0) { foreach ($results as $result) { if (!$wpdb->query('DROP TABLE ' . $result->TABLE_NAME . ';')) { throw new Exception("drop table error.\nquery : " . $result->DROP_QUERY); } $del_count++; } } if ($del_count > 0) { cmd_echo($del_count . " md5 table deleted.\n"); }
16^2テーブルを作成する際の処理は以下のような感じです。あとあと16^3に対応できるよう指数は定数にしています。
// md5テーブルを16^n個生成 $create_count = 0; $table_create_count = pow(16, MD5_PREFIX_NUM); for ($i = 0; $i < $table_create_count; $i++) { // テーブル名 $table_name = "md5_" . str_pad(dechex($i), MD5_PREFIX_NUM, 0, STR_PAD_LEFT); // テーブル生成 $query = <<<EOD CREATE TABLE `{$table_name}` ( `id` int(11) NOT NULL AUTO_INCREMENT, `md5_key` varchar(255) NOT NULL, `md5_hash` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `md5_encrypted_key_idx` (`md5_encrypted_key`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; EOD; if (!$wpdb->query($query)) { throw new Exception("create table error.\nquery : " . $query); } $create_count++; }
ハッシュデータのレコードが散らばってしまうので総数を管理するテーブルも作成しています。
// md5カウンタテーブルを生成 $query = <<<EOD CREATE TABLE `md5_counter` ( `id` int(11) NOT NULL AUTO_INCREMENT, `count` int(11) NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; EOD; if (!$wpdb->query($query)) { throw new Exception("create table error.\nquery : " . $query); } if (!$wpdb->insert('md5_counter', ['count' => 0])) { throw new Exception("md5_counter insert error."); } $create_count++; cmd_echo($create_count . " md5 table created.\n");
以下が今回メインのハッシュデータを登録しまくるプログラムです。
途中でこけたりしたときのことを考慮してトランザクションとロールバックをwpdbで呼び出しています。
また公開用のデータを作成するためにデータ総数が62^nとなったときに処理を中断する処理を入れています。
プログラム内の英語はGoogle翻訳です。
try { // コマンドライン引数から登録する件数を取得 $insert_count = get_md5_insert_count(1000, $argc, $argv); // トランザクションを開始 $wpdb->query("START TRANSACTION"); // 現在の登録数を取得 $result = $wpdb->get_col("SELECT count FROM md5_counter"); if (is_null($result) || count($result) <= 0) { throw new Exception("select md5_counter error."); } $start_count = intval($result[0]); // 引数で指定した件数分処理 $register_count = 0; for ($i = $start_count; $i < $start_count + $insert_count; $i++) { // // 配布用データ生成のための中断処理 // // 62^2のデータ登録が終わったら処理を終了 // if (3844 <= $i) { // cmd_echo("finished processing because the maximum of 2 characters (62 base numbers) was registered.\n"); // break; // } // // 62^3のデータ登録が終わったら処理を終了 // if (238328 <= $i) { // cmd_echo("finished processing because the maximum of 3 characters (62 base numbers) was registered.\n"); // break; // } // // 62^4のデータ登録が終わったら処理を終了 // if (14776336 <= $i) { // cmd_echo("finished processing because the maximum of 4 characters (62 base numbers) was registered.\n"); // break; // } // 62^5のデータ登録が終わったら処理を終了 if (916132832 <= $i) { cmd_echo("finished processing because the maximum of 5 characters (62 base numbers) was registered.\n"); break; } // 10進数を62進数に変換 $md5_before = dec_to_b62($i); // md5に変換 $md5_after = md5($md5_before); // 先頭n文字を抽出してインサート先のテーブル名を取得 $table_name = "md5_" . substr($md5_after, 0, MD5_PREFIX_NUM); // インサート処理 $insert_data = [ 'md5_key' => $md5_before, 'md5_hash' => $md5_after, ]; if (!$wpdb->insert($table_name, $insert_data)) { throw new Exception("insert error.\ntable : " . $table_name . "\ndata : " . print_r($insert_data)); } $register_count++; } if ($register_count > 0) { cmd_echo($register_count . " rows registerd.\n"); } // カウンタを更新 $wpdb->update("md5_counter", ['count' => $start_count + $register_count], ['id' => 1]); // DBをコミット $wpdb->query('COMMIT'); // 正常終了、処理時間を表示 cmd_echo("successful completion.\nprocessing time was " . round((microtime(true) - $time_start), 2) . " seconds.\n"); } catch (Exception $e) { // DBをロールバック $wpdb->query("ROLLBACK"); echo "Exception was thrown.\nwpdb rollbacked.\n" . $e->getMessage(); }
10進数を62進数に変換する処理は以下のブログを参考に作成しました。
参考:PHP:10進数を64進数にエンコード・デコードする。 – QWERTY.WORK
/** * 10進数を62進数に変換する * @param unknown $decnum * @return string */ function dec_to_b62($decnum) { $chars = array_merge(range(0,9),range('a','z'),range('A','Z')); $result = '' ; for (; $decnum > 0; $decnum = floor($decnum/62)) { $result = $chars[$decnum%62].$result ; } return $result == '' ? '0' : $result ; }
その他
WordPressでちょっとしたものを作るのが楽しい。
おしまい( ・`ー・´三 ・`ー・´)
追記(6/17)
10万件を処理するのにかかる時間についてキャプチャを上げ忘れていたので更新。
最初の頃から変わらず18秒くらいをうろうろしています。
ただいま5桁の辞書データが佳境といった感じで全体で7億レコードくらいありました。
6桁目はディスクの容量が勿体ないのでこれで終わり( ・`ー・´)