Selenium IDE: Gosub Control 使い方

定型操作のlogin

gosubを使わない例

# login user
open ${BASE_URL}/login
type name=username ${USER_USERNAME}
type name=password ${USER_PASSWORD}
clickAndWait id=btnLogin
# action and verify
...
# lougout
clickAndWait link="logout"
#
# login admin
open ${BASE_URL}/login
type name=username ${ADMIN_USERNAME}
type name=password ${ADMIN_PASSWORD}
clickAndWait id=btnLogin
# action and verify
...
# logout
clickAndWait link="logout"

gosubを使って、login user、login admin、logoutをサブルーチン化した例

gosub login_user
# action and verify
...
#
gosub logout
#
gosub login_admin
# action and verify
...
gosub logout
#
#----------------------------------------
# sub login_user
#----------------------------------------
sub login_user
open ${BASE_URL}/login
type name=username ${USER_USERNAME}
type name=password ${USER_PASSWORD}
clickAndWait id=btnLogin
endsub
#----------------------------------------
# sub login_admin
#----------------------------------------
sub login_admin
open ${BASE_URL}/login
type name=username ${ADMIN_USERNAME}
type name=password ${ADMIN_PASSWORD}
clickAndWait id=btnLogin
endsub
#----------------------------------------
# sub logout
#----------------------------------------
sub logout
clickAndWait link="logout"
endsub
#

Selenium IDE ユーザー拡張スクリプトでstore変数を参照する

Selenium IDE で、store変数USERNAMEに “taro” を保存する。
コマンド: store
対象: taro
値: USERNAME

store変数USERNAMEをinputタグ(id=”username”)に入力する。
コマンド: type
対象: id=username
値: ${USERNAME}

ユーザ拡張スクリプト(jsファイル)で独自コマンドを定義する。
http://oss.infoscience.co.jp/seleniumhq/docs/08_user_extensions.html

ユーザ拡張スクリプト(jsファイル)の設定箇所
Selenium IDE>オプション>設定>Selenium Core 拡張スクリプト(user-extensions.js)

ユーザ拡張スクリプトの例

Selenium.prototype.doEchoUsername = function() {
	// 独自コマンド echoUsername
	// store変数USERNAME をログに表示する

	// Selenium IDEのログに表示する
	this.doEcho( storedVars.USERNAME );

};

ディールシャッフルをランダム順に配ってみると

去年からジン・ラミーというカードゲームで遊ぶようになった。そしてシャッフルに興味を持ち、ヒンズーシャッフルより良いシャッフル手法について検討した。

1. シャッフルの手法

カット

 1組の山を2つに分けて、上下を入れ替える。シャッフル後に1回カットする、という使い方をする。1組の山をリング状と考えると、カードの並びは変化しないため、カットだけを繰り返してもシャッフルとしては不十分である。

ヒンズーシャッフル

 日本で一般的なシャッフル手法。ときどき手さばきを失敗してカードを撒き散らしてしまう。充分にシャッフルするために延々とヒンズーシャッフルすることがあるが、本当にシャッフルできているか、シミュレーションで確認したい。

ウォッシュシャッフル

 カードをテーブルに広げ、手でかき混ぜる。最後にカードを揃えることが手間であり、時間もかかる。

ファローシャッフル

 1組の山を2つに分けて端同士を押し付けて、互い違いにかみ合わせる。慣れると1枚づつ互い違いにかみ合わせることは難しくなく、この場合のランダム性は山の分け方だけである。

ディールシャッフル

 1組の山から1枚づつ順にいくつかの山に配る。配り終えたら、1組の山にまとめる。隣り合ったカードが離れることが長所である。時間がかかる。並び方にランダム性はない。

2. 理想的なシャッフル

 テーブルに52枚のカードを並べる場所を用意する。PC152をランダムに表示しながら、1組の山から順に表示された場所へ置く。52枚を置き終えたら、1組の山にまとめる。ところが52枚を並べるために広いテーブルが必要である。

