やり切れない夜のお供にグリースモンキー

てことでログピOpenIDでログインし辛いのでとてもやり切れないからグリモンでさくっとログイン出来るようにしてみた。

// ==UserScript==
// @name           logpi_openid
// @namespace      tikilabo
// @include        http://logpi.jp/
// ==/UserScript==
(function(){
	var frm = document.getElementsByTagName("form")[0];
	var openid = document.createElement("input");
	openid.type = "hidden";
	openid.name = "openid";
	//自分のOpenIDを設定
	openid.value = "";
	frm.appendChild(openid);
	frm.action = "/signin/openid";
	frm.submit();
})();

PHPでPOSTの生データを扱う

下書き保存したと思ってたらそんなものは無かった・・・
何を書いてたのやら思い出しつつ書き直してみる。
なんとなく覚えてるんですよ。うっすらと。
多分あれだ、PHPチェックボックスとかラジオボタンとか扱う時の話。

下書き保存してたはずなのに記事が無かったのは何故?
<form method="post">
	<input type="checkbox" name="chk" value="1" />下書きしたと思い込んでるだけ
	<input type="checkbox" name="chk" value="2" />雪のせい
	<input type="checkbox" name="chk" value="3" />電池が切れた
	<input type="checkbox" name="chk" value="4" />ネジが外れた
	<input type="submit" value="下書きは忘れずに!" />
</form>

こんな風にnameを同じ値に揃えるとグループ化できるけど、
複数にチェック入れた状態でPHPに値を渡すと最後にチェックした値だけが返ってくるんですよ。


なのでそれを回避するためにnameをちょっぴり細工するのが流行ってます。

下書き保存してたはずなのに記事が無かったのは何故?
<form method="post">
	<input type="checkbox" name="chk[]" value="1" />下書きしたと思い込んでるだけ
	<input type="checkbox" name="chk[]" value="2" />雪のせい
	<input type="checkbox" name="chk[]" value="3" />電池が切れた
	<input type="checkbox" name="chk[]" value="4" />ネジが外れた
	<input type="submit" value="下書きは忘れずに!" />
</form>

nameをchkからchkにしてあげるとPHP側で配列と解釈されて複数チェックしたものがちゃんと配列に格納されます。
が、しかしこんなへんてこりんな書き方すると今度はJavaScriptがとばっちりを受けるからやんなっちゃう。


name="chk"だとdocument.form[0].chk[0].valueで1が取得できるが、name="chk"の時はエラーになる。
というのもchkまで含めてnameとみなされるから、document.form[0]["chk[]"][0].valueな風に書かなきゃ値が取れない。
なんだそりゃって感じですよねー
それもこれも$_POSTのあんちくしょうのせいだってんでPOSTの生データを取ってやろうということです。


なんとまぁ長い前振りだこと。前書いた時よりも断然長くなってる気がする。
てことで結論はさらりと流す方向であっさり書くと、PHPでPOSTの生データを取得するにはphp://inputってのを使えば簡単です。
ソースはこんな感じ。

<?php
	$posts = file_get_contents('php://input');
?>

これでPOSTしたデータがGETした時のQUERY_STRINGみたいに取得できます。
キー=値のセットが&で連結されてるので(例:chk=1&chk=3&chk=4)てきとーに分割してやって、キーが同じなら配列にしちゃえば完成!
全体のソースはこんな感じ。

<?php
	$posts = explode('&', urldecode(file_get_contents('php://input')));
	foreach($posts as $buf){
		list($key, $val) = explode('=', $buf);
		if(isset($post[$key])){
			if(is_array($post[$key])){
				array_push($post[$key], $val);
			}else{
				$post[$key] = array($post[$key], $val);
			}
		}else{
			$post[$key] = $val;
		}
	}
?>

しかしこの方式には一つ大きな弱点が・・・
formのenctypeがmultipart/form-dataの時は値が取ってこれない!!!!
すなわちファイルのアップロード用フォームなんかでは全く使いもんにならんっちゅう話です。
PHP使うのもうやめようかな、、、(下書き保存はもう二度と使わない)


参考:
PHP: PHP 入出力ストリーム - Manual

Google Analyticsのカスタム変数でABテストしてみる

UIの変更なんかをする時に使われるABテストがカスタム変数を使うと簡単に出来たのでメモ。

タイプの振り分け

まずはPHPでユーザーをAタイプ、Bタイプに振り分ける

