SlideShare a Scribd company logo
お前は PHP の歴史的な
理由の数を覚えているのか
Kousuke Ebihara (海老原昂輔)
<kousuke@co3k.org>
答
答
5 個
答
5 個
_人人人人人人人人人人_	
 
> 言うほどなかった <	
 
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄	
 
※ドキュメントから数えただけなので実体としてはもっとありそうですが、今回は考えません
自己紹介
• Kousuke Ebihara (海老原昂輔) a.k.a. @co3k
• 株式会社 VOYAGE GROUP (2014/02 より)
• スマホコミュケーション事業室で Python 書いてます
• そういえば PHP 全然書いてないです
• セキュリティ周り
• 以前は某 OSS の SNS エンジンとかその辺やってました
歴史的な理由のある機能
• implode()
• urlencode() / rawurlencode()
• double 型 / float 型 と、 gettype() の返り値
• Phar アーカイブのマニフェスト情報
• Zend Engine の HashTable (間に合わず)
調査方法
• PHP 4 以降については普通に Git で潜っていく
• php-src : 公式の Git リポジトリ
• ドキュメント: git-svn で公式のリポジトリを変換
• PHP 3 以前
• museum.php.net (かなり重いので注意)
• ML
• 1996/07 - 1998/01: PHP/FI Mailing List
• 1996/12 以降: php-internals (marc.info なら旧 php-dev 時代も追える)
implode()
implode() の歴史的な理由
• 「implode() は、歴史的な理由により、引数をどちらの順番
でも受けつけることが可能です」
• おそらく PHP で一番有名な「歴史的な理由」
implode() の実装
PHP_FUNCTION(implode)!
{!
********************** SNIP *********************!
if (arg2 == NULL) {!
********************** SNIP *********************!
} else {!
if (Z_TYPE_PP(arg1) == IS_ARRAY) {!
arr = *arg1;!
convert_to_string_ex(arg2);!
delim = *arg2;!
} else if (Z_TYPE_PP(arg2) == IS_ARRAY) {!
arr = *arg2;!
convert_to_string_ex(arg1);!
delim = *arg1;!
} else {!
********************** SNIP *********************!
}!
}!
!
php_implode(delim, arr, return_value TSRMLS_CC);
implode() の実装
PHP_FUNCTION(implode)!
{!
********************** SNIP *********************!
if (arg2 == NULL) {!
********************** SNIP *********************!
} else {!
if (Z_TYPE_PP(arg1) == IS_ARRAY) {!
arr = *arg1;!
convert_to_string_ex(arg2);!
delim = *arg2;!
} else if (Z_TYPE_PP(arg2) == IS_ARRAY) {!
arr = *arg2;!
convert_to_string_ex(arg1);!
delim = *arg1;!
} else {!
********************** SNIP *********************!
}!
}!
!
php_implode(delim, arr, return_value TSRMLS_CC);
第 2 引数が指定されている
implode() の実装
PHP_FUNCTION(implode)!
{!
********************** SNIP *********************!
if (arg2 == NULL) {!
********************** SNIP *********************!
} else {!
if (Z_TYPE_PP(arg1) == IS_ARRAY) {!
arr = *arg1;!
convert_to_string_ex(arg2);!
delim = *arg2;!
} else if (Z_TYPE_PP(arg2) == IS_ARRAY) {!
arr = *arg2;!
convert_to_string_ex(arg1);!
delim = *arg1;!
} else {!
********************** SNIP *********************!
}!
}!
!
php_implode(delim, arr, return_value TSRMLS_CC);
第 2 引数が指定されている
配列が第 1 引数に指定され
ていればデリミタは第 2 引数
implode() の実装
PHP_FUNCTION(implode)!
{!
********************** SNIP *********************!
if (arg2 == NULL) {!
********************** SNIP *********************!
} else {!
if (Z_TYPE_PP(arg1) == IS_ARRAY) {!
arr = *arg1;!
convert_to_string_ex(arg2);!
delim = *arg2;!
} else if (Z_TYPE_PP(arg2) == IS_ARRAY) {!
arr = *arg2;!
convert_to_string_ex(arg1);!
delim = *arg1;!
} else {!
********************** SNIP *********************!
}!
}!
!
php_implode(delim, arr, return_value TSRMLS_CC);
第 2 引数が指定されている
配列が第 1 引数に指定され
ていればデリミタは第 2 引数
配列が第 2 引数に指定され
ていればデリミタは第 1 引数
implode() の実装
PHP_FUNCTION(implode)!
{!
********************** SNIP *********************!
if (arg2 == NULL) {!
********************** SNIP *********************!
} else {!
if (Z_TYPE_PP(arg1) == IS_ARRAY) {!
arr = *arg1;!
convert_to_string_ex(arg2);!
delim = *arg2;!
} else if (Z_TYPE_PP(arg2) == IS_ARRAY) {!
arr = *arg2;!
convert_to_string_ex(arg1);!
delim = *arg1;!
} else {!
********************** SNIP *********************!
}!
}!
!
php_implode(delim, arr, return_value TSRMLS_CC);
第 2 引数が指定されている
配列が第 1 引数に指定され
ていればデリミタは第 2 引数
配列が第 2 引数に指定され
ていればデリミタは第 1 引数
配列が指定されていなければ
エラー
implode() の歴史
• PHP/FI 2 には implode() も explode() も存在しない
• PHP 3 にはある
• PHP 3 時点では implode() は現在と同じ挙動に
• つまりこの「歴史的な理由」は PHP/FI 2 → PHP 3 の開
発中に生まれたものと思われる
PHP 3.0a3
(November 23 1997)
• Switched between the 1st and 2nd parameters to
explode(), so that it acts like split()

(拙訳: explode() の第 1 引数と第 2 引数を交換したの
で、 split() と同じように動作するようになりました)
split() と explode()
split() と explode()
元々は逆 (implode() が歴史的な理由により受け付ける順序と同じ�)
PHP 3.0b5
(February 24 1998)
• Made implode() accept arguments in the order
used by explode() as well

(拙訳: implode() が explode() で使われているような引
数順も受け付けるようにしました)
explode() と implode() に
何が起こったか
• PHP 3.0 開発中に explode(), implode(), split() が追加された
• このタイミングで追加、変更された機能は多いので当時の CHANGELOG を眺めているだけでも結構楽しい
• PHP 3.0a3 にて、 explode() の引数順を split() に合わせた
• この結果、 implode() との統一性が取れなくなったので、PHP
3.0b5 にて、 implode() では両方の引数順を受け付けるようにした
• explode() が据え置きだったのは、引数が両方とも文字列型だから?
• わずか 3 ヶ月の「歴史」
urlencode() / rawurlencode()
URL エンコード用の 2 つの関数
• urlencode()
• 文字列を URL エンコード
• rawurlencode()
• 文字列を URL エンコード
URL エンコード用の 2 つの関数
• urlencode()
• 文字列を URL エンコード
• rawurlencode()
• 文字列を URL エンコード
(RFC 3986 に基づかない)
URL エンコード用の 2 つの関数
• urlencode()
• 文字列を URL エンコード
• rawurlencode()
• 文字列を URL エンコード
(RFC 3986 に基づかない)
(RFC 3986 に基づく)
どのような違いがあるか?
• urlencode()
• 空白 (U+0020) を + (U+003B) に置き換える
• ~ (U+007E) をエンコードする (RFC 1738 に基づく)
• rawurlencode()
• 空白 (U+0020) をパーセントエンコードする
• ~ (U+007E) をエンコードしない (RFC 3986 に基づく)
どのような違いがあるか?
• urlencode()
• 空白 (U+0020) を + (U+003B) に置き換える
• ~ (U+007E) をエンコードする (RFC 1738 に基づく)
• rawurlencode()
• 空白 (U+0020) をパーセントエンコードする
• ~ (U+007E) をエンコードしない (RFC 3986 に基づく)
追従漏れじゃね……?
urlencode() の歴史的な理由
• 「歴史的な理由により、この関数は RFC 3986 エンコード
(rawurlencode() を参照してください) とは異なり、空白を
+ 記号にエンコードします」
urlencode() の実装
(EBCDIC モード時の処理は省略)
PHPAPI char *php_url_encode(char const *s, int len, int
*new_length)!
{!
********************** SNIP *********************!
while (from < end) {!
c = *from++;!
!
if (c == ' ') {!
*to++ = '+';!
} else if ((c < '0' && c != '-' && c != '.') ||!
(c < 'A' && c > '9') ||!
(c > 'Z' && c < 'a' && c != '_') ||!
(c > 'z')) {!
to[0] = '%';!
to[1] = hexchars[c >> 4];!
to[2] = hexchars[c & 15];!
to += 3;!
} else {!
*to++ = c;!
}!
}
urlencode() の実装
(EBCDIC モード時の処理は省略)
PHPAPI char *php_url_encode(char const *s, int len, int
*new_length)!
{!
********************** SNIP *********************!
while (from < end) {!
c = *from++;!
!
if (c == ' ') {!
*to++ = '+';!
} else if ((c < '0' && c != '-' && c != '.') ||!
(c < 'A' && c > '9') ||!
(c > 'Z' && c < 'a' && c != '_') ||!
(c > 'z')) {!
to[0] = '%';!
to[1] = hexchars[c >> 4];!
to[2] = hexchars[c & 15];!
to += 3;!
} else {!
*to++ = c;!
}!
}
while ループで文字列終端まで from
を 1 文字ずつ走査
urlencode() の実装
(EBCDIC モード時の処理は省略)
PHPAPI char *php_url_encode(char const *s, int len, int
*new_length)!
{!
********************** SNIP *********************!
while (from < end) {!
c = *from++;!
!
if (c == ' ') {!
*to++ = '+';!
} else if ((c < '0' && c != '-' && c != '.') ||!
(c < 'A' && c > '9') ||!
(c > 'Z' && c < 'a' && c != '_') ||!
(c > 'z')) {!
to[0] = '%';!
to[1] = hexchars[c >> 4];!
to[2] = hexchars[c & 15];!
to += 3;!
} else {!
*to++ = c;!
}!
}
while ループで文字列終端まで from
を 1 文字ずつ走査
スペースを + に置換して to に格納
urlencode() の実装
(EBCDIC モード時の処理は省略)
PHPAPI char *php_url_encode(char const *s, int len, int
*new_length)!
{!
********************** SNIP *********************!
while (from < end) {!
c = *from++;!
!
if (c == ' ') {!
*to++ = '+';!
} else if ((c < '0' && c != '-' && c != '.') ||!
(c < 'A' && c > '9') ||!
(c > 'Z' && c < 'a' && c != '_') ||!
(c > 'z')) {!
to[0] = '%';!
to[1] = hexchars[c >> 4];!
to[2] = hexchars[c & 15];!
to += 3;!
} else {!
*to++ = c;!
}!
}
while ループで文字列終端まで from
を 1 文字ずつ走査
スペースを + に置換して to に格納
それ以外のエンコード対象の文字は
パーセントエンコードして to に格納
urlencode() の実装
(EBCDIC モード時の処理は省略)
PHPAPI char *php_url_encode(char const *s, int len, int
*new_length)!
{!
********************** SNIP *********************!
while (from < end) {!
c = *from++;!
!
if (c == ' ') {!
*to++ = '+';!
} else if ((c < '0' && c != '-' && c != '.') ||!
(c < 'A' && c > '9') ||!
(c > 'Z' && c < 'a' && c != '_') ||!
(c > 'z')) {!
to[0] = '%';!
to[1] = hexchars[c >> 4];!
to[2] = hexchars[c & 15];!
to += 3;!
} else {!
*to++ = c;!
}!
}
while ループで文字列終端まで from
を 1 文字ずつ走査
スペースを + に置換して to に格納
それ以外のエンコード対象の文字は
パーセントエンコードして to に格納
エンコードしない文字はそのまま
to に格納
PHPAPI char *php_raw_url_encode(char const *s, int len, int
*new_length)!
{!
register int x, y;!
unsigned char *str;!
!
str = (unsigned char *) safe_emalloc(3, len, 1);!
for (x = 0, y = 0; len--; x++, y++) {!
str[y] = (unsigned char) s[x];!
if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||!
(str[y] < 'A' && str[y] > '9') ||!
(str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||!
(str[y] > 'z' && str[y] != '~')) {!
str[y++] = '%';!
str[y++] = hexchars[(unsigned char) s[x] >> 4];!
str[y] = hexchars[(unsigned char) s[x] & 15];!
}!
}
rawurlencode() の実装
(EBCDIC モード時の処理は省略)
PHPAPI char *php_raw_url_encode(char const *s, int len, int
*new_length)!
{!
register int x, y;!
unsigned char *str;!
!
str = (unsigned char *) safe_emalloc(3, len, 1);!
for (x = 0, y = 0; len--; x++, y++) {!
str[y] = (unsigned char) s[x];!
if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||!
(str[y] < 'A' && str[y] > '9') ||!
(str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||!
(str[y] > 'z' && str[y] != '~')) {!
str[y++] = '%';!
str[y++] = hexchars[(unsigned char) s[x] >> 4];!
str[y] = hexchars[(unsigned char) s[x] & 15];!
}!
}
rawurlencode() の実装
(EBCDIC モード時の処理は省略)
for ループで文字列終端まで 1 文字
ずつ走査
PHPAPI char *php_raw_url_encode(char const *s, int len, int
*new_length)!
{!
register int x, y;!
unsigned char *str;!
!
str = (unsigned char *) safe_emalloc(3, len, 1);!
for (x = 0, y = 0; len--; x++, y++) {!
str[y] = (unsigned char) s[x];!
if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||!
(str[y] < 'A' && str[y] > '9') ||!
(str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||!
(str[y] > 'z' && str[y] != '~')) {!
str[y++] = '%';!
str[y++] = hexchars[(unsigned char) s[x] >> 4];!
str[y] = hexchars[(unsigned char) s[x] & 15];!
}!
}
rawurlencode() の実装
(EBCDIC モード時の処理は省略)
for ループで文字列終端まで 1 文字
ずつ走査
とりあえず str に文字を格納
PHPAPI char *php_raw_url_encode(char const *s, int len, int
*new_length)!
{!
register int x, y;!
unsigned char *str;!
!
str = (unsigned char *) safe_emalloc(3, len, 1);!
for (x = 0, y = 0; len--; x++, y++) {!
str[y] = (unsigned char) s[x];!
if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||!
(str[y] < 'A' && str[y] > '9') ||!
(str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||!
(str[y] > 'z' && str[y] != '~')) {!
str[y++] = '%';!
str[y++] = hexchars[(unsigned char) s[x] >> 4];!
str[y] = hexchars[(unsigned char) s[x] & 15];!
}!
}
rawurlencode() の実装
(EBCDIC モード時の処理は省略)
for ループで文字列終端まで 1 文字
ずつ走査
エンコード対象の文字ならパーセントエンコード
とりあえず str に文字を格納
(つд⊂)ゴシゴシゴシ
(;゚Д゚)	
 共通化されてねぇ……!
なぜ空白を + に置き換えるか
技術/HTTP/URLエンコードで 0x20(スペース) を "+" にすべきか "%20" にすべきか - Glamenv-Septzen.net

http://www.glamenv-septzen.net/view/1170

※PHP に関する記述 (rawurlencode() が用意された経緯など) は若干事実と異なる部分がある。本スライドで詳述
なぜ空白を + に置き換えるか
• application/x-www-form-urlencoded のため
• W3C の規格 (たとえば HTML 5) などに含まれる (単独の
規格は存在しない)
• form が submit された場合のレスポンスボディのエンコー
ド方式
• 空白を + に置き換えるほかはだいたいパーセントエンコード
他の言語の状況
• Python 2 (Python 3 では urllib.parse)
• RFC 3986 の URL エンコード: urllib.quote()
• application/x-www-form-urlencoded: urllib.quote_plus()
• Ruby
• RFC 3986 の URL エンコード: ERB::Util.u(), URI.encode()
• application/x-www-form-urlencoded: URI.encode_www_form(),
CGI.escape()
• ただし URI.encode() は obsolete で、 ERB::Util.u() とかが代替となっている模
様
urlencode() の歴史
• PHP/FI 2.0 から存在
• この当時から空白 (U+0020) を + (U+003B) に置き
換える実装になっていた
char *php_urlencode(char *s) {!
********************** SNIP *********************!
for(x=0,y=0; s[x]; x++,y++) {!
str[y] = s[x];!
if(str[y]==' ') {!
str[y]='+';
rawurlencode() の歴史
• PHP 3.0b3 から存在 (当時は RFC 1738 ベース)
• まーた PHP 3.0 か!
• RFC 3986 は 2005 年 1 月
• PHP 3.0 は 1999 年 (PHP 3.0b3 は 1998 年)
• PHP 5.0 のタイミングで RFC 3986 ベースに変更
rawurlencode() 誕生秘話
(序章)
• 1997/06/16 (PHP/FI 2.0b12)
• UrlEncode() がスペースを + に置換するようになる
• 1997/11/12 (PHP/FI 2.0)
• UrlEncode() が / をエンコードしないようになる (後に撤
回)
• URL 文字列全体のエンコードを意図した
議論のスレッドは http://marc.info/?t=90279138700001&r=1&w=4 http://marc.info/?t=90279138700002&r=1&w=2
rawurlencode() 誕生秘話
(事件篇)
• 1998/01/15 頃 (PHP 3.0b3 開発中)
• Jaakko Hyvätti が urlencode() で & をエンコードしないように変
更? (意図の説明を user ML にポストしたようだが入手できず)
• PHP/FI 2.0 での変更の意図に合わせたもの
• おそらくここで rawurlencode() と formencode() (おそらくリリー
ス前に削除) が入ったと思われる ([PHP-DEV] ML で my
rawurlencode() などと説明しているので)
• PHP 3.0b3-dev の urlencode() が壊れたと報告がくる
議論のスレッドは http://marc.info/?t=90279138700001&r=1&w=4 http://marc.info/?t=90279138700002&r=1&w=2
rawurlencode() 誕生秘話
(解決篇)
• 1998/01/16
• PHP 2.0 の変更が不適切ということになり、スペースを + に置換する版まで戻される
• ちなみに Jaakko は「rawurl*code() っていい名前ない?�urlpath*code() とか?」
とも言ってるが Rasmus 華麗にこれをスルー
• スペースを + にする件も戻した方がいいのでは、という Jaakko の提案に Rasmus
は反対
• 「スペースが + になっていれば urldecode() なしで POST data が使える」、
「URL 中の + は自動的にスペースにデコードされる」(= つまり + なら URL と
POST data どっちもいける)
• 既存のコードが壊れる
議論のスレッドは http://marc.info/?t=90279138700001&r=1&w=4 http://marc.info/?t=90279138700002&r=1&w=2
urlencode() と rawurlencode()
についてのまとめ
• URL 中の文字列のエンコードをおこなう際は、
rawurlencode() を使ったほうがよい
• urlencode() を使うべき場面は滅多にない
• 名前的に urlencode() の方が正しそうなので多用されがち
だが……
• 自力で POST データのエンコードをしたい場合くらい
• そもそも RFC�3986 にも追従していない
問題のあった事例
• Bug(バグ) #3383: mail_to 関数を用いるときに空白が
+ に変換されてしまう - OpenPNE 3

https://redmine.openpne.jp/issues/3383
• ここで気がついた (symfony の link_to() は
urlencode() を使っている)
• 空白を意図して渡していても + をスペースに展開しない
メーラがあったと思われる
DOUBLE 型と FLOAT 型
PHP における浮動小数点数
• 精度は環境依存だが、通常 IEEE 754 の double (倍精度
浮動小数点数)
• PHP では float 型ということで統一されている
• 型キャストの際に (double) や (real) しても float にな
る
浮動小数点数の (?)
歴史的な理由
• 「double は float と同じものだと考えてください。 2 種類
の名前が存在するのは、歴史的な理由によるものです」
float / double 型の歴史
• PHP/FI 2.0 (1997/11/12) : double
• PHP 3.0 (1998/06/06) : double (float, real でもキャスト
できるように)
• PHP 4.1.0 (2001/12/10): float……?
• 2c275bf793f70ad2a38bbf4a0f7ad12fecaca095,
03f7406711d3706af0f237e1ea03974616dd2139
など
なんで double -> float に
なったか
• 不明……
• ML では議論されてない?
• 突如として float 派が出現したように見える [要出典]
• 無理に double を float に置換する必要があったのかどう
か疑問
float 派の闘いの記録
• Hartmut Holzgraefe
• double -> float への置換をおこなった最初の人物
• 多くの double -> float の置換に貢献
• Jeroen van Wolffelaar
• ドキュメントに存在するほとんどの double を float に置換した
• Gabor Hojtsy
float 派の登場
• Hartmut Holzgraefe による 2001/09/21 のコミット
(2c275bf7) で、 floatval(), is_float() のエイリアスとし
て doubleval(), is_double() を使うように変更 (それまで
は逆)
• さらに (03f74067) で関数定義部分のコメント (返り値や
引数型などが書かれている) の double を float に置換
float 派の登場
新たな double の出現
float 派の反撃
php has no ‘double’, only ’float’
しかし止まらない double の追加
そして 1 年が経ったある日
“php has no ‘double’” とはなんだったのか
ドキュメントの置換も忘れない
• r53773 (2001/08/07) by Jeroen van Wolffelaar
• r54456 (2001/08/12) by Jeroen van Wolffelaar
• r54918 (2001/08/14) by Jeroen van Wolffelaar
• r57972 (2001/09/21) by Hartmut Holzgraefe
• r57997 (2001/09/21) by Jeroen van Wolffelaar
• r57999 (2001/09/21) by Jeroen van Wolffelaar
ぜんぶ歴史のせいだ。
未だ残る double の痕跡
• 「歴史的な理由により、 float の場合には “double” が返
されます。 “float” とはなりません」
Phar アーカイブのマニフェスト情報
Phar アーカイブのマニフェスト
情報における歴史的な理由
• 「Phar マニフェストは高度に最適化された書式で (略) 1
バイトをこえる大きさの値はリトルエンディアン形式のバイト
順で保存されます。ただし API バージョンだけは例外です。
これは 3 ニブルのデータですが、歴史的な理由によりビッ
グエンディアン形式のバイト順で保存されます」
Phar アーカイブの構造
スタブ
マニフェスト
コンテンツ
シグネチャ (optional)
Phar アーカイブの構造
スタブ
マニフェスト
コンテンツ
シグネチャ (optional)
Phar アーカイブの起動時に実行される
PHP スクリプト。
__HALT_COMPILER(); で終了
Phar アーカイブの構造
スタブ
マニフェスト
コンテンツ
シグネチャ (optional)
Phar アーカイブの起動時に実行される
PHP スクリプト。
__HALT_COMPILER(); で終了
バージョン情報などのメタ情報
Phar アーカイブの構造
スタブ
マニフェスト
コンテンツ
シグネチャ (optional)
Phar アーカイブの起動時に実行される
PHP スクリプト。
__HALT_COMPILER(); で終了
バージョン情報などのメタ情報
アーカイブの内容
Phar アーカイブの構造
スタブ
マニフェスト
コンテンツ
シグネチャ (optional)
Phar アーカイブの起動時に実行される
PHP スクリプト。
__HALT_COMPILER(); で終了
バージョン情報などのメタ情報
アーカイブの内容
パッケージ検証用のシグネチャ。
Phar 形式のみ
Phar アーカイブのマニフェスト
マニフェストの長さ
(Little Endian)
格納するファイルの数
(Little Endian)
ビットマップフラグ
(Little Endian)
API バージョン
(Big Endian)
エイリアスの長さ
(Little Endian)
エイリアス
(※任意桁)
…
メタデータの長さ
(Little Endian)
メタデータ
(※任意桁)
…
ファイルのリスト
(※任意桁)
…
Phar アーカイブのマニフェスト
マニフェストの長さ
(Little Endian)
格納するファイルの数
(Little Endian)
ビットマップフラグ
(Little Endian)
API バージョン
(Big Endian)
エイリアスの長さ
(Little Endian)
エイリアス
(※任意桁)
…
メタデータの長さ
(Little Endian)
メタデータ
(※任意桁)
…
ファイルのリスト
(※任意桁)
…
ニブル単位でバージョンを表現
Phar アーカイブのマニフェスト
マニフェストの長さ
(Little Endian)
格納するファイルの数
(Little Endian)
ビットマップフラグ
(Little Endian)
API バージョン
(Big Endian)
エイリアスの長さ
(Little Endian)
エイリアス
(※任意桁)
…
メタデータの長さ
(Little Endian)
メタデータ
(※任意桁)
…
ファイルのリスト
(※任意桁)
…
ニブル単位でバージョンを表現 検証用のシグネチャが含まれて
いるか、圧縮されたファイルが存
在するかなどのフラグ
Phar アーカイブのマニフェスト
(composer.phar (1.0.0-alpha8) の例)
0x17F-82 : マニフェストの長さ (26489)
0x183-86 : ファイル数 (322)
0x187-88: API バージョン (1.1.0)
※最後の 4bit は未使用
0x189-8C : ビットマップフラグ
(0x00010000 : この Phar には検証用シグネチャが含まれる)
0x18D-90 : この Phar のエイリアスの長さ (13)
0x191-9D : エイリアス (composer.phar)
バージョン情報のみ
ビッグエンディアンである理由
• 3842b67 には元になった PHP_Archive に由来する理由とある
• PHP_Archive の最新の実装もバージョン情報だけビッグエンディアンで格納している
(PHP_Archive_Creator::serializeManifest())
• PHP_Archive の 2f41f8f48 ではアーカイブ作成時にビッグエンディアンで格納
しているが、展開時にはリトルエンディアンでパースしようとしている
• PHP_Archive の 8931abf6 で展開時にもビッグエンディアンでパースするよう修
正された
• つまり、間違えてビッグエンディアンで格納してしまったために後に引けなくなった
のでは……
ZEND ENGINE の HASHTABLE
Zend Engine の HashTable
API における歴史的な理由
• 「hash exists for historical reasons and is always
ignored」

(拙訳: 引数 hash は歴史的な理由のために存在し、常に
無視される)
Zend Engine の HashTable
API における歴史的な理由
• 時間切れで追い切れず
• まあなんとなくわかる
まとめ
• 身近な機能とかを深追いしていくのは結構楽しい
• PHP 3.0 時代のチェンジログと ML は本当にオススメ
• たとえば「昔の PHP は + 演算子で文字列結合できたんだよー」と
か無駄知識を披露してドヤ顔できる
• 5 個がっつり深追いしていくだけでも結構時間が埋まるので助かった
• Phar とか Zend Engine 周りの歴史的な理由が出てきたおかげで割
と闇 PHP っぽくなった気がする

More Related Content

What's hot

Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
Masahito Zembutsu
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!
kwatch
 
ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開
Yahoo!デベロッパーネットワーク
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
Kohei Tokunaga
 
コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」
Masahito Zembutsu
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
yoku0825
 
Docker Compose 徹底解説
Docker Compose 徹底解説Docker Compose 徹底解説
Docker Compose 徹底解説
Masahito Zembutsu
 
Apache Avro vs Protocol Buffers
Apache Avro vs Protocol BuffersApache Avro vs Protocol Buffers
Apache Avro vs Protocol Buffers
Seiya Mizuno
 
ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方
Yoshiyasu SAEKI
 
Glibc malloc internal
Glibc malloc internalGlibc malloc internal
Glibc malloc internal
Motohiro KOSAKI
 
「速」を落とさないコードレビュー
「速」を落とさないコードレビュー「速」を落とさないコードレビュー
「速」を落とさないコードレビュー
Takafumi ONAKA
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
Kota Saito
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
mosa siru
 
Apache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォームApache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォーム
Kouhei Sutou
 
グラフデータベース Neptune 使ってみた
グラフデータベース Neptune 使ってみたグラフデータベース Neptune 使ってみた
グラフデータベース Neptune 使ってみた
Yoshiyasu SAEKI
 
MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法
Tetsutaro Watanabe
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Yoshifumi Kawai
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
Takuto Wada
 
グルーミングしながら進めるプロダクト開発
グルーミングしながら進めるプロダクト開発グルーミングしながら進めるプロダクト開発
グルーミングしながら進めるプロダクト開発
Takafumi ONAKA
 
ゼロから始めるQ#
ゼロから始めるQ#ゼロから始めるQ#
ゼロから始めるQ#
Takayoshi Tanaka
 

What's hot (20)

Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!
 
ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
Docker Compose 徹底解説
Docker Compose 徹底解説Docker Compose 徹底解説
Docker Compose 徹底解説
 
Apache Avro vs Protocol Buffers
Apache Avro vs Protocol BuffersApache Avro vs Protocol Buffers
Apache Avro vs Protocol Buffers
 
ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方
 
Glibc malloc internal
Glibc malloc internalGlibc malloc internal
Glibc malloc internal
 
「速」を落とさないコードレビュー
「速」を落とさないコードレビュー「速」を落とさないコードレビュー
「速」を落とさないコードレビュー
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
Apache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォームApache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォーム
 
グラフデータベース Neptune 使ってみた
グラフデータベース Neptune 使ってみたグラフデータベース Neptune 使ってみた
グラフデータベース Neptune 使ってみた
 
MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
グルーミングしながら進めるプロダクト開発
グルーミングしながら進めるプロダクト開発グルーミングしながら進めるプロダクト開発
グルーミングしながら進めるプロダクト開発
 
ゼロから始めるQ#
ゼロから始めるQ#ゼロから始めるQ#
ゼロから始めるQ#
 

Similar to お前は PHP の歴史的な理由の数を覚えているのか

URLで遊ぼう
URLで遊ぼうURLで遊ぼう
URLで遊ぼう
Hiraku Nakano
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
yak1ex
 
PBL1-v1-006j.pptx
PBL1-v1-006j.pptxPBL1-v1-006j.pptx
PBL1-v1-006j.pptx
NAIST
 
PBL1-v1-007j.pptx
PBL1-v1-007j.pptxPBL1-v1-007j.pptx
PBL1-v1-007j.pptx
NAIST
 
2008.10.18 L4u Tech Talk
2008.10.18 L4u Tech Talk2008.10.18 L4u Tech Talk
2008.10.18 L4u Tech Talkmitamex4u
 
CUDAを利用したPIV解析の高速化
CUDAを利用したPIV解析の高速化CUDAを利用したPIV解析の高速化
CUDAを利用したPIV解析の高速化
翔新 史
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
natrium11321
 
Write good parser in perl
Write good parser in perlWrite good parser in perl
Write good parser in perlJiro Nishiguchi
 
HaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングHaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングKiwamu Okabe
 
PBL1-v1-003j.pptx
PBL1-v1-003j.pptxPBL1-v1-003j.pptx
PBL1-v1-003j.pptx
NAIST
 
PBL1-v1-008j.pptx
PBL1-v1-008j.pptxPBL1-v1-008j.pptx
PBL1-v1-008j.pptx
NAIST
 
Brief introduction of Boost.ICL
Brief introduction of Boost.ICLBrief introduction of Boost.ICL
Brief introduction of Boost.ICL
yak1ex
 
x86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTx86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNT
takesako
 
知って得するC#
知って得するC#知って得するC#
知って得するC#Shota Baba
 
拡張ライブラリ作成による高速化
拡張ライブラリ作成による高速化拡張ライブラリ作成による高速化
拡張ライブラリ作成による高速化
Kazunori Jo
 
ScalableCore system at SWoPP2010 BoF-2
ScalableCore system at SWoPP2010 BoF-2ScalableCore system at SWoPP2010 BoF-2
ScalableCore system at SWoPP2010 BoF-2Shinya Takamaeda-Y
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
yak1ex
 
Polyphony の行く末(2018/3/3)
Polyphony の行く末(2018/3/3)Polyphony の行く末(2018/3/3)
Polyphony の行く末(2018/3/3)
ryos36
 
HPC Phys-20201203
HPC Phys-20201203HPC Phys-20201203
HPC Phys-20201203
MITSUNARI Shigeo
 

Similar to お前は PHP の歴史的な理由の数を覚えているのか (20)

URLで遊ぼう
URLで遊ぼうURLで遊ぼう
URLで遊ぼう
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
 
PBL1-v1-006j.pptx
PBL1-v1-006j.pptxPBL1-v1-006j.pptx
PBL1-v1-006j.pptx
 
PBL1-v1-007j.pptx
PBL1-v1-007j.pptxPBL1-v1-007j.pptx
PBL1-v1-007j.pptx
 
2008.10.18 L4u Tech Talk
2008.10.18 L4u Tech Talk2008.10.18 L4u Tech Talk
2008.10.18 L4u Tech Talk
 
CUDAを利用したPIV解析の高速化
CUDAを利用したPIV解析の高速化CUDAを利用したPIV解析の高速化
CUDAを利用したPIV解析の高速化
 
ILE-RPG Study 001
ILE-RPG Study 001ILE-RPG Study 001
ILE-RPG Study 001
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
 
Write good parser in perl
Write good parser in perlWrite good parser in perl
Write good parser in perl
 
HaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングHaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミング
 
PBL1-v1-003j.pptx
PBL1-v1-003j.pptxPBL1-v1-003j.pptx
PBL1-v1-003j.pptx
 
PBL1-v1-008j.pptx
PBL1-v1-008j.pptxPBL1-v1-008j.pptx
PBL1-v1-008j.pptx
 
Brief introduction of Boost.ICL
Brief introduction of Boost.ICLBrief introduction of Boost.ICL
Brief introduction of Boost.ICL
 
x86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTx86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNT
 
知って得するC#
知って得するC#知って得するC#
知って得するC#
 
拡張ライブラリ作成による高速化
拡張ライブラリ作成による高速化拡張ライブラリ作成による高速化
拡張ライブラリ作成による高速化
 
ScalableCore system at SWoPP2010 BoF-2
ScalableCore system at SWoPP2010 BoF-2ScalableCore system at SWoPP2010 BoF-2
ScalableCore system at SWoPP2010 BoF-2
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
 
Polyphony の行く末(2018/3/3)
Polyphony の行く末(2018/3/3)Polyphony の行く末(2018/3/3)
Polyphony の行く末(2018/3/3)
 
HPC Phys-20201203
HPC Phys-20201203HPC Phys-20201203
HPC Phys-20201203
 

More from Kousuke Ebihara

XML と PHP のイケナイ関係 (セキュリティ的な意味で) -Introduction of XXE attack and XML Bomb with...
XML と PHP のイケナイ関係 (セキュリティ的な意味で) -Introduction of XXE attack and XML Bomb with...XML と PHP のイケナイ関係 (セキュリティ的な意味で) -Introduction of XXE attack and XML Bomb with...
XML と PHP のイケナイ関係 (セキュリティ的な意味で) -Introduction of XXE attack and XML Bomb with...
Kousuke Ebihara
 
Open pne3 with_symfony
Open pne3 with_symfonyOpen pne3 with_symfony
Open pne3 with_symfony
Kousuke Ebihara
 
Let's creating your own PHP (tejimaya version)
Let's creating your own PHP (tejimaya version)Let's creating your own PHP (tejimaya version)
Let's creating your own PHP (tejimaya version)
Kousuke Ebihara
 
Using Symfony Templating On Symfony 1
Using Symfony Templating On Symfony 1Using Symfony Templating On Symfony 1
Using Symfony Templating On Symfony 1Kousuke Ebihara
 
Introduction of symfony development process & What's symfony 1.3?
Introduction of symfony development process & What's symfony 1.3?Introduction of symfony development process & What's symfony 1.3?
Introduction of symfony development process & What's symfony 1.3?Kousuke Ebihara
 
OAuthで気持ちのいい アクセス制御を
OAuthで気持ちのいいアクセス制御をOAuthで気持ちのいいアクセス制御を
OAuthで気持ちのいい アクセス制御をKousuke Ebihara
 
Php5.3ってなんなんだー
Php5.3ってなんなんだーPhp5.3ってなんなんだー
Php5.3ってなんなんだーKousuke Ebihara
 
ルーティングを使って シンプルなアプリケーション開発を
ルーティングを使ってシンプルなアプリケーション開発をルーティングを使ってシンプルなアプリケーション開発を
ルーティングを使って シンプルなアプリケーション開発をKousuke Ebihara
 

More from Kousuke Ebihara (9)

XML と PHP のイケナイ関係 (セキュリティ的な意味で) -Introduction of XXE attack and XML Bomb with...
XML と PHP のイケナイ関係 (セキュリティ的な意味で) -Introduction of XXE attack and XML Bomb with...XML と PHP のイケナイ関係 (セキュリティ的な意味で) -Introduction of XXE attack and XML Bomb with...
XML と PHP のイケナイ関係 (セキュリティ的な意味で) -Introduction of XXE attack and XML Bomb with...
 
Open pne3 with_symfony
Open pne3 with_symfonyOpen pne3 with_symfony
Open pne3 with_symfony
 
Let's creating your own PHP (tejimaya version)
Let's creating your own PHP (tejimaya version)Let's creating your own PHP (tejimaya version)
Let's creating your own PHP (tejimaya version)
 
Hybrid Onboarding
Hybrid OnboardingHybrid Onboarding
Hybrid Onboarding
 
Using Symfony Templating On Symfony 1
Using Symfony Templating On Symfony 1Using Symfony Templating On Symfony 1
Using Symfony Templating On Symfony 1
 
Introduction of symfony development process & What's symfony 1.3?
Introduction of symfony development process & What's symfony 1.3?Introduction of symfony development process & What's symfony 1.3?
Introduction of symfony development process & What's symfony 1.3?
 
OAuthで気持ちのいい アクセス制御を
OAuthで気持ちのいいアクセス制御をOAuthで気持ちのいいアクセス制御を
OAuthで気持ちのいい アクセス制御を
 
Php5.3ってなんなんだー
Php5.3ってなんなんだーPhp5.3ってなんなんだー
Php5.3ってなんなんだー
 
ルーティングを使って シンプルなアプリケーション開発を
ルーティングを使ってシンプルなアプリケーション開発をルーティングを使ってシンプルなアプリケーション開発を
ルーティングを使って シンプルなアプリケーション開発を
 

Recently uploaded

【JSAI2024】LLMエージェントの人間との対話における反芻的返答の親近感向上効果_v1.1.pdf
【JSAI2024】LLMエージェントの人間との対話における反芻的返答の親近感向上効果_v1.1.pdf【JSAI2024】LLMエージェントの人間との対話における反芻的返答の親近感向上効果_v1.1.pdf
【JSAI2024】LLMエージェントの人間との対話における反芻的返答の親近感向上効果_v1.1.pdf
ARISE analytics
 
なぜそのDDDは効果が薄いのか?名ばかりDX案件での経験を踏まえて培った他の思考を交えた現代風?のDDD
なぜそのDDDは効果が薄いのか?名ばかりDX案件での経験を踏まえて培った他の思考を交えた現代風?のDDDなぜそのDDDは効果が薄いのか?名ばかりDX案件での経験を踏まえて培った他の思考を交えた現代風?のDDD
なぜそのDDDは効果が薄いのか?名ばかりDX案件での経験を踏まえて培った他の思考を交えた現代風?のDDD
ssuserfcafd1
 
協働AIがもたらす業務効率革命 -日本企業が押さえるべきポイント-Collaborative AI Revolutionizing Busines...
協働AIがもたらす業務効率革命 -日本企業が押さえるべきポイント-Collaborative AI Revolutionizing Busines...協働AIがもたらす業務効率革命 -日本企業が押さえるべきポイント-Collaborative AI Revolutionizing Busines...
協働AIがもたらす業務効率革命 -日本企業が押さえるべきポイント-Collaborative AI Revolutionizing Busines...
Osaka University
 
気ままなLLMをAgents for Amazon Bedrockでちょっとだけ飼いならす
気ままなLLMをAgents for Amazon Bedrockでちょっとだけ飼いならす気ままなLLMをAgents for Amazon Bedrockでちょっとだけ飼いならす
気ままなLLMをAgents for Amazon Bedrockでちょっとだけ飼いならす
Shinichi Hirauchi
 
20240621_AI事業者ガイドライン_セキュリティパートの紹介_SeiyaShimabukuro
20240621_AI事業者ガイドライン_セキュリティパートの紹介_SeiyaShimabukuro20240621_AI事業者ガイドライン_セキュリティパートの紹介_SeiyaShimabukuro
20240621_AI事業者ガイドライン_セキュリティパートの紹介_SeiyaShimabukuro
Seiya Shimabukuro
 
無形価値を守り育てる社会における「デー タ」の責務について - Atlas, Inc.
無形価値を守り育てる社会における「デー タ」の責務について - Atlas, Inc.無形価値を守り育てる社会における「デー タ」の責務について - Atlas, Inc.
無形価値を守り育てる社会における「デー タ」の責務について - Atlas, Inc.
Yuki Miyazaki
 
iMacwoSu_Gong_de_barabaranishitaHua_.pptx
iMacwoSu_Gong_de_barabaranishitaHua_.pptxiMacwoSu_Gong_de_barabaranishitaHua_.pptx
iMacwoSu_Gong_de_barabaranishitaHua_.pptx
kitamisetagayaxxx
 
生成AIの実利用に必要なこと-Practical Requirements for the Deployment of Generative AI
生成AIの実利用に必要なこと-Practical Requirements for the Deployment of Generative AI生成AIの実利用に必要なこと-Practical Requirements for the Deployment of Generative AI
生成AIの実利用に必要なこと-Practical Requirements for the Deployment of Generative AI
Osaka University
 
ろくに電子工作もしたことない人間がIoT用ミドルウェアを作った話(IoTLT vol112 発表資料)
ろくに電子工作もしたことない人間がIoT用ミドルウェアを作った話(IoTLT  vol112 発表資料)ろくに電子工作もしたことない人間がIoT用ミドルウェアを作った話(IoTLT  vol112 発表資料)
ろくに電子工作もしたことない人間がIoT用ミドルウェアを作った話(IoTLT vol112 発表資料)
Takuya Minagawa
 
実体験に基づく、成功するスクラム vs 失敗するスクラム 何が違う? 2024年6月22日
実体験に基づく、成功するスクラム vs 失敗するスクラム 何が違う? 2024年6月22日実体験に基づく、成功するスクラム vs 失敗するスクラム 何が違う? 2024年6月22日
実体験に基づく、成功するスクラム vs 失敗するスクラム 何が違う? 2024年6月22日
Hideo Kashioka
 
Microsoft Azureで生成AIを使ってみた話 2024/6/14の勉強会で発表されたものです。
Microsoft Azureで生成AIを使ってみた話 2024/6/14の勉強会で発表されたものです。Microsoft Azureで生成AIを使ってみた話 2024/6/14の勉強会で発表されたものです。
Microsoft Azureで生成AIを使ってみた話 2024/6/14の勉強会で発表されたものです。
iPride Co., Ltd.
 
Kotest を使って 快適にテストを書こう - KotlinFest 2024
Kotest を使って 快適にテストを書こう - KotlinFest 2024Kotest を使って 快適にテストを書こう - KotlinFest 2024
Kotest を使って 快適にテストを書こう - KotlinFest 2024
Hirotaka Kawata
 
ヒアラブルへの入力を想定したユーザ定義型ジェスチャ調査と IMUセンサによる耳タッチジェスチャの認識
ヒアラブルへの入力を想定したユーザ定義型ジェスチャ調査と IMUセンサによる耳タッチジェスチャの認識ヒアラブルへの入力を想定したユーザ定義型ジェスチャ調査と IMUセンサによる耳タッチジェスチャの認識
ヒアラブルへの入力を想定したユーザ定義型ジェスチャ調査と IMUセンサによる耳タッチジェスチャの認識
sugiuralab
 

Recently uploaded (13)

【JSAI2024】LLMエージェントの人間との対話における反芻的返答の親近感向上効果_v1.1.pdf
【JSAI2024】LLMエージェントの人間との対話における反芻的返答の親近感向上効果_v1.1.pdf【JSAI2024】LLMエージェントの人間との対話における反芻的返答の親近感向上効果_v1.1.pdf
【JSAI2024】LLMエージェントの人間との対話における反芻的返答の親近感向上効果_v1.1.pdf
 
なぜそのDDDは効果が薄いのか?名ばかりDX案件での経験を踏まえて培った他の思考を交えた現代風?のDDD
なぜそのDDDは効果が薄いのか?名ばかりDX案件での経験を踏まえて培った他の思考を交えた現代風?のDDDなぜそのDDDは効果が薄いのか?名ばかりDX案件での経験を踏まえて培った他の思考を交えた現代風?のDDD
なぜそのDDDは効果が薄いのか?名ばかりDX案件での経験を踏まえて培った他の思考を交えた現代風?のDDD
 
協働AIがもたらす業務効率革命 -日本企業が押さえるべきポイント-Collaborative AI Revolutionizing Busines...
協働AIがもたらす業務効率革命 -日本企業が押さえるべきポイント-Collaborative AI Revolutionizing Busines...協働AIがもたらす業務効率革命 -日本企業が押さえるべきポイント-Collaborative AI Revolutionizing Busines...
協働AIがもたらす業務効率革命 -日本企業が押さえるべきポイント-Collaborative AI Revolutionizing Busines...
 
気ままなLLMをAgents for Amazon Bedrockでちょっとだけ飼いならす
気ままなLLMをAgents for Amazon Bedrockでちょっとだけ飼いならす気ままなLLMをAgents for Amazon Bedrockでちょっとだけ飼いならす
気ままなLLMをAgents for Amazon Bedrockでちょっとだけ飼いならす
 
20240621_AI事業者ガイドライン_セキュリティパートの紹介_SeiyaShimabukuro
20240621_AI事業者ガイドライン_セキュリティパートの紹介_SeiyaShimabukuro20240621_AI事業者ガイドライン_セキュリティパートの紹介_SeiyaShimabukuro
20240621_AI事業者ガイドライン_セキュリティパートの紹介_SeiyaShimabukuro
 
無形価値を守り育てる社会における「デー タ」の責務について - Atlas, Inc.
無形価値を守り育てる社会における「デー タ」の責務について - Atlas, Inc.無形価値を守り育てる社会における「デー タ」の責務について - Atlas, Inc.
無形価値を守り育てる社会における「デー タ」の責務について - Atlas, Inc.
 
iMacwoSu_Gong_de_barabaranishitaHua_.pptx
iMacwoSu_Gong_de_barabaranishitaHua_.pptxiMacwoSu_Gong_de_barabaranishitaHua_.pptx
iMacwoSu_Gong_de_barabaranishitaHua_.pptx
 
生成AIの実利用に必要なこと-Practical Requirements for the Deployment of Generative AI
生成AIの実利用に必要なこと-Practical Requirements for the Deployment of Generative AI生成AIの実利用に必要なこと-Practical Requirements for the Deployment of Generative AI
生成AIの実利用に必要なこと-Practical Requirements for the Deployment of Generative AI
 
ろくに電子工作もしたことない人間がIoT用ミドルウェアを作った話(IoTLT vol112 発表資料)
ろくに電子工作もしたことない人間がIoT用ミドルウェアを作った話(IoTLT  vol112 発表資料)ろくに電子工作もしたことない人間がIoT用ミドルウェアを作った話(IoTLT  vol112 発表資料)
ろくに電子工作もしたことない人間がIoT用ミドルウェアを作った話(IoTLT vol112 発表資料)
 
実体験に基づく、成功するスクラム vs 失敗するスクラム 何が違う? 2024年6月22日
実体験に基づく、成功するスクラム vs 失敗するスクラム 何が違う? 2024年6月22日実体験に基づく、成功するスクラム vs 失敗するスクラム 何が違う? 2024年6月22日
実体験に基づく、成功するスクラム vs 失敗するスクラム 何が違う? 2024年6月22日
 
Microsoft Azureで生成AIを使ってみた話 2024/6/14の勉強会で発表されたものです。
Microsoft Azureで生成AIを使ってみた話 2024/6/14の勉強会で発表されたものです。Microsoft Azureで生成AIを使ってみた話 2024/6/14の勉強会で発表されたものです。
Microsoft Azureで生成AIを使ってみた話 2024/6/14の勉強会で発表されたものです。
 
Kotest を使って 快適にテストを書こう - KotlinFest 2024
Kotest を使って 快適にテストを書こう - KotlinFest 2024Kotest を使って 快適にテストを書こう - KotlinFest 2024
Kotest を使って 快適にテストを書こう - KotlinFest 2024
 
ヒアラブルへの入力を想定したユーザ定義型ジェスチャ調査と IMUセンサによる耳タッチジェスチャの認識
ヒアラブルへの入力を想定したユーザ定義型ジェスチャ調査と IMUセンサによる耳タッチジェスチャの認識ヒアラブルへの入力を想定したユーザ定義型ジェスチャ調査と IMUセンサによる耳タッチジェスチャの認識
ヒアラブルへの入力を想定したユーザ定義型ジェスチャ調査と IMUセンサによる耳タッチジェスチャの認識
 

お前は PHP の歴史的な理由の数を覚えているのか

  • 2.
  • 4. 答 5 個 _人人人人人人人人人人_ > 言うほどなかった <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄ ※ドキュメントから数えただけなので実体としてはもっとありそうですが、今回は考えません
  • 5. 自己紹介 • Kousuke Ebihara (海老原昂輔) a.k.a. @co3k • 株式会社 VOYAGE GROUP (2014/02 より) • スマホコミュケーション事業室で Python 書いてます • そういえば PHP 全然書いてないです • セキュリティ周り • 以前は某 OSS の SNS エンジンとかその辺やってました
  • 6. 歴史的な理由のある機能 • implode() • urlencode() / rawurlencode() • double 型 / float 型 と、 gettype() の返り値 • Phar アーカイブのマニフェスト情報 • Zend Engine の HashTable (間に合わず)
  • 7. 調査方法 • PHP 4 以降については普通に Git で潜っていく • php-src : 公式の Git リポジトリ • ドキュメント: git-svn で公式のリポジトリを変換 • PHP 3 以前 • museum.php.net (かなり重いので注意) • ML • 1996/07 - 1998/01: PHP/FI Mailing List • 1996/12 以降: php-internals (marc.info なら旧 php-dev 時代も追える)
  • 9. implode() の歴史的な理由 • 「implode() は、歴史的な理由により、引数をどちらの順番 でも受けつけることが可能です」 • おそらく PHP で一番有名な「歴史的な理由」
  • 10. implode() の実装 PHP_FUNCTION(implode)! {! ********************** SNIP *********************! if (arg2 == NULL) {! ********************** SNIP *********************! } else {! if (Z_TYPE_PP(arg1) == IS_ARRAY) {! arr = *arg1;! convert_to_string_ex(arg2);! delim = *arg2;! } else if (Z_TYPE_PP(arg2) == IS_ARRAY) {! arr = *arg2;! convert_to_string_ex(arg1);! delim = *arg1;! } else {! ********************** SNIP *********************! }! }! ! php_implode(delim, arr, return_value TSRMLS_CC);
  • 11. implode() の実装 PHP_FUNCTION(implode)! {! ********************** SNIP *********************! if (arg2 == NULL) {! ********************** SNIP *********************! } else {! if (Z_TYPE_PP(arg1) == IS_ARRAY) {! arr = *arg1;! convert_to_string_ex(arg2);! delim = *arg2;! } else if (Z_TYPE_PP(arg2) == IS_ARRAY) {! arr = *arg2;! convert_to_string_ex(arg1);! delim = *arg1;! } else {! ********************** SNIP *********************! }! }! ! php_implode(delim, arr, return_value TSRMLS_CC); 第 2 引数が指定されている
  • 12. implode() の実装 PHP_FUNCTION(implode)! {! ********************** SNIP *********************! if (arg2 == NULL) {! ********************** SNIP *********************! } else {! if (Z_TYPE_PP(arg1) == IS_ARRAY) {! arr = *arg1;! convert_to_string_ex(arg2);! delim = *arg2;! } else if (Z_TYPE_PP(arg2) == IS_ARRAY) {! arr = *arg2;! convert_to_string_ex(arg1);! delim = *arg1;! } else {! ********************** SNIP *********************! }! }! ! php_implode(delim, arr, return_value TSRMLS_CC); 第 2 引数が指定されている 配列が第 1 引数に指定され ていればデリミタは第 2 引数
  • 13. implode() の実装 PHP_FUNCTION(implode)! {! ********************** SNIP *********************! if (arg2 == NULL) {! ********************** SNIP *********************! } else {! if (Z_TYPE_PP(arg1) == IS_ARRAY) {! arr = *arg1;! convert_to_string_ex(arg2);! delim = *arg2;! } else if (Z_TYPE_PP(arg2) == IS_ARRAY) {! arr = *arg2;! convert_to_string_ex(arg1);! delim = *arg1;! } else {! ********************** SNIP *********************! }! }! ! php_implode(delim, arr, return_value TSRMLS_CC); 第 2 引数が指定されている 配列が第 1 引数に指定され ていればデリミタは第 2 引数 配列が第 2 引数に指定され ていればデリミタは第 1 引数
  • 14. implode() の実装 PHP_FUNCTION(implode)! {! ********************** SNIP *********************! if (arg2 == NULL) {! ********************** SNIP *********************! } else {! if (Z_TYPE_PP(arg1) == IS_ARRAY) {! arr = *arg1;! convert_to_string_ex(arg2);! delim = *arg2;! } else if (Z_TYPE_PP(arg2) == IS_ARRAY) {! arr = *arg2;! convert_to_string_ex(arg1);! delim = *arg1;! } else {! ********************** SNIP *********************! }! }! ! php_implode(delim, arr, return_value TSRMLS_CC); 第 2 引数が指定されている 配列が第 1 引数に指定され ていればデリミタは第 2 引数 配列が第 2 引数に指定され ていればデリミタは第 1 引数 配列が指定されていなければ エラー
  • 15. implode() の歴史 • PHP/FI 2 には implode() も explode() も存在しない • PHP 3 にはある • PHP 3 時点では implode() は現在と同じ挙動に • つまりこの「歴史的な理由」は PHP/FI 2 → PHP 3 の開 発中に生まれたものと思われる
  • 16. PHP 3.0a3 (November 23 1997) • Switched between the 1st and 2nd parameters to explode(), so that it acts like split()
 (拙訳: explode() の第 1 引数と第 2 引数を交換したの で、 split() と同じように動作するようになりました)
  • 18. split() と explode() 元々は逆 (implode() が歴史的な理由により受け付ける順序と同じ�)
  • 19. PHP 3.0b5 (February 24 1998) • Made implode() accept arguments in the order used by explode() as well
 (拙訳: implode() が explode() で使われているような引 数順も受け付けるようにしました)
  • 20. explode() と implode() に 何が起こったか • PHP 3.0 開発中に explode(), implode(), split() が追加された • このタイミングで追加、変更された機能は多いので当時の CHANGELOG を眺めているだけでも結構楽しい • PHP 3.0a3 にて、 explode() の引数順を split() に合わせた • この結果、 implode() との統一性が取れなくなったので、PHP 3.0b5 にて、 implode() では両方の引数順を受け付けるようにした • explode() が据え置きだったのは、引数が両方とも文字列型だから? • わずか 3 ヶ月の「歴史」
  • 22. URL エンコード用の 2 つの関数 • urlencode() • 文字列を URL エンコード • rawurlencode() • 文字列を URL エンコード
  • 23. URL エンコード用の 2 つの関数 • urlencode() • 文字列を URL エンコード • rawurlencode() • 文字列を URL エンコード (RFC 3986 に基づかない)
  • 24. URL エンコード用の 2 つの関数 • urlencode() • 文字列を URL エンコード • rawurlencode() • 文字列を URL エンコード (RFC 3986 に基づかない) (RFC 3986 に基づく)
  • 25. どのような違いがあるか? • urlencode() • 空白 (U+0020) を + (U+003B) に置き換える • ~ (U+007E) をエンコードする (RFC 1738 に基づく) • rawurlencode() • 空白 (U+0020) をパーセントエンコードする • ~ (U+007E) をエンコードしない (RFC 3986 に基づく)
  • 26. どのような違いがあるか? • urlencode() • 空白 (U+0020) を + (U+003B) に置き換える • ~ (U+007E) をエンコードする (RFC 1738 に基づく) • rawurlencode() • 空白 (U+0020) をパーセントエンコードする • ~ (U+007E) をエンコードしない (RFC 3986 に基づく) 追従漏れじゃね……?
  • 27. urlencode() の歴史的な理由 • 「歴史的な理由により、この関数は RFC 3986 エンコード (rawurlencode() を参照してください) とは異なり、空白を + 記号にエンコードします」
  • 28. urlencode() の実装 (EBCDIC モード時の処理は省略) PHPAPI char *php_url_encode(char const *s, int len, int *new_length)! {! ********************** SNIP *********************! while (from < end) {! c = *from++;! ! if (c == ' ') {! *to++ = '+';! } else if ((c < '0' && c != '-' && c != '.') ||! (c < 'A' && c > '9') ||! (c > 'Z' && c < 'a' && c != '_') ||! (c > 'z')) {! to[0] = '%';! to[1] = hexchars[c >> 4];! to[2] = hexchars[c & 15];! to += 3;! } else {! *to++ = c;! }! }
  • 29. urlencode() の実装 (EBCDIC モード時の処理は省略) PHPAPI char *php_url_encode(char const *s, int len, int *new_length)! {! ********************** SNIP *********************! while (from < end) {! c = *from++;! ! if (c == ' ') {! *to++ = '+';! } else if ((c < '0' && c != '-' && c != '.') ||! (c < 'A' && c > '9') ||! (c > 'Z' && c < 'a' && c != '_') ||! (c > 'z')) {! to[0] = '%';! to[1] = hexchars[c >> 4];! to[2] = hexchars[c & 15];! to += 3;! } else {! *to++ = c;! }! } while ループで文字列終端まで from を 1 文字ずつ走査
  • 30. urlencode() の実装 (EBCDIC モード時の処理は省略) PHPAPI char *php_url_encode(char const *s, int len, int *new_length)! {! ********************** SNIP *********************! while (from < end) {! c = *from++;! ! if (c == ' ') {! *to++ = '+';! } else if ((c < '0' && c != '-' && c != '.') ||! (c < 'A' && c > '9') ||! (c > 'Z' && c < 'a' && c != '_') ||! (c > 'z')) {! to[0] = '%';! to[1] = hexchars[c >> 4];! to[2] = hexchars[c & 15];! to += 3;! } else {! *to++ = c;! }! } while ループで文字列終端まで from を 1 文字ずつ走査 スペースを + に置換して to に格納
  • 31. urlencode() の実装 (EBCDIC モード時の処理は省略) PHPAPI char *php_url_encode(char const *s, int len, int *new_length)! {! ********************** SNIP *********************! while (from < end) {! c = *from++;! ! if (c == ' ') {! *to++ = '+';! } else if ((c < '0' && c != '-' && c != '.') ||! (c < 'A' && c > '9') ||! (c > 'Z' && c < 'a' && c != '_') ||! (c > 'z')) {! to[0] = '%';! to[1] = hexchars[c >> 4];! to[2] = hexchars[c & 15];! to += 3;! } else {! *to++ = c;! }! } while ループで文字列終端まで from を 1 文字ずつ走査 スペースを + に置換して to に格納 それ以外のエンコード対象の文字は パーセントエンコードして to に格納
  • 32. urlencode() の実装 (EBCDIC モード時の処理は省略) PHPAPI char *php_url_encode(char const *s, int len, int *new_length)! {! ********************** SNIP *********************! while (from < end) {! c = *from++;! ! if (c == ' ') {! *to++ = '+';! } else if ((c < '0' && c != '-' && c != '.') ||! (c < 'A' && c > '9') ||! (c > 'Z' && c < 'a' && c != '_') ||! (c > 'z')) {! to[0] = '%';! to[1] = hexchars[c >> 4];! to[2] = hexchars[c & 15];! to += 3;! } else {! *to++ = c;! }! } while ループで文字列終端まで from を 1 文字ずつ走査 スペースを + に置換して to に格納 それ以外のエンコード対象の文字は パーセントエンコードして to に格納 エンコードしない文字はそのまま to に格納
  • 33. PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)! {! register int x, y;! unsigned char *str;! ! str = (unsigned char *) safe_emalloc(3, len, 1);! for (x = 0, y = 0; len--; x++, y++) {! str[y] = (unsigned char) s[x];! if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||! (str[y] < 'A' && str[y] > '9') ||! (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||! (str[y] > 'z' && str[y] != '~')) {! str[y++] = '%';! str[y++] = hexchars[(unsigned char) s[x] >> 4];! str[y] = hexchars[(unsigned char) s[x] & 15];! }! } rawurlencode() の実装 (EBCDIC モード時の処理は省略)
  • 34. PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)! {! register int x, y;! unsigned char *str;! ! str = (unsigned char *) safe_emalloc(3, len, 1);! for (x = 0, y = 0; len--; x++, y++) {! str[y] = (unsigned char) s[x];! if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||! (str[y] < 'A' && str[y] > '9') ||! (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||! (str[y] > 'z' && str[y] != '~')) {! str[y++] = '%';! str[y++] = hexchars[(unsigned char) s[x] >> 4];! str[y] = hexchars[(unsigned char) s[x] & 15];! }! } rawurlencode() の実装 (EBCDIC モード時の処理は省略) for ループで文字列終端まで 1 文字 ずつ走査
  • 35. PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)! {! register int x, y;! unsigned char *str;! ! str = (unsigned char *) safe_emalloc(3, len, 1);! for (x = 0, y = 0; len--; x++, y++) {! str[y] = (unsigned char) s[x];! if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||! (str[y] < 'A' && str[y] > '9') ||! (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||! (str[y] > 'z' && str[y] != '~')) {! str[y++] = '%';! str[y++] = hexchars[(unsigned char) s[x] >> 4];! str[y] = hexchars[(unsigned char) s[x] & 15];! }! } rawurlencode() の実装 (EBCDIC モード時の処理は省略) for ループで文字列終端まで 1 文字 ずつ走査 とりあえず str に文字を格納
  • 36. PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)! {! register int x, y;! unsigned char *str;! ! str = (unsigned char *) safe_emalloc(3, len, 1);! for (x = 0, y = 0; len--; x++, y++) {! str[y] = (unsigned char) s[x];! if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||! (str[y] < 'A' && str[y] > '9') ||! (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||! (str[y] > 'z' && str[y] != '~')) {! str[y++] = '%';! str[y++] = hexchars[(unsigned char) s[x] >> 4];! str[y] = hexchars[(unsigned char) s[x] & 15];! }! } rawurlencode() の実装 (EBCDIC モード時の処理は省略) for ループで文字列終端まで 1 文字 ずつ走査 エンコード対象の文字ならパーセントエンコード とりあえず str に文字を格納
  • 39. なぜ空白を + に置き換えるか 技術/HTTP/URLエンコードで 0x20(スペース) を "+" にすべきか "%20" にすべきか - Glamenv-Septzen.net
 http://www.glamenv-septzen.net/view/1170
 ※PHP に関する記述 (rawurlencode() が用意された経緯など) は若干事実と異なる部分がある。本スライドで詳述
  • 40. なぜ空白を + に置き換えるか • application/x-www-form-urlencoded のため • W3C の規格 (たとえば HTML 5) などに含まれる (単独の 規格は存在しない) • form が submit された場合のレスポンスボディのエンコー ド方式 • 空白を + に置き換えるほかはだいたいパーセントエンコード
  • 41. 他の言語の状況 • Python 2 (Python 3 では urllib.parse) • RFC 3986 の URL エンコード: urllib.quote() • application/x-www-form-urlencoded: urllib.quote_plus() • Ruby • RFC 3986 の URL エンコード: ERB::Util.u(), URI.encode() • application/x-www-form-urlencoded: URI.encode_www_form(), CGI.escape() • ただし URI.encode() は obsolete で、 ERB::Util.u() とかが代替となっている模 様
  • 42. urlencode() の歴史 • PHP/FI 2.0 から存在 • この当時から空白 (U+0020) を + (U+003B) に置き 換える実装になっていた char *php_urlencode(char *s) {! ********************** SNIP *********************! for(x=0,y=0; s[x]; x++,y++) {! str[y] = s[x];! if(str[y]==' ') {! str[y]='+';
  • 43. rawurlencode() の歴史 • PHP 3.0b3 から存在 (当時は RFC 1738 ベース) • まーた PHP 3.0 か! • RFC 3986 は 2005 年 1 月 • PHP 3.0 は 1999 年 (PHP 3.0b3 は 1998 年) • PHP 5.0 のタイミングで RFC 3986 ベースに変更
  • 44. rawurlencode() 誕生秘話 (序章) • 1997/06/16 (PHP/FI 2.0b12) • UrlEncode() がスペースを + に置換するようになる • 1997/11/12 (PHP/FI 2.0) • UrlEncode() が / をエンコードしないようになる (後に撤 回) • URL 文字列全体のエンコードを意図した 議論のスレッドは http://marc.info/?t=90279138700001&r=1&w=4 http://marc.info/?t=90279138700002&r=1&w=2
  • 45. rawurlencode() 誕生秘話 (事件篇) • 1998/01/15 頃 (PHP 3.0b3 開発中) • Jaakko Hyvätti が urlencode() で & をエンコードしないように変 更? (意図の説明を user ML にポストしたようだが入手できず) • PHP/FI 2.0 での変更の意図に合わせたもの • おそらくここで rawurlencode() と formencode() (おそらくリリー ス前に削除) が入ったと思われる ([PHP-DEV] ML で my rawurlencode() などと説明しているので) • PHP 3.0b3-dev の urlencode() が壊れたと報告がくる 議論のスレッドは http://marc.info/?t=90279138700001&r=1&w=4 http://marc.info/?t=90279138700002&r=1&w=2
  • 46. rawurlencode() 誕生秘話 (解決篇) • 1998/01/16 • PHP 2.0 の変更が不適切ということになり、スペースを + に置換する版まで戻される • ちなみに Jaakko は「rawurl*code() っていい名前ない?�urlpath*code() とか?」 とも言ってるが Rasmus 華麗にこれをスルー • スペースを + にする件も戻した方がいいのでは、という Jaakko の提案に Rasmus は反対 • 「スペースが + になっていれば urldecode() なしで POST data が使える」、 「URL 中の + は自動的にスペースにデコードされる」(= つまり + なら URL と POST data どっちもいける) • 既存のコードが壊れる 議論のスレッドは http://marc.info/?t=90279138700001&r=1&w=4 http://marc.info/?t=90279138700002&r=1&w=2
  • 47. urlencode() と rawurlencode() についてのまとめ • URL 中の文字列のエンコードをおこなう際は、 rawurlencode() を使ったほうがよい • urlencode() を使うべき場面は滅多にない • 名前的に urlencode() の方が正しそうなので多用されがち だが…… • 自力で POST データのエンコードをしたい場合くらい • そもそも RFC�3986 にも追従していない
  • 48. 問題のあった事例 • Bug(バグ) #3383: mail_to 関数を用いるときに空白が + に変換されてしまう - OpenPNE 3
 https://redmine.openpne.jp/issues/3383 • ここで気がついた (symfony の link_to() は urlencode() を使っている) • 空白を意図して渡していても + をスペースに展開しない メーラがあったと思われる
  • 50. PHP における浮動小数点数 • 精度は環境依存だが、通常 IEEE 754 の double (倍精度 浮動小数点数) • PHP では float 型ということで統一されている • 型キャストの際に (double) や (real) しても float にな る
  • 51. 浮動小数点数の (?) 歴史的な理由 • 「double は float と同じものだと考えてください。 2 種類 の名前が存在するのは、歴史的な理由によるものです」
  • 52. float / double 型の歴史 • PHP/FI 2.0 (1997/11/12) : double • PHP 3.0 (1998/06/06) : double (float, real でもキャスト できるように) • PHP 4.1.0 (2001/12/10): float……? • 2c275bf793f70ad2a38bbf4a0f7ad12fecaca095, 03f7406711d3706af0f237e1ea03974616dd2139 など
  • 53. なんで double -> float に なったか • 不明…… • ML では議論されてない? • 突如として float 派が出現したように見える [要出典] • 無理に double を float に置換する必要があったのかどう か疑問
  • 54. float 派の闘いの記録 • Hartmut Holzgraefe • double -> float への置換をおこなった最初の人物 • 多くの double -> float の置換に貢献 • Jeroen van Wolffelaar • ドキュメントに存在するほとんどの double を float に置換した • Gabor Hojtsy
  • 55. float 派の登場 • Hartmut Holzgraefe による 2001/09/21 のコミット (2c275bf7) で、 floatval(), is_float() のエイリアスとし て doubleval(), is_double() を使うように変更 (それまで は逆) • さらに (03f74067) で関数定義部分のコメント (返り値や 引数型などが書かれている) の double を float に置換
  • 58. float 派の反撃 php has no ‘double’, only ’float’ しかし止まらない double の追加
  • 59. そして 1 年が経ったある日 “php has no ‘double’” とはなんだったのか
  • 60. ドキュメントの置換も忘れない • r53773 (2001/08/07) by Jeroen van Wolffelaar • r54456 (2001/08/12) by Jeroen van Wolffelaar • r54918 (2001/08/14) by Jeroen van Wolffelaar • r57972 (2001/09/21) by Hartmut Holzgraefe • r57997 (2001/09/21) by Jeroen van Wolffelaar • r57999 (2001/09/21) by Jeroen van Wolffelaar
  • 62. 未だ残る double の痕跡 • 「歴史的な理由により、 float の場合には “double” が返 されます。 “float” とはなりません」
  • 64. Phar アーカイブのマニフェスト 情報における歴史的な理由 • 「Phar マニフェストは高度に最適化された書式で (略) 1 バイトをこえる大きさの値はリトルエンディアン形式のバイト 順で保存されます。ただし API バージョンだけは例外です。 これは 3 ニブルのデータですが、歴史的な理由によりビッ グエンディアン形式のバイト順で保存されます」
  • 66. Phar アーカイブの構造 スタブ マニフェスト コンテンツ シグネチャ (optional) Phar アーカイブの起動時に実行される PHP スクリプト。 __HALT_COMPILER(); で終了
  • 67. Phar アーカイブの構造 スタブ マニフェスト コンテンツ シグネチャ (optional) Phar アーカイブの起動時に実行される PHP スクリプト。 __HALT_COMPILER(); で終了 バージョン情報などのメタ情報
  • 68. Phar アーカイブの構造 スタブ マニフェスト コンテンツ シグネチャ (optional) Phar アーカイブの起動時に実行される PHP スクリプト。 __HALT_COMPILER(); で終了 バージョン情報などのメタ情報 アーカイブの内容
  • 69. Phar アーカイブの構造 スタブ マニフェスト コンテンツ シグネチャ (optional) Phar アーカイブの起動時に実行される PHP スクリプト。 __HALT_COMPILER(); で終了 バージョン情報などのメタ情報 アーカイブの内容 パッケージ検証用のシグネチャ。 Phar 形式のみ
  • 70. Phar アーカイブのマニフェスト マニフェストの長さ (Little Endian) 格納するファイルの数 (Little Endian) ビットマップフラグ (Little Endian) API バージョン (Big Endian) エイリアスの長さ (Little Endian) エイリアス (※任意桁) … メタデータの長さ (Little Endian) メタデータ (※任意桁) … ファイルのリスト (※任意桁) …
  • 71. Phar アーカイブのマニフェスト マニフェストの長さ (Little Endian) 格納するファイルの数 (Little Endian) ビットマップフラグ (Little Endian) API バージョン (Big Endian) エイリアスの長さ (Little Endian) エイリアス (※任意桁) … メタデータの長さ (Little Endian) メタデータ (※任意桁) … ファイルのリスト (※任意桁) … ニブル単位でバージョンを表現
  • 72. Phar アーカイブのマニフェスト マニフェストの長さ (Little Endian) 格納するファイルの数 (Little Endian) ビットマップフラグ (Little Endian) API バージョン (Big Endian) エイリアスの長さ (Little Endian) エイリアス (※任意桁) … メタデータの長さ (Little Endian) メタデータ (※任意桁) … ファイルのリスト (※任意桁) … ニブル単位でバージョンを表現 検証用のシグネチャが含まれて いるか、圧縮されたファイルが存 在するかなどのフラグ
  • 73. Phar アーカイブのマニフェスト (composer.phar (1.0.0-alpha8) の例) 0x17F-82 : マニフェストの長さ (26489) 0x183-86 : ファイル数 (322) 0x187-88: API バージョン (1.1.0) ※最後の 4bit は未使用 0x189-8C : ビットマップフラグ (0x00010000 : この Phar には検証用シグネチャが含まれる) 0x18D-90 : この Phar のエイリアスの長さ (13) 0x191-9D : エイリアス (composer.phar)
  • 74. バージョン情報のみ ビッグエンディアンである理由 • 3842b67 には元になった PHP_Archive に由来する理由とある • PHP_Archive の最新の実装もバージョン情報だけビッグエンディアンで格納している (PHP_Archive_Creator::serializeManifest()) • PHP_Archive の 2f41f8f48 ではアーカイブ作成時にビッグエンディアンで格納 しているが、展開時にはリトルエンディアンでパースしようとしている • PHP_Archive の 8931abf6 で展開時にもビッグエンディアンでパースするよう修 正された • つまり、間違えてビッグエンディアンで格納してしまったために後に引けなくなった のでは……
  • 75. ZEND ENGINE の HASHTABLE
  • 76. Zend Engine の HashTable API における歴史的な理由 • 「hash exists for historical reasons and is always ignored」
 (拙訳: 引数 hash は歴史的な理由のために存在し、常に 無視される)
  • 77. Zend Engine の HashTable API における歴史的な理由 • 時間切れで追い切れず • まあなんとなくわかる
  • 78. まとめ • 身近な機能とかを深追いしていくのは結構楽しい • PHP 3.0 時代のチェンジログと ML は本当にオススメ • たとえば「昔の PHP は + 演算子で文字列結合できたんだよー」と か無駄知識を披露してドヤ顔できる • 5 個がっつり深追いしていくだけでも結構時間が埋まるので助かった • Phar とか Zend Engine 周りの歴史的な理由が出てきたおかげで割 と闇 PHP っぽくなった気がする