blog.ishinao.net

318月/06

いろいろ買った

近所のオリンピックでテキトーに自転車を買った後にを読んだら、「ちゃんと自転車に乗る気があるなら、近所のスーパーで自転車を買ってはダメ」と書かれている(本格的な利用に耐えられる自転車はスーパーとかでは売ってない4、5万円〜の価格帯のやつだそうな)のを読んで、orz...となったわけですが、たぶんいい自転車と乗り比べないと俺には違いはわからないだろうということで、しばらく体が慣れるまではこの自転車に乗っておこうと思いつつ、いろいろ関連物を買ってみたわけですよ。

今日買ったのは、ドリンクホルダーとサイクルコンピュータ()とグローブとサドルバッグとバックミラーとオイルと空気入れ。一通り取り付けてみたんだけど、サイクルコンピュータがセンサーから情報を受け取れていない。センサーと磁石の距離が遠すぎたのかなー。それともセンサーの故障? といったところで今日は時間切れになってしまったんで、明日続きをやらないと。これが一番楽しそうなブツなんだから、ちゃんと動いてくれないと困るのよ。

Filed under: 日記 No Comments
318月/06

サーバートラブル中です

セッション&キャッシュサーバーでディスクフルをやらかしてしまいまして、1470.net関連のサービスが不安定になっています。


セッション&キャッシュを試行錯誤(ファイル、memcached)の末、MySQLバックエンドに切り替えたんだけど、セッション&キャッシュ用MySQLでbinlogを取る設定になっていたんで、セッション&キャッシュの大量の更新ログを出力してしまい、想定外の速さでディスクを食いつぶしていった、ということだった。

Filed under: 日記 No Comments
298月/06

Plaggerをインストールしてみた

死ぬほど依存モジュールが多かったけど、基本的にCPANでinstall Plaggerときどきforceな感じでインストールできた。で、よく分からんけどまず基本はBloglinesのGmail転送だよねと、YAMLファイルを書いて、plagger --config bloglines2gmail.ymlとかしてみたら、gmail_notify.ttがないとかエラー終了して、Bloglinesの未読が消えた。がっでむ。結局、

svn export http://svn.bulknews.net/repos/plagger/trunk/plagger/assets/

でテンプレートファイルをレポジトリから持ってきて、global/assets_pathにそのディレクトリを指定して対応したけど、テンプレートファイルってこうやって別に入れるものなの? どこかにインストールされていそうな気がするんだけど、よくわからん。

Filed under: 日記 No Comments
298月/06

またマルチアカウントなspammerが来てるなー