3. ランダム順に置くディールシャッフル

 ディールシャッフルは時間はかかるが、カードの操作は易しく練習する必要はない。そこでディールシャッフルで山に配るとき、ランダムな順番で山に配ったらどうだろうか。以下「ランダムディール」と呼ぶことにする。

4. シミュレーションプログラムについて

 カード番号0,1,2,3のカード4枚の並び替えで説明する。カードの並びを配列で表現する。配列の添え字がカード位置、配列の内容がカード番号である。

シャッフル前:カード配列 = [ 0, 1, 2 ,3 ]

シャッフル後:カード配列 = [ 1, 3 ,0, 2 ]

 集計用の配列を2次元で、横軸をカード番号、縦軸をカード位置で表現し、内容を出現回数とする。試行1回ごとに集計し、集計配列1[カード番号][カード位置] +1 する。

カード位置

カード番号

0

1

1

3

2

0

3

2

カード番号

0

1

2

3

カード位置

0

0

1

0

0

1

0

0

0

1

2

1

0

0

0

3

0

0

1

0

 次に、シャッフル前の隣合ったカードの位置の変化を調べた。試行1回ごとに、集計配列2[カード番号][距離] +1 する。

A

B

C

D

E

カード番号

カード位置

A–1

カード番号

Cのシャッフル後の

カード位置

距離

B-D

0

2

3

1

1

1

0

0

2

-2

2

3

1

0

2

3

1

2

3

-1

Aカード番号

0

1

2

3

E距離

0

0

0

0

0

1, -3

1

0

0

0

2, -2

0

1

1

0

3, -1

0

0

0

1

 同様の要領で、シャッフル前の一つ飛びのカード、2つ飛びのカードとのシャッフル後の距離を集計配列3、集計配列4に保存した。

5. 集計結果の可視化

 文献1でシャッフルのシミュレーション結果をグレイスケール画像で表現している。これを集計配列14を画像14に画像化した。

6. シミュレーション結果

 310は各シャッフル手法を10000回シミュレーションしたものである。

 左から画像14とする。画像14とも、均一になっていることが望ましい。

 ヒンズーシャッフルは、切る枚数を420の一様分布とし、山がなくなるまで切ったら、1回と数える。ヒンズーシャッフル(3)は、これを3回繰り返し、試行1回とした。

shuffle_sim

1

シャッフル前のソートされた初期状態
画像
1は、左上から右下へ直線である。
画像
2は、全カードで距離=1
画像
3は、全カードで距離=2
画像
4は、全カードで距離=3となっている。

2

位置10でカットした状態
画像
24とも初期状態と同じであり、並び順に変化がないことがわかる。

3

ランダム:疑似乱数で並び替えた
すべての画像が均一となっている。

4

ランダムディール(5)
画像
1の右上角と左上角が黒く、先頭の数枚は末尾へ、末尾の数枚は先頭へ移動しやすいことがわかる。山の数と同じ縞模様がある。
画像
2は上部に白い部分があり、距離=51に集中がある。隣同士のカードは、同じ山に配れば隣のまま、違う山に配れば1山分の枚数(5山では約10)離れる。つまり隣同士だったカードは、29枚離れる可能性が非常に小さいためである。

5

ランダムディール(10)
5
山と比較すると、より均一になった。
画像
1の距離=51の集中は残っている。

6

ヒンズーシャッフル(3)
画像
1には左上から右下への縞模様がある。
画像
2を見ると、初期状態の画像2とほぼ同じであり、距離=1に集中している。隣り合ったカードの位置は変化しにくいことがわかる。

7

ヒンズーシャッフル(10)

8

ヒンズーシャッフル(20)
画像
1はほぼ均一である。標準偏差は19とランダムディール(10)より小さく、ランダムの13に近い。
画像
2は、距離=1の集中が残っている。

9

