Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

PHP 5.3.9RC1 Released for Testing

リリースされました。NEWSファイルを見ると、かなりの量のバグFIXがなされているようです。
順調に行けば、2週間後にRC2が出てくるようです。


The first release candidates of 5.3.9 was just released for testing and can be downloaded here:

http://downloads.php.net/johannes/php-5.3.9RC1.tar.bz2 (md5sum: 5e8564008606edfab6a81137c1daf354)

The windows binaries are available at: http://windows.php.net/qa/

This is the first step in the release process of this versions and goal is having a 2nd RC two weeks from now. Majority of the changes are of the "bug fix" variety.

大きな修正点として、is_a関数の挙動が挙げられています。autoload機能を使ってクラスをロードしている場合への対応と思われますが、PHP5.3.7以降挙動が変更されていたのですが、PHP5.3.9RC1ではデフォルトの挙動を元に戻したようです。


One important change is for bug #55475 [1] where PHP 5.3.7 changed the behavior of is_a(). 5.3.9 will revert the behavior. Please ensure that the release is solid and all things behave as expected! Please test this RC against your code base and report any problems you encounter or successful tests you've run.

ここで「デフォルトの挙動」と書いたのは第3引数が追加されたためで、第1引数にクラス名を文字列で指定するかどうかを設定できるようになっています。Zend/zend_builtin_functions.cで以下のように定義されています。

static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool only_subclass)
{
    zval *obj;
    char *class_name;
    int class_name_len;
    zend_class_entry *instance_ce;
    zend_class_entry **ce;
    zend_bool allow_string = only_subclass;
    zend_bool retval;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|b", &obj, &class_name, &class_name_len, &allow_string) == FAILURE) {
        return;
    }
    /*
       allow_string - is_a default is no, is_subclass_of is yes. 
       if it's allowed, then the autoloader will be called if the class does not exist.
       default behaviour is different, as 'is_a' usage is normally to test mixed return values 
    */

    if (allow_string && Z_TYPE_P(obj) == IS_STRING) {
        zend_class_entry **the_ce;
        if (zend_lookup_class(Z_STRVAL_P(obj), Z_STRLEN_P(obj), &the_ce TSRMLS_CC) == FAILURE) {
            RETURN_FALSE;
        }
        instance_ce = *the_ce;
    } else if (Z_TYPE_P(obj) == IS_OBJECT && HAS_CLASS_ENTRY(*obj)) {
        instance_ce = Z_OBJCE_P(obj);
    } else {
        RETURN_FALSE;
    }

    if (zend_lookup_class_ex(class_name, class_name_len, 0, &ce TSRMLS_CC) == FAILURE) {
        retval = 0;
    } else {
        if (only_subclass && instance_ce == *ce) {
            retval = 0;
        } else {
            retval = instanceof_function(instance_ce, *ce TSRMLS_CC);
        }
    }

    RETURN_BOOL(retval);
}
        :
/* {{{ proto bool is_subclass_of(mixed object_or_string, string class_name [, bool allow_string=true])
   Returns true if the object has this class as one of its parents */
ZEND_FUNCTION(is_subclass_of)
{
    is_a_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */

実際にbuildして試してみました。まずA.class.phpとB.class.php

<?php
class A {}
<?php
class B extends A {}

test.phpはこんな感じ。

<?php
function __autoload($class_name) {
    echo "load class '{$class_name}'" . PHP_EOL;
    require_once "{$class_name}.class.php";
}

function output($target, $result) {
    echo 'B is ' . ($result ? '' : 'not ') . $target . PHP_EOL;
}

/**
 * $objはオブジェクト
 */
$obj = new B();
output('A', is_a($obj, 'A'));
output('A', is_a($obj, 'A', true));
output('A', is_a($obj, 'A', false));
output('B', is_a($obj, 'A'));
output('B', is_a($obj, 'A', true));
output('B', is_a($obj, 'A', false));

/**
 * $objは文字列(オブジェクトではない)
 */
$obj = 'B';
output('A', is_a($obj, 'A'));
output('A', is_a($obj, 'A', true));
output('A', is_a($obj, 'A', false));
output('B', is_a($obj, 'A'));
output('B', is_a($obj, 'A', true));
output('B', is_a($obj, 'A', false));

これを実行すると、次のようになりました。

$ ./sapi/cli/php test.php
load class 'B'
load class 'A'
B is A
B is A
B is A
B is B
B is B
B is B
B is not A
B is A
B is not A
B is not B
B is B
B is not B
$ 

第1引数がオブジェクトの場合は特に問題ありませんが、クラス名を文字列で指定し、かつ、第3引数の値によって挙動が変わることが確認できました。

フレームワークやライブラリを作ってる人は注意が必要かもしれません。