xdjfgbuhg
vburhnem
y564jf8mg
m3nf72b6
x6j37fhga
ldi9ebguf
cgytxu3y91
wyebfts3
p4n56122
f6hfuye6
a4hr63g2a
fjf74fhd62
hhdyn36
v4hte53hd
dkduen7g
dkduen3ygt
jduye73bg
ikkifjeu3
due7e63h9
y93je7fw
jsyd74nbg
cvcxrdrk4
pdur73b1
shs71h36f
aaudyrng
fjryu4hgoo
lseujg84
skisu47gh
xcefry391
s7sbh37gh
dkdugu4ng
duu3bgpg
hmbnvye43
dud71s9d
sis8gj00j
djr74ngidy
p4kgu7gber
x7rh4ht6
nsy8fjt3e
firjtu36
irjtufjr74
suryru74
dkdue732a
vkrirh47
soed74se
lkgjfnruv
n47rhw93
p12ns7fe
du37eu
zbceu39
g4n5r7rh1
ir74nhft35
ogjrnfyu
djyu37gfe
so4tyr632
f2314det
aofjrur73h
fjfysbsfq
i3he51f
dhe351hs
lkgurndg
vncmdh3
h4nd63n27
o4f6ebwy
sksye63bg
ffye73bg
kis3hgy73
z9f843j
gkfj4gh98
f9f84jh7
d8485jgb
apf93jes
d9h4jh84h
d9h84gg5
d74j0d84j
dk3udj37
mvlwp9jg
dlod94jh9
dod84jtff
d47j8ed2
ptihjruf
fgifdnrf8
r4t7461
a0z38gk
e47tfjri
sv84jf75
zlfi4jgy
prltmfjs
jfu5ke84
xmbjfnri
sk3ls81j
y47fj5uro
dlritj4nbh
kkdirj48
jfmtt273
plfjrntyu
n5hyrf74t
tt84jt849s4
c02f450t
woitr74n9r
lorgor834
orm9tr4n4
lainj1349
mie8t02f7
pa9ktcmr7
shrbur348j
inr4jr8t34
aru94giuu
z244838en
ma83n8eer
an4hnq
wer8932
owkre934r4
bnur734r7
kfoe8u343
ojf9w4t7
mc48n5t85
o4u45h4t5
nvty5vb5wp
wovu8544jr
iwpvu857
ju58w0vw
vktu58w
mv587tw8v
signr74j5
gkvirng8
so48jg8
pwirnbi7
afkjugt84
mf4mf84
g84ns8t
l8fm48a
n77a4jr9
nv4ka94l8
w83940t
p93ja8aa
ms94ng94
l884ma4
krkan93
lar8an34r
a0023r4b
oi38r8gc
anfiarar3
mvosiru9a
oiur9vfrf
rryr83n8a
aoinar8
vkr8a46a
wfiejroaer7
m83n8rn8r
kdfjae84
lkdjgsproi
kd83jnga9
kfmru33
nvutks7e
nvwpf6b
pmxurai
ma38q4js
zqjldf9ua
oa8ka349rk
bndkeyr6
mce8eu
kurjr7ea
krfa8ra
la3rjfna
aeiefhai7
nfloeua7
oqm7f4kg8a
v72mvks
nbbcye363
zarq03q
zlaia23r
ashape3
zmar131
apaura098
vkoar9a3
arakea8
kru63nf7
nciaor81
xveyq734q
aeiapr7a
bspiru8
jgp49jd
fjbg3i
fjgblwlp3
gl3oyfks
gugj4649
gggfjggk
nfl02840
kawausa
jrh84839
lhgjro49
jgle6389
zmzcmcor2
mrhyfk820
aieial78
ieje745s
amaoei3
lvue7rtr
ja38jcfie7
e8fe73fre
eue7jee3
cmdua759
e3jfaviez
zoe74tf
zndgt258
cbsger8r
cmfi73ak
mchr629
ovneyxmg
laien6394
m81kfur7
hfirg3p9
jglfidge62
hfltgoege0
hekrhd6
h94jgklfhw
j5b2740rkf
pp4jgjdjfj
hoglnvbcm
ncvbmrju
bcieibup3
vbcx840
bhgbjnc93
bmchv3902
nvbwfhi6
bcmo38a
jflgh079jdl
ofklsb6
nbk730hr20
kfjbwmdj0
ghdlkht9
nhflir0
b639flwh
i8394heiii
kdj26r983
kdoud993
hlriyt89
jfrketl2g7
kflypfwo93
lkhkglwk
ldfor0legk
krhebi7f
hwu042003
hfi6tfh
wrhfl38
hgkwugtl8
rkhw73
lhbwlfu34
jfrjglw7
hkrugo48
j4hlkwlt
jhg37tlw
jkegflw94
lrfhtp3r59
it89tg1tlw
hlbicge3
ker3lg83h
hfkrgh0
jfoehjf20
hek39fl
isjdkkif3
h83y3r
xiu94575
e23i84her
egtp2i4
hgo300200
jf73pgj21
gh2906sji
hfo39t
hi3y693k
hfi39t2k
lejp4ug0
gkp3ut9
pfjgp37lwsp
lkpjvgii8
kpehs605
kgoe305jg
h495kd3o
oghdo93
loehf972
loh04jhpe
jhoehgi4o
rhornfl8
jpemhjru
korkho47
fhofki47k3
jpyti7639t
joehdmn83
khrofnr9
heoemj83ki
djvo370fh
jfpwgfk6k
hfwkdjkv2
tttflehwo
jgprhfpo370
yeorjf73
kgoirhdkd6
jroeheo9
fjfrigh8

前回の情報とあわせて、spammerがどういうことをやっている&やりたいのか分析する対象として使わせていただきます。飽きたらアカウントごと消します。

それにしても

前回も今回も、基本的に1アカウントごとに専用ドメインを持つ1サイトのみを登録していってるけど、最近ドメインが安くなっているとはいえ、それなりに投資はしているんだろうなー。それともどこかにただ同然でドメイン+レンタルスペースを配っているサービスとかがあって、そういうのを使っているんだろうか? 内容は良くありがちなキーワードポータル系で、できはいい方ではないけど、こんなんでもそれなりの成果はあるのかなー?

