Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPの緩やかな比較の実態

ゆい
April 11, 2022

 PHPの緩やかな比較の実態

ゆい

April 11, 2022
Tweet

More Decks by ゆい

Other Decks in Programming

Transcript

  1. Copyright© M&AΫϥ΢υ
    PHPͷ؇΍͔ͳൺֱͷ࣮ଶ
    PHPerKaigi 2022

    View Slide

  2. Copyright© M&AΫϥ΢υ 2
    Profile
    גࣜձࣾM&AΫϥ΢υ
    ᅳଜʢΏ͍ʣ
    fyui001
    @fyui_001

    View Slide

  3. Copyright© M&AΫϥ΢υ 3
    ֓ཁ
    PHPʹ͸౳Ձԋࢉࢠ͕2ͭ͋Γ·͢ɻʮ==ʯͱʮ===ʯͰ͢ɻ
    લऀ͸؇΍͔ͳൺֱɺޙऀ͸ݫີͳൺֱ͕ߦΘΕ·͢ɻ
    PHPerͷํͳΒΑ͘͝ଘ஌ͩͱࢥ͍·͢ɻ
    ࠓճ͸લऀͷ؇΍͔ͳൺֱΛ࣮ଶ͕Ͳ͏ͳ͍ͬͯΔͷ͔Λ௥ͬͯݟ͍ͨͱࢥ͍·͢ɻ

    View Slide

  4. Copyright© M&AΫϥ΢υ 4
    ؇΍͔ͳൺֱͷΧΦε͞
    ྫ:




    var_dump(0 == '00');


    var_dump('00' == '0000');


    var_dump('01' == '001');


    ग़ྗ:


    bool(true)


    bool(true)


    bool(true)


    View Slide

  5. Copyright© M&AΫϥ΢υ 5
    ؇΍͔ͳൺֱͷΧΦε͞
    ͸͍ɺҙຯΘ͔Γ·ͤΜɻ
    1, 2ߦ໨͸ཧղͰ͖ͳ͍͜ͱ΋͋Γ·ͤΜ͕
    3ߦ໨͕ԿނtrueʹͳΔͷ͔ɺݫີʹઆ໌͢Δʹ͸Ͳ͏ͨ͠Β͍͍Ͱ͠ΐ
    ͏͔ɻ
    ͜ͷൃදΛฉ͍ͯཧղ͠આ໌Ͱ͖ΔΑ͏ʹͳͬͯ໯͑Ε͹خ͍͠Ͱ͢ɻ

    View Slide

  6. Copyright© M&AΫϥ΢υ 6
    ؇΍͔ͳൺֱͷ࣮ମ͸Ͳ͜ʹ͋Δͷ͔
    ݴޠ࢓༷ʹؔ͢Δઆ໌͸υΩϡϝϯτΛಡΊ͹ྑ͍ͷͰ͕͢ɺ
    ࠷΋ਖ਼֬ͳυΩϡϝϯτ͸ԿͰ͠ΐ͏ɻ
    ͦΕ͸ιʔείʔυͰ͢ɻ
    ιʔείʔυͱͦΕΛઆ໌͍ͯ͠ΔυΩϡϝϯτʹࠩҟ͕͋ͬͯ΋ɺͦ
    ͷιʔείʔυͰಈ͍͍ͯΔҎ্͸ιʔείʔυ͕ਖ਼֬ͳυΩϡϝϯτ
    ʹͳΓ·͢ɻ
    PHPͷ؇΍͔ͳൺֱΛߦ͍ͬͯΔιʔείʔυຊମΛݟͯΈ·͠ΐ͏ɻ

    View Slide

  7. Copyright© M&AΫϥ΢υ 7
    ؇΍͔ͳൺֱͷ࣮ମ͸Ͳ͜ʹ͋Δͷ͔
    ࠓճ͸PHP8.0.9ͷιʔείʔυΛಡΜͰ͍͜͏ͱࢥ͍·͢ɻ

    View Slide

  8. Copyright© M&AΫϥ΢υ 8
    ιʔείʔυͷམͱ͠ํ
    ·ͣ͸ී௨ʹgit clone͠·͢
    git clone https://github.com/php/php-src

    View Slide

  9. Copyright© M&AΫϥ΢υ 9
    ιʔείʔυͷམͱ͠ํ
    ࠓͷphp-srcͷ࠷৽͸PHP8.1.4ͷRCʹͳ͍ͬͯΔͷͰɺ8.0.9ʹΞοϓ
    σʔτ͞ΕͨίϛοτΛಛఆͯ͠νΣοΫΞ΢τͯ͋͛͠·͢ɻ
    git checkout a5368c55be0a42af9fdc2c76fb0c2a06a42ddf36

    View Slide

  10. Copyright© M&AΫϥ΢υ 10
    PHPͷ؇΍͔ͳൺֱͷ࣮ଶ
    ʊਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓʊ
    ʼɹPHPͷ؇΍͔ͳൺֱ͸ಈ࡞Λݫີʹઆ໌Ͱ͖Δਓ͔͠࢖ͬͯ͸͍͚·ͤΜɻɹʻ
    ʉY^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^
    ʉ

    View Slide

  11. Copyright© M&AΫϥ΢υ 11
    ֘౰ιʔεʹḷΓண͘ํ๏
    ϊʔώϯτͰPHPͷιʔείʔυΛಡΜͰ΋ɺ໨తͷ৔ॴʹͨͲΓண
    ͚ͳ͚Ε͹ҙຯ͕͋Γ·ͤΜɻ
    ݴޠಛ༗ͷԋࢉࢠΛจࣈྻͰgrep΍IDEͷϑΝΠϧݕࡧΛͯ͠Έ·͢ɻ
    ࠓճ͸Ӊ஦ધԋࢉࢠ(<=>)Ͱݕࡧͯ͠Έ·͢ɻ

    View Slide

  12. Copyright© M&AΫϥ΢υ 12
    ֘౰ιʔεʹḷΓண͘ํ๏
    ͳΜ͔ͦΕͬΆ͍ϑΝΠϧ͕ݟ͔ͭΓ·ͨ͠ɻ
    PHPͷԋࢉࢠΛจࣈྻͱͯ͠ఆ͍ٛͯ͠Δࠜຊ͸Zend/
    zend_language_parser.yʹ͋Γͦ͏Ͱ͢ɻ
    ※.yͷ֦ுࢠͷϑΝΠϧ͸ߏจղੳػΛੜ੒͢ΔͨΊʹɺYaccͱݴ͏πʔ
    ϧͷϑΝΠϧͷԋࢉࢠͰ͢ɻ
    Zend/zend_language_parser.yΛ։͍ͯΈ·͢ɻ

    View Slide

  13. Copyright© M&AΫϥ΢υ 13
    ֘౰ιʔεʹḷΓண͘ํ๏
    Ӊ஦ધԋࢉࢠͷ্͙͢ʹո͍͠ߦ͕͋Γ·ͨ͠ɻ
    ໨తͷʮ==ʯ͸T_IS_EQUALͱ͍͏໊લ͕෇͍͍ͯΔΈ͍ͨͰ͢ɻ
    Zend/zend_language_parser.y಺ͰT_IS_EQUALΛݕࡧ͢Δͱ

    View Slide

  14. Copyright© M&AΫϥ΢υ 14
    ֘౰ιʔεʹḷΓண͘ํ๏
    ͜Μͳιʔε͕ݟ͔ͭΓ·͢ɻ
    Ͳ͏΍ΒZEND_IS_EQUALͱ͍͏໊લͰதؒίʔυʹม׵͞ΕΔΈͨ
    ͍Ͱ͢ɻ

    View Slide

  15. Copyright© M&AΫϥ΢υ 15
    ZEND_IS_EQUALͱݴ͏ϫʔυΛෳ਺ϑΝΠϧݕࡧ͢Δͱɺ
    Zend/zend_opcode.cʹ͜Μͳߦ͕͋Γ·ͨ͠
    case ZEND_IS_EQUAL:

    View Slide

  16. Copyright© M&AΫϥ΢υ 16
    Zend/zend_opcode.cͷத਎
    END_API binary_op_type get_binary_op(int opcode)


    {


    switch (opcode) {


    /* ~~~~~~~~~~ */


    case ZEND_IS_EQUAL:


    case ZEND_CASE:


    return (binary_op_type) is_equal_function;


    case ZEND_IS_NOT_EQUAL:


    return (binary_op_type) is_not_equal_function;


    /* ~~~~~~~~~~ */


    default:


    ZEND_UNREACHABLE();


    return (binary_op_type) NULL;


    }


    }
    ͜Μͳͷ͕͍·ͨ͠ɻ


    ZEND_IS_EQUAL͸is_equal_functionͱ͍͏ؔ਺ʹରԠ͍ͯ͠ΔΑ͏Ͱ͢Ͷɻ


    is_equal_functionͰݕࡧΛ͔͚ͯΈ·͢ɻ

    View Slide

  17. Copyright© M&AΫϥ΢υ 17
    is_equal_functionͰݕࡧΛ͔͚ͯΈΔ
    ZEND_API int ZEND_FASTCALL is_equal_function(zval *result, zval *op1, zval *op2)
    Zend/zend_operators.cʹḷΓண͖·ͨ͠ɻ


    ϊʔώϯτͰ΋ɺIDEͷϑΝΠϧݕࡧͰେͨ࣌ؒ͠Λ͔͚ͣʹ΄͍͠
    ৘ใ·ͰḷΓண͚·͢ɻ


    ϥΠϒϥϦͷத਎Λௐ΂͍ͨͱ͖౳ʹ΋࢖͑ΔͷͰࢀߟʹͯ͠Έͯ͘
    ͍ͩ͞ɻ

    View Slide

  18. Copyright© M&AΫϥ΢υ 18
    ؇΍͔ͳൺֱͷਖ਼ମݟͨΓʂ
    is_equal_function͸zend_compareͱݴ͏ؔ਺ʹରԠ͍ͯ͠ΔΑ͏Ͱ͢ɻ
    /* == ͷॲཧ */


    ZEND_API zend_result ZEND_FASTCALL is_equal_function(zval *result, zval *op1, zval *op2) /* {{{ */


    {


    ZVAL_BOOL(result, zend_compare(op1, op2) == 0);


    return SUCCESS;


    }


    /* }}} */


    View Slide

  19. Copyright© M&AΫϥ΢υ 19
    ؇΍͔ͳൺֱͷਖ਼ମݟͨΓʂ
    ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2) /* {{{ */


    {


    int converted = 0;


    zval op1_copy, op2_copy;




    while (1) {


    switch (TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {


    case TYPE_PAIR(IS_LONG, IS_LONG):


    return Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */


    case TYPE_PAIR(IS_LONG, IS_DOUBLE):


    return ZEND_NORMALIZE_BOOL((double)Z_LVAL_P(op1) - Z_DVAL_P(op2));


    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */


    case TYPE_PAIR(IS_ARRAY, IS_ARRAY):


    return zend_compare_arrays(op1, op2);




    case TYPE_PAIR(IS_NULL, IS_NULL):


    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */


    case TYPE_PAIR(IS_STRING, IS_STRING):


    if (Z_STR_P(op1) == Z_STR_P(op2)) {


    return 0;


    }


    return zendi_smart_strcmp(Z_STR_P(op1), Z_STR_P(op2));


    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */


    default:


    if (Z_ISREF_P(op1)) {


    op1 = Z_REFVAL_P(op1);


    continue;


    } else if (Z_ISREF_P(op2)) {


    op2 = Z_REFVAL_P(op2);


    continue;


    }




    if (Z_TYPE_P(op1) == IS_OBJECT


    && Z_TYPE_P(op2) == IS_OBJECT


    && Z_OBJ_P(op1) == Z_OBJ_P(op2)) {


    return 0;


    } else if (Z_TYPE_P(op1) == IS_OBJECT) {


    return Z_OBJ_HANDLER_P(op1, compare)(op1, op2);


    } else if (Z_TYPE_P(op2) == IS_OBJECT) {


    return Z_OBJ_HANDLER_P(op2, compare)(op1, op2);


    }




    if (!converted) {


    if (Z_TYPE_P(op1) < IS_TRUE) {


    return zval_is_true(op2) ? -1 : 0;


    } else if (Z_TYPE_P(op1) == IS_TRUE) {


    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */


    } else {


    op1 = _zendi_convert_scalar_to_number_silent(op1, &op1_copy);


    op2 = _zendi_convert_scalar_to_number_silent(op2, &op2_copy);


    if (EG(exception)) {


    return 1; /* to stop comparison of arrays */


    }


    converted = 1;


    }


    } else if (Z_TYPE_P(op1)==IS_ARRAY) {


    return 1;


    } else if (Z_TYPE_P(op2)==IS_ARRAY) {


    return -1;


    } else {


    ZEND_UNREACHABLE();


    zend_throw_error(NULL, "Unsupported operand types");


    return 1;


    }


    }


    }


    }


    /* }}} */
    php-src/Zend/zend_operators.c

    View Slide

  20. Copyright© M&AΫϥ΢υ 20
    ؇΍͔ͳൺֱͷਖ਼ମݟͨΓʂ
    ௕͍...
    ΋͏ͳΜ͔ΠϠͳ༧ײ͕͠·͢Ͷɻ
    த਎Λཁ໿͍ͯ͘͠ͱɺԋࢉࢠͷࠨӈͷΦϖϥϯυͷܕͷ૊Έ߹ΘͤʹԠͯ͡ɺ
    ྑ͍ײ͡ʹॲཧΛ෼ذͯ͠ฦ͍ͯ͠ΔΈ͍ͨͰ͢ɻ
    ਺஋ಉ࢜ͷൺֱ͸ී௨ʹߦΘΕ͍ͯ·͕͢ɺจࣈྻͷൺֱ͸ͳΜ͔ؔ਺͕ݺ͹Εͯ·͢ɻ
    case TYPE_PAIR(IS_STRING, IS_STRING):


    if (Z_STR_P(op1) == Z_STR_P(op2)) {


    return 0;


    }


    return zendi_smart_strcmp(Z_STR_P(op1), Z_STR_P(op2));


    zendi_smart_strcmpͱݴ͏ո͍ؔ͠਺͕ݺ͹Ε͍ͯ·͢ɻ
    ͲΜͳౕ͔ݟͯΈ·͠ΐ͏ɻ

    View Slide

  21. Copyright© M&AΫϥ΢υ 21
    zendi_smart_strcmpͱݴ͏ո͍ؔ͠਺
    ZEND_API int ZEND_FASTCALL zendi_smart_strcmp(zend_string *s1, zend_string *s2) /* {{{ */


    {


    zend_uchar ret1, ret2;


    int oflow1, oflow2;


    zend_long lval1 = 0, lval2 = 0;


    double dval1 = 0.0, dval2 = 0.0;




    if ((ret1 = is_numeric_string_ex(s1->val, s1->len, &lval1, &dval1, false, &oflow1, NULL)) &&


    (ret2 = is_numeric_string_ex(s2->val, s2->len, &lval2, &dval2, false, &oflow2, NULL))) {


    #if ZEND_ULONG_MAX == 0xFFFFFFFF


    if (oflow1 != 0 && oflow1 == oflow2 && dval1 - dval2 == 0. &&


    ((oflow1 == 1 && dval1 > 9007199254740991. /*0x1FFFFFFFFFFFFF*/)


    || (oflow1 == -1 && dval1 < -9007199254740991.))) {


    #else


    if (oflow1 != 0 && oflow1 == oflow2 && dval1 - dval2 == 0.) {


    #endif


    /* both values are integers overflown to the same side, and the


    * double comparison may have resulted in crucial accuracy lost */


    goto string_cmp;


    }


    if ((ret1 == IS_DOUBLE) || (ret2 == IS_DOUBLE)) {


    if (ret1 != IS_DOUBLE) {


    if (oflow2) {


    /* 2nd operand is integer > LONG_MAX (oflow2==1) or < LONG_MIN (-1) */


    return -1 * oflow2;


    }


    dval1 = (double) lval1;


    } else if (ret2 != IS_DOUBLE) {


    if (oflow1) {


    return oflow1;


    }


    dval2 = (double) lval2;


    } else if (dval1 == dval2 && !zend_finite(dval1)) {


    /* Both values overflowed and have the same sign,


    * so a numeric comparison would be inaccurate */


    goto string_cmp;


    }


    dval1 = dval1 - dval2;


    return ZEND_NORMALIZE_BOOL(dval1);


    } else { /* they both have to be long's */


    return lval1 > lval2 ? 1 : (lval1 < lval2 ? -1 : 0);


    }


    } else {


    int strcmp_ret;


    string_cmp:


    strcmp_ret = zend_binary_strcmp(s1->val, s1->len, s2->val, s2->len);


    return ZEND_NORMALIZE_BOOL(strcmp_ret);


    }


    }


    /* }}} */


    View Slide

  22. Copyright© M&AΫϥ΢υ 22
    zendi_smart_strcmpͱݴ͏ո͍ؔ͠਺
    ͏͑ʙ
    ॲཧͷ಺༰͸ɺ
    ● ೖྗ͞Εͨจࣈྻ͕ʮ਺஋ͱͯ͠ධՁͰ͖Δ৔߹͸਺஋ͱͯ͠ѻ͍ൺֱ͢Δʯ
    ● ਺஋ͱͯ͠ධՁग़དྷͳ͍৔߹͸จࣈྻಉ࢜ͷൺֱΛߦ͏
    strcmp໋໊ͬͯ͸ͳΜͳΜͩ...
    ͭ·Γstrcmpͱ͸໊͹͔ΓͰɺจࣈྻͱͯ͠ҟͳ͍ͬͯͯ΋਺஋ͱͯ͠߹͍ͬͯΕ͹
    true Ͱ͢ɻ
    ୭ͩ͜ΜͳΫιΈ͍ͨͳ࢓༷ࢥ͍͍ͭͨ΍ͭʙ

    View Slide

  23. Copyright© M&AΫϥ΢υ 23
    PHPͷ؇΍͔ͳൺֱͷ࣮ଶ
    ͜ΕΛ౿·͑ͯ΋͏Ұ౓ίʔυΛݟͯΈ·͠ΐ͏
    ιʔείʔυɿ




    var_dump(0 == '00');


    var_dump('00' == '0000');


    var_dump('01' == '001');


    ग़ྗɿ


    bool(true)


    bool(true)


    bool(true)


    ͜ΕͰ๯಄ͷίʔυͷ࣮ߦ݁ՌͷṖ༹͕͚ͨͱࢥ͍·͢ɻ

    View Slide

  24. Copyright© M&AΫϥ΢υ 24
    ݫີͳൺֱͷιʔε͸Ͳ͜ʁ
    Zend/zend_operators.cʹ͍·͢ɻ
    ZEND_API zend_bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2) /* {{{ */


    {


    if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {


    return 0;


    }


    switch (Z_TYPE_P(op1)) {


    case IS_NULL:


    case IS_FALSE:


    case IS_TRUE:


    return 1;


    case IS_LONG:


    return (Z_LVAL_P(op1) == Z_LVAL_P(op2));


    case IS_RESOURCE:


    return (Z_RES_P(op1) == Z_RES_P(op2));


    case IS_DOUBLE:


    return (Z_DVAL_P(op1) == Z_DVAL_P(op2));


    case IS_STRING:


    return zend_string_equals(Z_STR_P(op1), Z_STR_P(op2));


    case IS_ARRAY:


    return (Z_ARRVAL_P(op1) == Z_ARRVAL_P(op2) ||


    zend_hash_compare(Z_ARRVAL_P(op1), Z_ARRVAL_P(op2), (compare_func_t)
    hash_zval_identical_function, 1) == 0);


    case IS_OBJECT:


    return (Z_OBJ_P(op1) == Z_OBJ_P(op2));


    default:


    return 0;


    }


    }


    /* }}} */


    View Slide

  25. Copyright© M&AΫϥ΢υ 25
    ݫີͳൺֱͷιʔε͸Ͳ͜ʁ
    ·ͣ࠷ॳʹܕҧ͍ͰfalseʹͳΓ·͢ɻ
    ܕ͕Ұக্ͨ͠ͰɺͦΕͧΕͷܕͰγϯϓϧͳൺֱΛ͍ͯ͠·͢ɻ
    ʮ==ʯͷιʔείʔυΑΓང͔ʹγϯϓϧͰ͢ɻ

    View Slide

  26. Copyright© M&AΫϥ΢υ 26
    ͍͞͝ʹ
    ࣗ෼΋ࠓ·ͰͳΜͱͳ͘Ͱ͔͠ɺཧղͯ͠ͳ͔ͬͨͰ͕͢ɺࠓճͷௐ
    ࠪͰͲΕ͚ͩةݥͳ୅෺͔Α͘ཧղͰ͖·ͨ͠ɻ
    օ͞ΜPHPͷൺֱԋࢉࢠ͸໧ͬͯݫ֨ͳൺֱΛ࢖͍·͠ΐ͏ɻ

    View Slide

  27. Copyright© M&AΫϥ΢υ
    ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ
    27

    View Slide