カット+ランダムディール(10)+カット
画像
1はほぼ均一であり、標準偏差もランダムとほぼ同じである。
画像
2の上部の白い部分、距離=51の集中は残っている。

10

カット+ヒンズーシャッフル(20)+カット
画像
1はほぼ均一になった。
画像
2の距離=1の集中は残っている。

 次に、隣合ったカードがシャッフル後も隣り合ったままの確率を調べた。画像2で距離=1や距離=51の集中が濃いほど確率は高い。

確率=Σ(集計配列2[カード番号][1]+集計配列2[カード番号][51]) / 52 / 試行回数

シャッフル手法

隣合ったカードが
隣り合ったままの確率

ランダム

0.0392

ランダムディール(5)

0.1998

ランダムディール(10)

0.1018

ヒンズーシャッフル(3)

0.7768

ヒンズーシャッフル(10)

0.4600

ヒンズーシャッフル(20)

0.2276

7. まとめ

 ヒンズーシャッフルは、隣合ったカードが隣のままの確率が高いことがわかった。ヒンズーシャッフル(3)78%、ヒンズーシャッフル(10)46%、ヒンズーシャッフル(20)でも23%もある。延々とシャッフルしても隣合ったカードをよく混ぜるのは難しいことがわかった。

 ランダムディールの隣だったカードの移動先には特徴があり、隣同士のままか、山1つ以上離れるかである。ディールシャッフルの配り方を考えれば当然であるが、画像2を見るまで思いつかなかった。逆順にはなるが隣のままの確率は、ランダムディール(5)20%、ランダムディール(10)10%である。

 標準偏差はヒンズーシャッフル(20)が良く、隣合ったカードではランダムディール(10)のほうが良かった。

 実際に手でシャッフルするとどちらも約1分とほぼ同じである。私の場合は、ヒンズーシャッフル(20)のほうが失敗してカードを撒き散らしやすいので、ランダムディールを主に使っている。つまりゲーム前にノートPCを見ながらカードを配ってシャッフルし(末尾のリンク参照)、あらためてゲームのためにカードを配っている。

参考文献

  1. 野瀬彰大、深川大路:TCGにおけるシャッフル手法に関する計算機実験を用いた考察, 情報処理学会研究報告, Vol.2011-GI-25 No4 (2011).

リンク
FlickPass>シャッフルナビ
ソースコード( cakePHP plugin) https://github.com/ninton/ShuffleSim

リーダブルコードcakePHP版?find()の引数並びにarray()を記述しない

cakePHPの公式サイトには次のようなサンプルコードがある。引数並びにarray()を記述している。

$allPublishedAuthors = $this->Article->find('list', array(
	'fields' => array('User.id', 'User.name'),
	'conditions' => array('Article.status !=' => 'pending'),
	'recursive' => 0
));

この書き方のメリットは、下記の書き方と比較するとわかりやすい。array()を$paramsなどの変数に代入していないので前後の変数との関連を考えなくてもいいこと(下記 A,C)、この1まとまりの処理の途中に無関係なコードを記述できないこと(下記B)だと思う。

//A. これ以前に$paramsに代入しているだろうか?
$params = array(
	'fields' => array('User.id', 'User.name'),
	'conditions' => array('Article.status !=' => 'pending'),
	'recursive' => 0
);
//B. ここに無関係なコードを記述できてしまう。
$allPublishedAuthors = $this->Article->User->find('all', $params);
//C. これ以降に$paramsを参照しているところがあるだろうか?

しかし実務では次のデメリットのほうが大きいと思う。

1つ目は、引数並びが長すぎて読む気力が萎えてしまうこと。find()の閉じ括弧までが長すぎて、頭の中のスタックのプッシュ量が多い感じがする。

2つ目はデバッグしにくいこと。printデバッグしたくても、引数並びのarray()のままではvar_dump()できない。eclipse+xDebugでトレースする場合も、引数並びのarray()を表示することはできない。ソースファイルがあれば関数へステップインすると表示できるが、組み込み関数の場合はステップインもできない。

