PHPのsplit関数

Posted on September, 8 at 10:07 pm

Ruby、Perl、Pythonのsplit関数の挙動をまとめたページをつらつらと読んでいたのだが、そういえばPHPはどうなっているか気になったのでちょっと試してみた。

まずPHPのsplit関数だが、マニュアルによれば

説明
array split ( string $pattern , string $string [, int $limit ] )

string を、正規表現によって配列に分割します。

となっている。つまり、splitの第一引数は正規表現しかない。

$str = "a,b,c";
print_r(split(",", $str));
/*当然下のようになる。
Array
(
    [0] => a
    [1] => b
    [2] => c
)
*/

$str = "a,,c,,";
print_r(split(",", $str));
/* 末尾の空要素は無視されない。
Array
(
    [0] => a
    [1] =>
    [2] => c
    [3] =>
    [4] =>
)
*/

$str = "";
print_r(split(",", $str));
/* 空文字を分割すると空の配列が返る
Array
(
    [0] =>
)
*/

$str = "";
print_r(split("", $str));
/*
分割パターンが空だとエラーになって実行できない。
*/

$str = "a:b;c.d";
print_r(split("\.", $str));
/* 文字列では指定できないので意地になってみる。
Array
(
    [0] => a:b;c
    [1] => d
)
*/

$str = "a:b;c.d";
print_r(split("[:;.]", $str));
/* 正規表現だから範囲指定で。
Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => d
)
*/

$str = "a.b.c";
print_r(split("[.]", $str));
/* これも同じ。
Array
(
    [0] => a
    [1] => b
    [2] => c
)
*/

$str = "a;b;c";
print_r(split(":", $str));
/* どれにもマッチしない。
Array
(
    [0] => a;b;c
)
*/

と、まあここまではいい。正規表現なのか文字列なのか区別がないので、正規表現しか受け付けないという潔さも、まあいいだろう。文字列が使いたいならexplodeすればいいじゃない、と王妃もおっしゃってましたし。

ところが、納得できないのはこれだ。

$str = "a.b.c";
print_r(split(".", $str));
/* 全部空文字で返ってきたよ。。。
Array
(
    [0] =>
    [1] =>
    [2] =>
    [3] =>
    [4] =>
    [5] =>
)
*/

$ret = split(".", $str);
var_dump($ret);
/* 文字列だって言い張るんだよ。。。
array(6) {
  [0]=>
  string(0) ""
  [1]=>
  string(0) ""
  [2]=>
  string(0) ""
  [3]=>
  string(0) ""
  [4]=>
  string(0) ""
  [5]=>
  string(0) ""
}
*/

直感的には、空の配列かFALSEが返ってくるような気がするのだが、こういうことらしい。ううむ。

Posted in PHP | 2 Comments »

夏休み最後の日は親も大変

Posted on September, 1 at 4:10 pm

今の小学校では、夏休みの宿題、たとえば算数とか漢字のドリルなどは、親が採点するようになっている。厚紙に印刷されたカードにドリルをやった日付と点数を記入する欄があり、そちらに親が点数を書いて提出するのだ。

20年くらい前は、採点するのは教員の仕事だったはずだ。ずいぶんと世の中も変わったものである。

こうなると、8月31日までめいっぱい遊んでいた子供を持つ親は大変である。夜遅くまでなんとか宿題を終わらせても、その後でこのたまりにたまった宿題を採点をしてやらないといけないのだ。うちの場合、なんだかんだと採点できるようになったのが夜遅かったので、結局夜中の2時までマルやバツをつけるはめになった。

確かに30人もの生徒の宿題を採点するのも大変だろうが、仕事なのだから可能なスケジュールを引っ張って作業すればいいことだし、これがいったいなぜ親の仕事になっているのかよくわからない。ゆとり教育とはよくいったものである。

Posted in Education, Family | No Comments »

fshの代わりにsshを使う

Posted on September, 1 at 5:23 am

fshがpython-2.4以降にちゃんと対応していないので、fshを使ったシステム構成は後で困ることになるなあ、となんとなく悩んでいたのだが、ふと調べてみると「Speeding up SSH: fsh vs. OpenSSH v4 」という記事を見つけた。そのエントリにリンクされている「Quick-Tip: Reusing OpenSSH connections to the same host 」という記事によれば、OpenSSHのバージョン4以降であればfshのようなコネクションプーリング用のプログラムなしでもコネクションの再利用が出来るらしい。やり方はいたって簡単。$HOME/.ssh/configを編集して下のような行を追加する。