Filed under: 日記 No Comments
258月/06

PHPで安全なセッション管理を実現する方法

なんかこの辺の情報って、ちゃんとまとまっているのを見たことがない気がするんで、まとめておく。特にPHPのセッションを標準設定で使った場合の問題とその対策について。ツッコミ歓迎。

まずセッションの仕組みの基本から。

Webアプリケーションは、通常ステートレス(=状態がない)である。ステートレスというのは何かというと、ユーザー(ブラウザ)が連続的に複数回のアクセス(Webページの表示)しても、サーバー側はそれを特定のユーザーの連続したアクセスと認識せず、単に誰ともしらない複数のユーザーが複数回アクセスしたものとして処理すること。

ステートレスの反対のステートフル(状態を持つ)ってのは、ユーザーがページA→ページB→ページCなんてアクセスをしたり、あるいはページBでフォームからデータを投稿したりしたときに、そのアクセス履歴やら入力したデータなどの情報(=状態)を保持した上で、次にそのユーザーがアクセスしたときに、そういう状態を持つユーザーからのアクセスであることをサーバーが(認識しようと思えば)認識できることを表す。

ステートレスなWebアプリケーションにステートを持たせるための機能が、いわゆるセッション管理機能。これは、

  • Webアプリケーションにアクセスするユーザーを特定する
  • そのユーザーに関する状態(情報)を、サーバー側で保持する

という二つの仕組みを組み合わせて実現している。

ユーザーを特定するための仕組みとしては、ユーザーに対して自動的に(通常初回アクセス時に)識別コードを割り振り、その識別コードを使って特定する。この識別コードのことをセッションIDという。ユーザー(ブラウザ)は、そのWebアプリケーションにアクセスするたびに毎回必ずセッションIDを送信する必要がある。そうしないと、サーバーは同一のユーザーだと識別できない。

セッションIDは通常Cookieとしてブラウザに記憶され、Webアプリケーション(というか、特定のドメインの特定のパス以下)にアクセスするときには、ブラウザが自動的に送信してくれる。Cookieに対応していないブラウザ(携帯とか)では、あらゆるリンクのQUERY_STRINGにセッションID情報をくっつけて送ったりする場合もある。

このセッションIDというのは、ユニークでありさえすれば機能的(ユーザーの識別)には問題ないんだけど、セキュリティまで考えると、ユニークであればなんでもいいという訳ではない。

たとえばセッションIDを、アクセスしてきた人に連番で割り振るような仕組みになっているとする。自分のセッションIDが100だったときに、試しにセッションIDを99とか98に書き換えてアクセスしてみたら、他人(=自分の直前にそのWebアプリケーションを利用した人)のセッションIDを使ってアクセスできてしまうかもしれない。

その場合、そのセッションIDに結びつけられた情報には、他人の個人情報などが含まれていたりして、そういうものが表示されてしまうかもしれない。あるいは、そういう他人の情報を書き換えてしまったり、あるいはその人の権限で何らかの処理を実行できてしまうかもしれない。

それを考えると、セッションIDは第三者が推測可能な内容であってはいけない。そのため一般的には、ランダム(=ロジックで推測されない)かつ十分に大きい(=可能性のある値を順次試す(=ブルートフォースアタック)ことで、実用的な時間内に正解に行き当たることがない)識別コードを生成して利用する。たとえば、マイクロ秒単位の現在時間+乱数発生器+サーバー固有の値+ハッシュ関数の組み合わせを使って、数十桁の16進数文字列を生成したり、とか。

ともかくそうやってセッションIDを使ってユーザーを識別できるようにしたら、そのセッションIDをキーにサーバー側で情報を保存する場所を用意する。どこに保存してもいいんだけど、PHPのデフォルトのセッション機能では、指定されたディレクトリ(/tmpとか)の下にセッションIDをファイル名の一部に持つファイルを作成し、その中にシリアライズ(バイト列表現に変換)されたPHPの配列を保存するようになっている。セッションハンドラーを変えれば、DBに保存したり、メモリキャッシュに保存したり、いろいろできる。

これで、

  • ユーザーがセッションIDをサーバーに送る
  • セッションIDにマッチするセッション保存ファイルを読み込む
  • セッション保存ファイルの内容を復元(unserialize)して、$_SESSION変数に入れる