次のようにしたほうが読みやすくデバッグもしやすいと思う。

$params = array(
	'fields' => array('User.id', 'User.name'),
	'conditions' => array('Article.status !=' => 'pending'),
	'recursive' => 0
);
$allPublishedAuthors = $this->Article->find('list', $params);

さらに、array要素を1行1要素にすると、gitで直前のリビジョンとdiffしたとき変更箇所がわかりやすい。

$params = array(
	'fields' => array(
		'User.id',
		'User.name',
 	),
 	'conditions' => array(
 		'Article.status !=' => 'pending',
 	),
 	'recursive' => 0,
);
$allPublishedAuthors = $this->Article->find('list', $params);

アクション内で$this->XXX->find()を何回も呼んでいて行数が多いと感じたら、それぞれのfind()呼び出しをモデルのメソッドにするといいと思う。モデルのメソッドにすれば1行で呼び出すことができ、前後の変数との関連はないし、一連の処理の途中に無関係なコードの記述もできない。

$allPublishedAuthors = $this->Article->find_list_ArticleStatusIsPending();

なお、viewの$this->input()の2つ目の引数は、ロジックとは関係がないhtmlタグやcssなどが多く、引数並びにarray()を記述したほうが読みやすいと思う。

関連:リーダブルコード 8章 巨大な式を分割する

整ったコードの7か条

「ビューティフルコード」 32章 働くコードの「整ったコードの7か条」を紹介する。
#は、投稿者(青木)の意見。

32章の著者 Laura Wingerd、Christopher Seiwald

原文
http://www.perforce.com/resources/white-papers/seven-pillars-pretty-code

サンプルコード
ftp://ftp.perforce.com/jam/src/make.c

■既存のスタイルに溶け込むこと
コードを変更するとき、既存のスタイルで書くこと。

「本のよう」であること
1行の長さを短くする。短い行ほど理解しやすい。 経験的に80文字がよい。

コードをブロックに分けること
コードにコメントをつけること

# サンプルコードはstep 1、step 2と大ブロックに分け、step 1の中をstep 1a、step 1b と小ブロックに分けている。

ごちゃごちゃにしないこと

繰り返し参照される変数名を短くする。
元のコードをコメントで残すのではなく削除する。元のコードはSCMを見ればよい。
何を変更したかをコメントしない。変更履歴はSCMを見ればよい。

# gitやバグ管理ツールを使うと、不要コードや不要コメントを思い切って削除できるようになる。

似たものは似ているように見えるようにすること

インデントを克服すること
インデントが深いと読みにくくなる。
しかし、最大の落とし穴は「入れ子」であり、「入れ子」になったコードは理解しにくい。

# 「入れ子」が深いと、頭のスタックがオーバーフローしてしまう。

Another HTML-lint htmllint.cgiをCentOS/5.5に設置する

Another HTML-lint
http://openlab.ring.gr.jp/k16/htmllint/htmllinte.html

Another HTML-lintはHTML文法チェッカーの老舗であり、Perlソースが公開されている。VMWare上のCentOS/5.5(32bit版)への設置手順を説明する。

[動作環境]

・Windows7上のVMWareにCentOSをインストールした。
・CentOSのIPアドレスを 192.168.0.4 とする。
・WindowsのWebブラウザで、http://192.168.0.4/ にアクセスすると「Apache 2 Test page powered by CentOS」と表示されること。

(1) Windows上の作業

●htmllint.zipをダウンロードする。(記事投稿時2011-11-28 16:17版)

●zipファイルを解凍すると、htmllint/ の下にディレクトリ階層なしで全ファイルが展開される。

●htmllintenv を htmllint.env へコピーまたはリネームする。

●htmllint.cgi、taglist.cgi の先頭行(perlパス)を編集する。

編集前

#!/usr/local/bin/perl

編集後

#!/usr/bin/perl