Host *
ControlMaster auto
ControlPath /tmp/%r@%h:%p

意味は、全てのホストでControlMasterを利用するように指定して、接続情報は/tmpに保存する、というものになる。いきなり/tmpだとマルチユーザのシステムではいかがなものかと思われるので、$HOME以下に適当な場所を指定する方がいいとのこと。まあ、その通りだ。autoのかわりにautoaskにすると、毎回接続を試みるたびにセッションを使い回すかどうか質問される。
実際に接続すると、ControlPathで指定した箇所に「リモートユーザ名@ホスト:ポート番号=」という名前のファイルが出来上がる。手元の環境でfsh、ssh共に適当な回数で実行してみたが、こんな結果になった。
まずはfsh:
localhost:~ yagi$ time fsh 192.168.x.xx ls > /dev/null
real 0m0.607s
user 0m0.039s
sys 0m0.021s

localhost:~ yagi$ time fsh 192.168.x.xx ls > /dev/null
real 0m0.121s
user 0m0.038s
sys 0m0.020s

localhost:~ yagi$ time fsh 192.168.x.xx ls > /dev/null

real 0m0.117s
user 0m0.035s
sys 0m0.019s

localhost:~ yagi$ time fsh 192.168.x.xx ls > /dev/null

real 0m0.130s
user 0m0.036s
sys 0m0.021s

初回の接続だけ0.6秒かかったが、あとはおおむね0.12秒くらい。
続いてssh:
何もしないとおよそ0.3秒くらいかかる。ところが、ControlPathで指定したディレクトリにファイルが出来ると一気に
real 0m0.054s
user 0m0.004s
sys 0m0.005s

こんなスコアになってしまった。fshより早いってのはどういうことだ?

Posted in Linux | No Comments »

まめぞうナウ

Posted on August, 31 at 1:31 pm

TS3I00010001.jpg まめぞうの大きさがわかる写真。

080829_2012~01.jpg 猫との比較。

080831_1337~01.jpg 凶暴化する猫。

Posted in Family | No Comments »

PHPのdefined関数でありがちなこと

Posted on August, 27 at 5:28 pm
<?php

define(_A_CONSTANT, 'this is a constant');

if(defined(_A_CONSTANT)){
    print("Hello");
}

?>

超特殊な場合を除き、絶対に「Hello」は出力されない。なぜなら、ちゃんとマニュアルにも記載されているが、上のコードは

<?php

define(_A_CONSTANT, 'this is a constant');

if(defined('this is a constant')){
    print("Hello");
}

?>

と等価になってしまう。

でも他人のコードをデバッグしているときは、これはなかなか見つからない。

<?php

define(_A_CONSTANT, 'this is a constant');

if(defined('_A_CONSTANT')){
    print("Hello");
}

?>

もちろん、これなら動く。

このパターンのバグが今日、職場で発見されたのでメモ。

Posted in PHP | No Comments »

猫が来る

Posted on August, 20 at 5:14 pm

右のやつ。名前はまだない。

追記:長男が名前を考えていた。次男まめぞうの妹ということで、小さいマメ、小豆。

Posted in Animals, Family | No Comments »

PostgreSQLの長期間運用

Posted on August, 19 at 2:09 pm

PostgreSQLの運用では、システム停止をともなう定期的なメンテナンスが必要とされている。

が、じゃあいったいどれくらい、何のためにシステム停止とVACUUM処理をやらなければいけないのかは、いまいちはっきりしたところはわからなかった。たとえば、データ領域が大きくなりすぎてディスク容量を圧迫しているとか、なんとなくパフォーマンスが低下しているとか、そんなタイミングで実施するようなアバウトな運用をしている人も結構いるのではないか。

マニュアルにも明記されているが、上に挙げた理由の他に、PostgreSQLのVACUUMにはもうひとつ重要な機能がある。といっても、通常のVACUUMやVACUUM FULLでは実行されず、psqlから「VACUUM F」まで入力してtabを押しても出てこないのでやっかいなのだが、長期間運用されるPostgreSQLではVACUUM FREEZEを定期的に実行してやる必要がある