といった形でPHPのセッション機能が実現されるようになる。

って、セッションの仕組みの基本を説明しているだけで、ずいぶん長くなったな。ここまでは前振り。次からが本論。

PHPのセッション機能は、セッション固定攻撃(session fixation)に対して脆弱だ。セッション固定攻撃っつーのは、攻撃者が用意したセッションIDを強制的に使わせることによって、本来推測不可能なはずのセッションIDの、推測(というかあらかじめ知ること)を可能にしてしまう方法。

PHPのセッション機能は通常、Cookie経由のセッションIDもQUERY_STRING経由のセッションIDも、どちらも認識できる(session.use_only_cookiesオプションでQUERY_STRING経由のセッションIDを認識しないようにできる)。また、セッションIDが外部から渡された場合、そのセッションIDに対応するセッションデータ(通常はファイル)が存在しない場合は、自動的にそのセッションIDに対応するセッションファイルを生成し、正常にセッションを開始してしまう。

つまり、あるユーザーがあるWebアプリケーションにアクセスする入り口のところで、何らかの方法(Cookieのドメインによる有効範囲をうまく利用したり、セッションID付きリンクを踏ませたり)でセッションIDを固定してしまえば、そのユーザーはそのアプリケーションで、既知のセッションIDを使ってセッションを開始してしまうことになる。

今のところPHPには、セッションデータが生成されていないセッションIDが渡されたときに、そのセッションIDを利用したセッションを有効にさせない、というようなオプションがない(パッチはある)ので、外部から渡されたセッションIDでセッションが開始されてしまうことは避けられない。

じゃあどうすればいいかというと、

  • セッション変数の内容を見て、その正当性を確認する
  • セッションIDを変更することによって、危険なセッションIDを無効にしてしまう

あたりの組み合わせが対策となる。たとえば、

session_start();
if (!isset($_SESSION['_SESSION_CHECK']) {
session_regenerate_id();
$_SESSION['_SESSION_CHECK'] = array(
'startTime' => time(),
);
}

などとする。

これで、もしも正しく初期化されていないセッションが開始された場合は、session_regenerate_id()関数を使って、新しいセッションIDを生成しなおすことによって、もしかしたら外部から渡されたのかもしれないセッションIDは使わないことになる。

また、次のようにすることで、より安全性を高めることもできる。

session_start();
if (
!isset($_SESSION['_SESSION_CHECK']) ||
($_SESSION['_SESSION_CHECK']['REMOTE_ADDR'] != $_SERVER['REMOTE_ADDR']))
{
session_regenerate_id();
$_SESSION['_SESSION_CHECK'] = array(
'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'],
'startTime' => time(),
);
}

これは、セッション変数に入っている(=セッション初期化時の)ユーザーのIPアドレスと、今回アクセスしたユーザーのIPアドレスがマッチしない場合も、同様にsession_regenerate_id()関数を使って新しいセッションIDが生成されるようにするコードだ。

ただし、複数のゲートウェイを持つネットワークからのアクセスなどの場合は、正当なユーザーでもアクセスのたびに別のIPアドレスが使われることもあるので、このような書き方はいつでも使えるわけではない。利用するユーザーの環境が限定できる場合のみ利用できる方法となる(あと、リバースプロキシとか使っている環境でもこれだと無意味)。

また、上記のような特定の(不正な可能性のある)条件にマッチした場合に限らず、たとえば、

session_start();
if (!isset($_SESSION['_SESSION_CHECK']) || (rand(0, 100) > 99)) {
session_regenerate_id();
$_SESSION['_SESSION_CHECK'] = array(
'startTime' => time(),
);
}

のように、ランダムに1%の確率でセッションIDの変更を行うという方法もありだろう。これによって、未知の(準備したロジックでは判別できない)攻撃に対しても、ある程度安全性を高めることができるようになる。

しかし、実際には上記のコードはあまり役に立たない。

というのは、session_regenerate_id()関数は、セッションIDは付け替えるが、古いセッションIDに結びつけられたセッションデータ自体はそのまま残してしまう(PHP 5.1.0以降ならば、session_regenerate_id(true)とすることで、古いセッションIDに結びつけられたデータを破棄してくれるので、以下の話は関係なくなる)。たとえば、元のセッションIDがxxxxxxで、session_regenerate_id()後のセッションIDがyyyyyyyだった場合、セッションデータファイルとしてはその両方が残ってしまう。