(2) WindowsのWinSCPで、CentOSへアップロード

●/var/www/cgi-bin/htmllint/htmllint.cgi の位置へ全ファイルをアップロードする。

●htmllin.cgi、taglist.cgi のアクセス権限を755 に設定する。

○WindowのWebブラウザで、http://192.168.0.4/cgi-bin/htmllint/htmllint.cgi を開き、「Another HTML-lint error!」と表示されるか?

サーバエラー → (1) と (2) へ

○WindowsのWebブラウザで、http://192.168.0.4/cgi-bin/htmllint/htmllint.html を表示できるか?

できない →(3)へ

○htmllint.htmlのURL欄に「http://www.yahoo.co.jp」を入力し[check]する。結果が表示されたか?

「調整中です」 →(4)へ

(3) CentOSでsshログイン作業

CentOSインストール直後は、/cgi-bin/ 下は *.cgi、*.pl以外は表示できない。htmlやcssを表示できるように/etc/httpd/conf/httpd.confを編集する。

編集前

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Options None
    Order allow,deny
    Allow from all
</Directory>

編集後

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Options None
    Order allow,deny
    Allow from all
    AddHandler text/css css
    AddHandler text/html html
    AddHandler image/gif gif
</Directory>

●webサーバを再起動する。

[root@192.168.0.4]# service httpd restart

○WindowのWebブラウザで、http://192.168.0.4/cgi-bin/htmllint/htmllint.html を表示できるはず。

(4) CentOSでsshログイン作業

gccとPerlモジュールをインストールする。いろいろ聞かれたら yes または y を入力して進める。

[root@192.168.0.4]# yum -y install gcc
[root@192.168.0.4]# cpan
cpan> install Jcode
cpan> install CGI
cpan> install LWP
cpan> exit

○WindowsのWebブラウザで、http://192.168.0.4/cgi-bin/htmllint/htmllint.html を表示し、URL欄に「http://www.yahoo.co.jp」を入力し[check]する。結果を表示できるはず。

デプロイ先を切り替えてgit aws.pushする

【環境】

PC: Windows7、git、AWS Elastic Beanstalk Command Line Tool
サーバー: AWS ElasticBeanstalk

AWS ElasticBeanstalk(以下EBT)はロードバランサーとオートスケーリングサーバのパッケージである。コマンドプロンプトのgit aws.configでデプロイ先を設定し、git aws.pushでデプロイする。

デプロイ先が一つだけなら最初に1回だけgit aws.configをすればいいのだが、デプロイ先として本番環境、ステージング環境、開発環境など複数ある場合、毎回aws.configするのは面倒である。ここではデプロイ先を切り替えてgit aws.pushする方法を説明する。

環境1
AWSアカウント1>EBT>Application=hoge1-app>Enevironment=hoge1-env
環境2
AWSアカウント2>EBT>Application=hoge2-app>Enevironment=hoge2-env

【方法1】

環境1用のgitフォルダD:\hoge1、環境2用のgitフォルダD:\hoge2と別々に用意する。リモートリポジトリを経由してD:\hoge1とD:\hoge2を同期する。

D:\hoge1で開発作業、commit
D:\hoge1で環境1へgit aws.push

D:\hoge1でリモートリポジトリへpush

D:\hoge2でリモートリポジトリからpull
D:\hoge2で環境2へgit aws.push

※リモートリポジトリ
インターネットを介して複数人で作業する場合はgithubが便利である。無料プランは非公開リポジトリを作成できないが、有料プランにすると非公開リポジトリを作ることができる。
一人gitでLAN内作業の場合は、共有ドライブ(ローカルドライブでもよい)が手軽である。エクスプローラ右クリック>Git Create repository here>[Make it Bare]のチェックを付けると、リモートリポジトリが作成される。Git syncのRemote URLに共有フォルダのパス(Z:\gitrepos\hogeなど)を指定すればよい。

【方法2】