<?php
if(isset($_COOKIE['type']) && $_COOKIE['type']){
	$type = $_COOKIE['type'];
}else{
	$type = (rand(0, 1) == 0) ? 'a' : 'b';
	setcookie('type', $type, strtotime('+7day'), '/');
}
?>

どっちのタイプに振り分けたかをCookieに保存することで途中で違うタイプに変わらないようにする。
あとは$typeの内容を見て表示を切り替えれば、ユーザーを振り分けることが出来る。

カスタム変数の定義

振り分けたタイプをGoogle Analytics側に通知するためにカスタム変数を使う。

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-xxxx");//自分のトラッキングコードを設定
pageTracker._setCustomVar(1, "user_type", "<?php echo $type ?>", 1);
pageTracker._trackPageview();
} catch(err) {}</script>

カスタム変数を定義してるのが_setCustomVarという部分。
一つ目の引数はカスタム変数のインデックスとして1〜5まで設定出来るのでとりあえず1を設定。
既に使われているならかぶらないようにした方が良いかも。

二つ目の引数は変数名なので今回はざっくりとuser_typeと設定してみた。

三つ目の引数は変数の値なので、ここにuser_typeがAタイプなのかBタイプなのかを設定する。

四つ目の引数はスコープ。
ちょっとわかりにくいけど、ABテストみたいにAタイプのユーザーが途中でBタイプになったりすると困るような場合はCookieに設定してくれるビジターレベルの1を設定した方が良いかも。
セッション単位で値が変わって良いものならセッションレベルの2を設定するみたい。


※参考:
Analytics 日本版 公式ブログ: カスタム変数 概要

結果をGoogle Analytics側で確認

メニューからアドバンスセグメントを選んで、アドバンスセグメントを新規作成する。
ディメンション>カスタム変数(キー1)を追加して値にuser_typeと入力。
「And」ステートメントを追加で、ディメンション>カスタム変数(値1)を追加して値にaと入力し、セグメント名をAタイプにして保存すると、Aタイプのユーザーだけ絞り込める。
同じようにカスタム変数(値1)にbと入力したセグメントを作ればBタイプのユーザーを絞り込める。


※_setCustomVarで設定したインデックスに対応しているため、インデックスを2にしてる場合はキー2、値2の組み合わせで作成する

jQuery1.4でgetJSONのコールバックが呼ばれない

なんかよーわからんけどgetJSONで値を返すとコールバックが呼ばれない。
仕方ないので1.3に戻した。


※追記
レスポンスを()で括ってたせいで動かなかったみたい。
昔()で括らないと動かなかったような気がしたんだけど。。。


※追記
()で括ってたのはJavaScript側でevalしてた時の名残だったんでさっくり削除して問題なし。

クラス変数をunsetするとエラーになる

class Hoge{
	public static $prop;
}
unset(Hoge::$prop);

ブラウザで見てもぱっと見わかんないけど、ばっちりステータスコード500が返ってくる。
のでauの端末なんかだとエラーをキャッチするので要注意。

memcacheでセッションを管理する

今時はphp.iniに設定するだけで出来ちゃうみたい。

session.save_handler = memcache
session.save_path = tcp://localhost:11211

session.save_pathにはmemcacheを接続する際に渡しているホストとポート番号を設定する。


参考サイト:
http://dozo.matrix.jp/pear/index.php/PECL/memcache/session_support.html

OpenIDを試してみる2

せっかくroot権限のあるSlicehostを借りてるので、前回できなかった証明書の更新をしてmixiのIDで認証できるようにしてみる。

1、まずはhttps://mixi.jp/へアクセスして証明書をエクスポートする。
保存するファイルの種類は「証明書パスを含むX.509証明書(PEM)」を選択。


2、既存の証明書ファイル(ca-bundle.crt)へエクスポートした内容を追記する。
CentOS5.3では「/etc/pki/tls/certs/ca-bundle.crt」にあった。

これで動くはず!がエラー。
エラーメッセージはこんな感じ。

[60] error setting certificate verify locations: CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none


うん、さっぱりわからん。
エラーメッセージを検索してみても英語ばっかりで萎える。
コマンドラインから直接cURLを叩いてmixiへアクセスすると何のエラーも出ない。
仕方ないのでGoogleさんで英語のサイトを翻訳してみたら、ca-bundle.crtが入ってるフォルダのパーミッションがダメだったみたい。
/etc/pki/tls/certs/のパーミッションを755に変更したら認証完了!


参考サイト:
http://d.hatena.ne.jp/botchy/20080820/1219255653
http://curl.haxx.se/mail/curlphp-2005-11/0038.html