VACUUMというのは、PostgreSQLが削除フラグを立てて未使用となったデータ領域を回収してあげるもので、ANALYZEはプランナが利用する統計情報を更新することで利用効率を向上させるものという理解で基本的には間違いないようなのだが、このFREEZEというのは正直なところこれまでその存在も全く知らなかった。今日、たまたま隣席で7.3系のPostgreSQLでVACUUM作業中の人がログに変なメッセージを見つけたのがきっかけで発見した。

同じような警告メッセージがPostgreSQL7.2のChangelogにある。

NOTICE:  Some databases have not been vacuumed in 1613770184 transactions.
        Better vacuum them within 533713463 transactions,
        or you may have a wraparound failure.

PostgreSQLのトランザクションには一意のID(XID)が振られていて、そのIDは32ビットのシステムでは40億くらい(2の32乗)が上限になっている。この上限値を超えると、トランザクションのIDはリセットされて0に戻ってしまう。数字が循環して0に戻ると、突然自分より前のトランザクションが存在しない状態になってしまい、逆に過去のトランザクションが未来のものに見えるようになるため、データ破壊を引き起こす。上のメッセージは、メンテナンス忘れなどでトランザクション数の累計が15億を超えるとVACUUM時に出てくるものだ。

VACUUM FREEZEはこの循環を回避するための機能で、現在のトランザクション以外のトランザクションにFrozenXIDというのを追加して、周回違いのIDを見間違わずに過去のものと判別できるようにする。何がなんだかわからないので、イメージしかわかないのだが、きっとPostgreSQLのトランザクションデータは構造体みたいなもので、そこにXIDというメンバ変数があって、そいつを更新してくれる、みたいなものなのだろう。

ちなみにVACUUM FREEZEはPostgreSQL 7.2から導入された機能で、上の説明もだいたいそれに合わせたものになっている。というのも、8.3のマニュアルではVACUUM FREEZEは廃止予定となっており、VACUUMの実行はpostgres.confのvacuum_freeze_min_ageの値を0に設定したものと同じことになる。つまり、VACUUMで代用できるわけだ。どうりでpsqlからもちゃんと出てこないわけだ。

PostgreSQLの7系で無停止運転が出来ないのは、こういう理由もあったからだ。

Posted in PostgreSQL | No Comments »

ルーツ

Posted on August, 17 at 3:06 pm

といってもコーヒーの話ではない。

会社の同僚が帰省中に読むのにちょうどいい本はないかというので、たまたま思い出した「ルーツ」はどうかと勧めてみた。

“ルーツ 1 (1) (現代教養文庫 971)” (アレックス・ヘイリー)

ちょっと前に、ブックオフがけしからんのは「ルーツ」のハードカバー版が100円コーナーで叩き売りされているところだ、と話していたので思い出したのだ。

しかしながら、他の同僚たちに聞いてみると、ほとんど誰も「ルーツ」のことを知らないのに驚いた。一過性のベストセラーとして歴史に葬られたような売られ方をしている本だから仕方がないのかもしれない。

ただし、同世代の中には、読んだことはなくてもこのテレビドラマ版“ルーツ コレクターズBOX” のことを覚えている人なら少しはいた。といっても、1977年に放映されたものなのであらすじなどは誰も覚えていなかったが、逃亡を図って捕まった主人公クンタ・キンテが白人たちに木に縛られ、足の先を斧で切り落とされるという強烈なシーンだけははっきりと覚えていると皆が口を揃えていた。確かに、子供の頃、このシーンを観て、父にどうしてこんなことをしているの?と尋ねると、父が黙ってニヤリと笑ったのでもっと怖くなったのを今でもはっきりと覚えている。そして10年以上経ってこの本を読み、ああ、これがあのときのあの場面か、と不意の再会に驚いたのも今では思い出だ。

著者のアレックス・ヘイリーの名前はジャズが好きな人ならどこかで目にしたことがあるかもしれない。マイルズ・デイヴィスの自伝“マイルス・デイビス自叙伝”を読んだ人は、マイルズにインタビューを申し込んで、当時ボクシングで体を鍛えていたマイルスにじゃあリングに上がれといわれてボコボコにされ、それでも引き下がらなかったので根性を認められて晴れてインタビューを取れたジャーナリストの逸話を覚えているだろう。その哀れなインタビュアーこそが、著者のアレックス・ヘイリーその人だ。