1つのgitフォルダ(以下、hogeとする)でデプロイ先を切り替える。

git aws.configは、hoge\.elasticbenstalk\configに設定内容を保存し、git aws.pushはこのconfigにしたがってデプロイする。

hoge\.elasticbenstalk\config

[global]
ApplicationName=hoge1-app
CredentialFile=C:\Users\taro\aws_credential_file
DevToolsEndpoint=git.elasticbeanstalk.ap-northeast-1.amazonaws.com
EnvironmentName=hoge1-env
Region=ap-northeast-1

そこで環境ごとのconfigファイルを用意しておき、バッチファイルでconfigにコピーし、git aws.push する。

まずconfigをコピペして、環境1のconfig-hoge1-env、環境2のconfig-hoge2-envを作成する。AWSアカウントが複数ある場合は、それぞれのaws_credential_fileも用意しておく。

hoge\.elasticbenstalk\config-hoge1-env

[global]
ApplicationName=hoge1-app
CredentialFile=C:\Users\taro\aws\account1\aws_credential_file
DevToolsEndpoint=git.elasticbeanstalk.ap-northeast-1.amazonaws.com
EnvironmentName=hoge1-env
Region=ap-northeast-1

hoge\.elasticbenstalk\config-hoge2-env

[global]
ApplicationName=hoge2-app
CredentialFile=C:\Users\taro\aws\account2\aws_credential_file
DevToolsEndpoint=git.elasticbeanstalk.ap-northeast-1.amazonaws.com
EnvironmentName=hoge2-env
Region=ap-northeast-1

次にバッチファイルを作成する。

hoge\git_awspush_1_hoge1-env.bat

copy .elasticbeanstalk\config-hoge1-env .elasticbeanstalk\config
git aws.push

hoge\git_awspush_2_hoge2-env.bat

copy .elasticbeanstalk\config-hoge2-env .elasticbeanstalk\config
git aws.push

環境1へデプロイしたいときは、hoge\git_awspush_1_hoge1-env.batを実行(またはダブルクリック)し、環境2へデプロイしたいときは、hoge\git_awspush_2_hoge2-env.batを実行(またはダブルクリック)する。

PHP5.3でMySQL接続エラー(old_passwords=1)

【環境】

接続元: PHP 5.3.20 (WebサーバA、新規)
接続先: MySQL 5.0.95 (MySQLサーバB、運用中)

【現象】

次のエラーが表示されてMySQLサーバに接続できない。
Warning (2): mysql_connect() [function.mysql-connect]: mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords flag from your my.cnf file [CORE\cake\libs\model\datasources\dbo\dbo_mysql.php, line 374]

xampp_1.5.4a(PHP5.1.4) → MySQLサーバB 接続OK
xampp_1.7.7(PHP5.3.8) → MySQLサーバB 接続ERR
コマンドプロンプトのmysqlコマンド → MySQLサーバB 接続OK
WebサーバA → 別のMySQLサーバ(MySQL5.5) 接続OK
つまりPHP5.3とMySQLサーバBの組み合わせで接続できない。新規のWebサーバAをPHP5.2やPHP5.1にすることはできない。

【原因】

PHP5.2から5.3になったとき、mysqlドライバ部分がmysqlndライブラリに変更された。mysqlndライブラリは41バイトのパスワードフォーマットを使用し、古い16バイトのパスワードフォーマットを使うとエラーを生成する。
http://php.net/manual/ja/migration53.incompatible.php

接続先のMySQLサーバのmy.cnfには old_passwords=1 が設定してあった。SELECT PASSWORD();を実行してみると16バイトのハッシュが表示された。またmysql.UserテーブルのPasswordカラムはchar(41)だが、保存されているデータは16バイトであった。

【検討】

新規のMySQLサーバの場合は、my.cnfのold_passwords=0にしてMySQLサーバを再起動し、DBユーザを新規作成したりパスワードを再設定すればよい。

