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

PHPの緩やかな比較の実態

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.
Avatar for ゆい ゆい
April 11, 2022

 PHPの緩やかな比較の実態

Avatar for ゆい

ゆい

April 11, 2022
Tweet

More Decks by ゆい

Other Decks in Programming

Transcript

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

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

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

    var_dump('00' == '0000'); var_dump('01' == '001'); ग़ྗɿ bool(true) bool(true) bool(true) ͜ΕͰ๯಄ͷίʔυͷ࣮ߦ݁ՌͷṖ༹͕͚ͨͱࢥ͍·͢ɻ
  10. 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; } } /* }}} */