「ルーツ」は、そんな型破りのジャーナリストが、奴隷としてアフリカで誘拐されアメリカに連れ去られてきた祖先に至までの自分の血縁を遡り、数世代に渡るスケールの大きなドラマとして再構成した物語だ。その時間軸、受け継がれていく血脈というあたりは、“橋のない川”“百年の孤独” に通じ、苦難の歴史を経た一家の物語が、やがて著者自身の誕生の瞬間にたどり着くあたりは読んでいて思わず身震いしてしまうほどだ。

一部が盗作であった(65万ドルを支払って和解)ことにより非難を浴び、そのためトニ・モリソンのような評価を受けることもないまま、著者の病死もあって歴史の中に埋もれてしまいそうな本なのだが、それでも読む価値はある本だと断言できる。

Posted in Books | No Comments »

Net_POP3でgmail

Posted on August, 11 at 10:25 am

PEARのNet_POP3でGmailからメールを受信するのはこんな感じでできる。実行環境で「ssl://」で始まるデータストリーム型がサポートされていれば問題ない。調べるには

$ php -i | grep "Stream Socket"
Registered Stream Socket Transports => tcp, udp, unix, udg, ssl, sslv3, sslv2, tls

このようにsslが表示されていればいい。

スクリプトは、例えば

<?php
require_once 'Net/POP3.php';
$host = 'ssl://pop.gmail.com';
$port = '995';
$user = 'Gmailのメールアドレス';
$pass = 'パスワード';

$pop = new Net_POP3();
$pop->connect($host, $port);
$pop->login($user, $pass, USER);
//成功したら配列が、失敗したらfalseが返る
$messages = $pop->getListing();
if($messages){
        foreach($messages as $k => $row){
                $id = $row['msg_id'];
                //特にやることもないのでとりあえず配列に入れる
                $mail[] = $pop->getMsg($id);
        }
}
$pop->disconnect();
print_r($mail);
exit;
?>

こんな風になる。

今さらなんだよ、という向きもあるかもしれないが、例えばGoogleのメールサーバを使って独自ドメインサービスをやっている場合、これで自分のサーバで空メールのサービスを立ち上げるときなどに使えるので、まあメモということで。

Posted in Family, Google, PHP | No Comments »

pgpool-IIのオンラインリカバリ機能と格闘中

Posted on August, 6 at 9:57 pm

いろいろ気づいたことのメモ。

まずは目玉機能のオンラインリカバリについて。プロジェクトのページによれば

1. CHECKPOINT 実行
2. ファーストステージの実施
3. 接続がすべて切断されるまで待機
4. CHECKPOINT 実行
5. セカンドステージの実施
6. postmaster の起動(pgpool_remote_start の実行)
7. ノードの復帰

という手順でリカバリされるのだが、手元の環境でまず引っかかったのが3の「接続がすべて切断されるまで待機」のところ。というのも、pgpool-IIにpgpoolを経由して接続するという変な環境だったのだが、child_life_timeを0に設定していたために接続がいつまで経っても切れずにここで失敗してしまったのだ。

で、pgpool-IIのソース(現時点でrev.1.21)を調べると(これは会社の同僚がやっていた)、まだ接続が切れていなければ3秒待ってから最大30回ループすることになっている。

/*
 * Wait all connections are closed.
 */
static int wait_connection_closed(void)
{
	int i = 0;

	do {

		if (Req_info->conn_counter == 0)
			return 0;

		if (WAIT_RETRY_COUNT != 0)
			sleep(3);
	} while (i++ < WAIT_RETRY_COUNT);

	pool_error("wait_connection_closed: existing connections did not close in %d sec.", pool_config->recovery_timeout);
	return 1;
}

なぜかしびれた。これはプログラム内に意味深な数字を見つけたときのプログラマの健康な反応なのだろう。まあ、根拠はよくわからないが、まあそういう数字であると。ふむふむ。

で、接続元がpgpoolだとコネクションを張りっぱなしになるのでこのループで終了してしまう。負荷の高いサービスだと、pgpool経由でpgpoolにつないだ状態では、child_life_timeを下げてもなかなかチャンスがこないかもしれない。というわけで現在ここを試験中。

接続の問題が解消されると、あっさり動作した。PostgreSQLは7系なのでrsyncで動作している。

Posted in PostgreSQL, pgpool | No Comments »