ところがMySQLサーバBは運用中であり、DBユーザも登録されている。なるべく再起動せずにすむ方法、既存のDBユーザに影響しない方法を実施したい。本番サーバで操作する前に実験サーバで次の2つの確認をした。以下、WebサーバAから接続するDBユーザを ‘taro’@’%’、パスワード ‘1234’ とする。

【実験1】

1-1. old_passwords=0にしてMySQLを再起動し、既存のDBユーザ(16バイトハッシュ)で接続できるか?

mysqlコマンド 接続OK
PHP 5.2 接続OK
PHP 5.3 接続ERR

1-2. DBユーザ taro’@’%’のパスワードを設定する。

SET PASSWORD FOR taro@'%' = PASSWORD('1234');

1-3. DBユーザ ‘taro’@’%’ (41バイトハッシュ)で接続できるか?

mysqlコマンド 接続OK
PHP 5.2 接続OK
PHP 5.3 接続OK

【実験2】

2-1. 別のMySQLサーバ(old_passwords=0)を用意し、41バイトハッシュを生成した。

SELECT PASSWORD('1234');
*A4B6157319038724E3560894F7F932C8886EBFCF

2-2. 対象のMySQLサーバはold_passwords=1のままで、DBユーザのパスワードを41バイトハッシュにするために次のSQLを実行した。

SET PASSWORD FOR taro@'%' = '*A4B6157319038724E3560894F7F932C8886EBFCF';
を実行する。

2-3. mysql.userテーブルを表示して41バイトハッシュになっていることを確認した。

SELECT * FROM `user` WHERE `Host`='%' AND `User`='taro';

2-4. DBユーザ ‘taro’@’%’ (41バイトハッシュ)で接続できるか?

mysqlコマンド 接続OK
PHP 5.2 接続OK
PHP 5.3 接続OK

【まとめ】

実験1では、old_passwords=0 にしても既存のDBユーザの接続に影響がないことがわかった。
実験2では、old_passwords=0にしなくても、41バイトハッシュを設定することができた。

本番のMySQLサーバでは実験2の方法を実施した。

検索ワードをUTF-8からEUC-JP変換してnamazu検索する

namazu.cgiはUTF-8の検索ワードに対応していないので、UTF-8ページの検索フォームからnamazu検索すると、検索ワードが文字化けしてしまい、正しく検索できない。http://www.namazu.org/FAQ.html#set-server-encoding に、namazu.cgiはUTF-8の入力に対応していないと記述がある。

次の案1と案2を作成した。案2はphp.iniでallow_url_fopen = Onの必要があり、必要条件の少ない案1を採用した。

案1: 検索フォームからnamazu.phpへ送信し、namazu.phpで検索ワードをUTF-8からEUC-JPに変換し、namazu.cgiへリダイレクトする。
案2: 検索フォームからnamazu.phpへ送信し、namazu.phpで検索ワードをUTF-8からEUC-JPに変換し、namazu.cgiへhttpリクエストし、内容を返す。

他に次のような記事もあった。試していないが、参考のため残しておく。

php用のnamazu.so拡張モジュールを使う。
・2013-02-14 RHEL6でnamazuをインストールしてPHPから使う
http://www.mogumagu.com/wp/wordpress/?p=1217
・2010-02-06 namazu.soの作り方
http://zkangaroo.blogspot.jp/2009/03/namazuso.html

formタグのaccept-charset指定、jsでdocument.charset指定。
・2009-01-12 UTF-8のページでnamazuを使う
http://blog.digital-assist.net/?p=21

以下、案1について説明する。

(1) namazu.phpに保存してください。

$path(赤太字)を本来のnamazu.cgi の設置パスに修正してください。

