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

PHPの緩やかな比較の実態

8e87776681d08819951c07ad313ab568?s=47 ゆい
April 11, 2022

 PHPの緩やかな比較の実態

8e87776681d08819951c07ad313ab568?s=128

ゆい

April 11, 2022
Tweet

Other Decks in Programming

Transcript

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

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

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

  4. Copyright© M&AΫϥ΢υ 4 ؇΍͔ͳൺֱͷΧΦε͞ ྫ: <?php var_dump(0 == '00'); var_dump('00'

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

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

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

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

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

  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^ ʉ

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

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

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

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

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

  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ͰݕࡧΛ͔͚ͯΈ·͢ɻ
  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ͷϑΝΠϧݕࡧͰେͨ࣌ؒ͠Λ͔͚ͣʹ΄͍͠ ৘ใ·ͰḷΓண͚·͢ɻ ϥΠϒϥϦͷத਎Λௐ΂͍ͨͱ͖౳ʹ΋࢖͑ΔͷͰࢀߟʹͯ͠Έͯ͘ ͍ͩ͞ɻ
  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; } /* }}} */
  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)<Z_LVAL_P(op2)?-1:0); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 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
  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ͱݴ͏ո͍ؔ͠਺͕ݺ͹Ε͍ͯ·͢ɻ ͲΜͳౕ͔ݟͯΈ·͠ΐ͏ɻ
  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); } } /* }}} */
  22. Copyright© M&AΫϥ΢υ 22 zendi_smart_strcmpͱݴ͏ո͍ؔ͠਺ ͏͑ʙ ॲཧͷ಺༰͸ɺ • ೖྗ͞Εͨจࣈྻ͕ʮ਺஋ͱͯ͠ධՁͰ͖Δ৔߹͸਺஋ͱͯ͠ѻ͍ൺֱ͢Δʯ • ਺஋ͱͯ͠ධՁग़དྷͳ͍৔߹͸จࣈྻಉ࢜ͷൺֱΛߦ͏

    strcmp໋໊ͬͯ͸ͳΜͳΜͩ... ͭ·Γstrcmpͱ͸໊͹͔ΓͰɺจࣈྻͱͯ͠ҟͳ͍ͬͯͯ΋਺஋ͱͯ͠߹͍ͬͯΕ͹ true Ͱ͢ɻ ୭ͩ͜ΜͳΫιΈ͍ͨͳ࢓༷ࢥ͍͍ͭͨ΍ͭʙ
  23. Copyright© M&AΫϥ΢υ 23 PHPͷ؇΍͔ͳൺֱͷ࣮ଶ ͜ΕΛ౿·͑ͯ΋͏Ұ౓ίʔυΛݟͯΈ·͠ΐ͏ ιʔείʔυɿ <?php var_dump(0 == '00');

    var_dump('00' == '0000'); var_dump('01' == '001'); ग़ྗɿ bool(true) bool(true) bool(true) ͜ΕͰ๯಄ͷίʔυͷ࣮ߦ݁ՌͷṖ༹͕͚ͨͱࢥ͍·͢ɻ
  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; } } /* }}} */
  25. Copyright© M&AΫϥ΢υ 25 ݫີͳൺֱͷιʔε͸Ͳ͜ʁ ·ͣ࠷ॳʹܕҧ͍ͰfalseʹͳΓ·͢ɻ ܕ͕Ұக্ͨ͠ͰɺͦΕͧΕͷܕͰγϯϓϧͳൺֱΛ͍ͯ͠·͢ɻ ʮ==ʯͷιʔείʔυΑΓང͔ʹγϯϓϧͰ͢ɻ

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

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