そのため、せっかくセッションIDを変更しても、古いセッションデータに有効な情報(たとえばあるユーザーのログイン権限と結びつけられた情報など)が含まれていた場合、せっかくセッションIDを付け替えても、古いセッションIDの方が悪用されてしまう可能性がある。

単純に、session_regenerate_id()でセッションIDを変更するだけで安全性が確保できるのは、あくまでもsession_regenerate_id()する前のセッションデータに、有効な情報が含まれていなかった場合のみなのだ。

そこで、

if (isLoginOk()) { // 認証が通った
session_regenerate_id();
$_SESSION['userId'] = [ユーザーID];
}

のような形で、ログイン処理の後など、セッション内に重要なデータを登録するタイミングで、session_regenerate_id()を実行してセッションIDを付け替えることによって、セッションの安全性を高めることになる。

たとえそれまで使っていたセッションIDが危険なものだったとしても、それには重要な情報は含まれておらず、重要な情報は必ずサーバー側で新しく生成したセッションIDに結びつけられることになるからだ。

しかし、未知の危険性に対策のために、すでにセッションデータに有効な情報が含まれている状態で、セッションIDを変更したい場合もあるだろう。その場合はどうすればいいだろうか。

セッションを継続する必要がないのならば、session_destroy()を実行することで、現在のセッションIDに結びつけられたセッションデータは破棄される。しかし、それでは新しいセッションIDで今までの情報を引き継ぐことができない。

そこで、次のようなコードを使うことになる。

session_start();
$tmp = $_SESSION;
session_destroy();
session_id(md5(uniqid(rand(), true)));
session_start();
$_SESSION = $tmp;

テンポラリ変数に現在のセッション変数を待避してから、現在のセッションデータを破棄し、新しいセッションIDをsession_regenerate_id()を使わずに独自のコードで生成してから、再びセッションを開始し、先ほどテンポラリ変数に待避してあったセッション変数を、新しいセッション変数にセットし直す、という方法だ(なぜsession_regenerate_id()ではなくsession_id(md5(uniqid(rand(), true)))を使っているかについてはコメント欄参照)

これで、すでにセッションデータに有効な情報が含まれている場合でも、セッションIDを変更して安全性を高めることができることになる。


何か問題がありそうな記述があったらツッコミください。修正しますんで。


2006/11/20 http://tdiary.ishinao.net/20061120.html#p01に追加情報を書きました

Filed under: 日記 No Comments
258月/06

残り容量が数十Mバイトになっていた

PCがなんかくそ遅いなーと思ってふと空きディスク容量をみたら、残り数十Mバイトまで減っていた。Folder Size for Windowsで各ディレクトリ単位のディスク使用量をながめてみたところ、

  • Thunderbirdでimapでアクセスしているアカウントのデータフォルダに、なぜか1GバイトオーバーのINBOXファイルがあった。なにこれ? 削除したけど別に動作には支障はなし。
  • puttyのlogが無限に追記されたよ……。数Gバイト。
  • 昔ダウンロードしたCD/DVD-ROMのisoイメージファイルが、そこかしこに消されず残ってたよ。10Gバイトオーバー。

あと、細かいテンポラリディレクトリの中身とか消したら、30Gバイトくらい空いた。そこまでやって久しぶりにデフラグを起動したら、表示が真っ赤(ほとんど全部断片化されている)だったので、最適化実行中。これで使わないファイルの圧縮とかが走ればさらに余裕ができるかな。

Filed under: 日記 No Comments
238月/06

String.replace(/\n/g, ‘<br />’)の挙動の違い

↑の2番目の機能を実現するために、Ajaxで取ってきたメモのJSONデータ中に含まれるプレーンテキスト形式のコメント本文を、

comment.escapeHTML().replace(/\n/g, '<br />')

してからinnerHTMLにセットしているんだけど(escapeHTML()はprototype.jsの拡張ね)、FirefoxとOperaでは想定通り表示されるのに、IEではなぜか連続した改行が1個の<br />になってしまい、空行が表現できていない模様。これって何が原因なんだ?

String.escapeHTML()のせいだった

