PHP 7.0.3で興味あったバグ トレイトでのメソッド名のバグ
TOP > てきとうにこらむ > ゲーム作りとプログラミング日記 > PHP 7.0.3で興味あったバグ トレイトでのメソッド名のバグ
恒例行事
mt_randでPHP界隈は騒がしいが、やはりPHP 7.0.3のChangelogを読んでもなんかおかしい。
mt_randについて
余談になるけど。結局いつmergeされるんでしょうか。
- PHPのmt_rand()についてプルリクを送った
- PHP の mt_rand() は一貫して壊れている(consistently broken)らしい
- PHP の壊れた mt_rand の品質を統計的に検証した
- 壊れてても修正されたほうでもあんまり変わらないらしい。
修正されたバグについて
いろいろありますが、興味深かったバグをピックアップ。
#71275 Bad method called on cloning an object having a trait
https://bugs.php.net/bug.php?id=71275
まーたtrait周りのバグかよーってくらいtraitのバグ多いなーという印象。
<?php
trait MyTrait {
public function _() {
throw new RuntimeException('Should not be called');
}
}
class MyClass {
use MyTrait;
public function __clone() {
echo 'I\'m working hard to clone';
}
}
$instance = new MyClass();
clone $instance;
そして実行した結果。まずはPHP 7.0.3。
# php test71275.php
I'm working hard to clone
PHP 7.0.1。
# php test71275.php
Fatal error: Uncaught RuntimeException: Should not be called in /tmp/test71275.php:5
Stack trace:
#0 /tmp/test71275.php(20): MyClass->_()
#1 {main}
thrown in /tmp/test71275.php on line 5
!?!?!?!?!?
diffを読んでみた。
古い方はこうなってる。
!strncmp(ZSTR_VAL(mname), ZEND_CLONE_FUNC_NAME, ZSTR_LEN(mname))
なるほど。ということは、mnameがZEND_CLONE_FUNC_NAMEとの比較がしたいのに、mnameの文字列長しか比較していなかったと。すなわち、mnameが「_」のとき、「__clone」との比較がしたいのに一文字しかないから「_」との比較になった結果、「_」がマッチしちゃうというわけか。以下の修正では、厳密に比較している。
zend_string_equals_literal(mname, ZEND_CLONE_FUNC_NAME)
この、zend_string_equals_literalは、Zend/zend_string.hにてプリプロセッサで定義されている。文字列長の数値とリテラルとの長さが同じかつ、メモリの中にあるsizeof(literal)-1バイト分同じですよということで正しく評価される。
#define zend_string_equals_literal(str, literal) \
(ZSTR_LEN(str) == sizeof(literal)-1 && !memcmp(ZSTR_VAL(str), literal, sizeof(literal) - 1))
でも、traitだけなんだ?あれ?と思って継承とか使ってみたけど再現できんかった。実装が違うからそうなるのはしかたがないか。