<?php
/*
namazu.php

■1.処理の流れ
(1) utf8ページから「ほげ」を検索
↓フォーム送信
(2) namazu.php?query=%E3%81%BB%E3%81%92&whence=0&max=20&result=normal&sort=score
↓リダイレクト
(3) namazu.cgi?query=%A4%DB%A4%B2&whence=0&max=20&result=normal&sort=score

■2.動作条件
(1) 5 <= PHP_VERSION(http_build_query関数があること)
(2) iconv拡張モジュールが有効であること。(通常はデフォルトで有効になっている)
*/
ini_set( 'display_errors', 1 );
ini_set( 'error_reporting', E_ALL );
ini_set( 'assert.bail', 1 );
assert( '5' <= PHP_VERSION );
assert( function_exists('iconv') );

if ( isset($_GET['query']) ) {
	$_GET['query'] = iconv( 'UTF-8', 'EUC-JP', $_GET['query'] );
}

$scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
$host   = $_SERVER['HTTP_HOST'];
$path   = '/cgi-bin/namazu/namazu.cgi';
$query  = http_build_query($_GET);
$url    = sprintf( '%s://%s%s?%s', $scheme, $host, $path, $query );

header( "Location: $url" );
?>

(2) namazu.phpをサーバに設置してください。

動くならnamazu.cgiと同じディレクトリがわかりやすい。cgi-bin下でphpが動かない場合は、/namazu.php などに設置してください。

(3) utf-8ページの検索フォームのaction(赤太字)をnamazu.phpの設置パスに編集してください。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>namazu</title>
</head>
<body>
<h1>検索ボックス/UTF-8</h1>
<form method="get" action="/cgi-bin/namazu/namazu.php">
  <input type="text" name="query" />
  <input type="hidden" name="whence" value="0" />
  <input type="hidden" name="max" value="20" />
  <input type="hidden" name="result" value="normal" />
  <input type="hidden" name="sort" value="score" />
  <input type="submit" value="search" />
</form>
</body>
</html>

AWS RDS MySQLのtime zoneをcakePHPで設定する

【環境】

AWS RDS MySQL 5.5

【現象と要望】

AWSのRDSでMySQLサーバを起動し、日本時間2013-04-02 15:01 :00に “SELECT NOW();” を実行すると、”2013-04-02 06:01:00″と表示される。これを日本向けに”2013-04-02 15:01:00″と表示させたい。

【対応】

phpMyAdmin>変数を表示すると、time zoneはUTCであり、上記の「06:01:00」は正しい表示結果である。

time zoneを’Asia/Tokyo’または’+09:00’に設定すれば解決しそうだが、AWS Management Console>RDS>DB Parameter Groupsを見ると、default_time_zoneは変更不可と表示されている。

対応方法の一つとして、MySQLサーバ接続ごとに、またはSQL実行ごとに「SET @@time_zone=’+09:00′ 」を実行する方法がある。

cakePHP 1.2での対応例を紹介する。

※ソースそのままではなく記事用に編集してあるので、もしエラーが発生したら自分で対応してほしい。

【対応例】

(1) app/config/database.php

DATABASE_CONFIGのdriverの値をmysql_customに変更し、time_zoneを追加する。

class DATABASE_CONFIG {
    var $default = array(
        'driver'	=> 'mysql_custom',
        'persistent'	=> false	,
        'host'		=> 'localhost'	,
        'login'		=> 'user'	,
        'password'	=> 'pass'	,
        'database'	=> 'cake'	,
        'prefix'	=> ''		,
        'encoding'	=> 'utf8'	,
        'time_zone'	=> '+09:00'	,
    );

(2) app/models/datasources/dbo_mysql_custom.php

ファイルを新規作成する。

<?php
uses( 'model' . DS . 'datasources' . DS . 'dbo' . DS . 'dbo_mysql' ); 

class DboMysqlCustom extends DboMysql{
    function connect() {
        $r = parent::connect();
        if ( $r ) {
            if ( isset($this->config['time_zone']) ) {
                $qry = sprintf( "SET @@time_zone = '%s'", $this->config['time_zone'] );
                $this->query( $qry );
            }
        }
        return $r;
    }
}

?>