IEでString.escapeHTML()した段階で、連続改行が消えてしまうらしい。確かprototype.jsのesacpeHTML処理ってブラウザ依存の特殊な書き方だった気がするし、そのせいだろうな。

というわけで、自前で置換を使ったescapeHTML()相当の処理に書き直して対応。ただ、非Windows環境ではまた別の問題が出るのかも?

Filed under: 日記 No Comments
238月/06

1470.netメモの一覧表示時の可読性向上

メモを一覧表示する際、メモのパーマリンク表示時以外では、255バイト以上は省略表示していたのを、1000バイトまで省略せずに表示するようにしました。また、省略表示時にも改行は有効になるようにしました。

また、省略表示時に「続きを読む」をクリックすると、従来はメモのパーマリンクに遷移していましたが、Ajaxが有効な環境の場合はその場でコメント全文を表示するように変更しました。

Filed under: 日記 No Comments
218月/06

自転車買った

っつーか、オクサンに2ヶ月遅れの誕生日プレゼントとして買ってもらったんだけど。サギサカのRSXとしか書いてなくて、サギサカってメーカーはWeb上に公式な情報がほとんどないんで、いまいちよくわからん。近所のオリンピックに置いてあった中から機能ベース(前後サスペンション、オートライト、前後泥よけ)で選んだだけなんで。

ちょっとだけ近所をぐるっと回ってきたところ、一応スポーツサイクル向けだけあって、今まで乗ってきた通勤通学自転車と比べると、ずいぶんちゃんと走るなー。車道を走ってもそんなにきつくない。ただ、バックミラーがないと路駐避けとかのときに危ないんで、そのうちつけよう。あと、スピードメーターがついていると思っていたんだけど、二つあるメーターは前後のギア表示用のメーターで、スピードメーターはついてなかった。スピード+トリップメーターも後で買おう。

しばらくは近所を走って体を(特にけつの痛みを)慣らして、そのうちときどき通勤で使えるようになるといいなー。ただ、会社まで25kmもあるから、常時通勤に使うってのは無理だろうけど。時間もどうやったって電車よりかかるはずだし。

Filed under: 日記 No Comments
198月/06

PHP 6のUnicodeサポート

昼飯の時に「たぶんバイナリ文字列とUTF文字列という二つの文字列型が追加されて、文字列処理関数は型を見て処理を振り分けるんじゃない?」と100%想像で言ってしまったんで、気になってPHP6-devのソースを見てみた。

zend_variables.cの

   switch (Z_TYPE_P(zvalue) & ~IS_CONSTANT_INDEX) {
case IS_CONSTANT: {
TSRMLS_FETCH();
if (UG(unicode)) goto dtor_unicode;
}
case IS_STRING:
CHECK_ZVAL_STRING_REL(zvalue);
free(Z_STRVAL_P(zvalue));
break;
case IS_UNICODE:
dtor_unicode:
CHECK_ZVAL_UNICODE_REL(zvalue);
free(Z_USTRVAL_P(zvalue));
break;
case IS_ARRAY:
case IS_CONSTANT_ARRAY:
case IS_OBJECT:
case IS_RESOURCE:
zend_error(E_CORE_ERROR, "Internal zval's can't be arrays, objects or resources");
break;
case IS_LONG:
case IS_DOUBLE:
case IS_BOOL:
case IS_NULL:
default:
break;
}

あたりを見ると、やっぱり従来型のバイナリ文字列とUNICODE文字列の二つの文字列型になるみたいだね。で、各文字列処理関数ではzend_builtin_functions.cから適当に抜き出した、

ZEND_NAMED_FUNCTION(zend_if_strlen)
{
zval **str;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &str) == FAILURE) {
ZEND_WRONG_PARAM_COUNT();
}
switch (Z_TYPE_PP(str)) {
case IS_UNICODE:
RETVAL_LONG(u_countChar32(Z_USTRVAL_PP(str), Z_USTRLEN_PP(str)));
break;
case IS_STRING:
RETVAL_LONG(Z_STRLEN_PP(str));
break;
default:
convert_to_text_ex(str);
RETVAL_LONG(Z_UNILEN_PP(str));
break;
}
}

みたいな感じで、やっぱりUNICODE文字列かどうかで単純に処理を振り分けているみたい。UNICODE文字列とバイナリ文字列が入り交じった状態の比較とか、文字列結合とかがどうなるかまでは追ってない。

Filed under: 日記 No Comments