"PHP" / Говнокод #18191 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
/* Convert IP address to unsigned long int. */
function ip2ulong($ip) {
    return sprintf("%u", ip2long32($ip));
}

https://github.com/pfsense/pfsense/blob/master/etc/inc/util.inc#L414-L417

Продолжаем осторожно идти дальше. Это нормально вообще? Да, я видел всякие смехуёчки с магическим приведением типов в PHP и JS, так что наверняка оно где-то правильно конвертится и все работает несмотря ни на что и вопреки. Может, это даже нормальная практика в мире PHP, но мне страшно.

Запостил: superhacker777 superhacker777, (Updated )

Комментарии (19) RSS

  • Я выяснил кое-что. В мире PHP есть только знаковый целочисленный тип длиной в аж 32 бита, лонгов нет. Байтоебле это дело не мешает, но ребятам нужно проверять, чтобы начало блока IP-адресов было меньше конца (ну и не только). Знак тут может сильно делу помешать. Поэтому они делают это так:
    /* Return true if the first IP is 'after' the second */
    function ip_greater_than($ip1, $ip2) {
      // Compare as unsigned long because otherwise it wouldn't work
      //   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
      return ip2ulong($ip1) > ip2ulong($ip2);
    }


    Ну, сравнение строк-то работает как надо. Во-от.
    Ответить
    • > как надо
      Потому что большие числа внутри строк кастуются в дабл?
      Ответить
      • Нет, обычное сравнение строк. «Стол» меньше «табуретки», так как у строки длина меньше, но «стол» больше, чем «Стол», так как сначала идут в кодовых таблицах заглавные, а значит номера у них поменьше.
        Ответить
        • Но ведь "стул" > "столик", хотя длина строки у столика больше. Почему же для чисел работает?
          Ответить
          • O-o-oh sh~ Тогда, очевидно, длина не учитывается. Вернее, сравнивают по самому короткому. Страшно представить, к чему приведет такая хуйня.
            Ответить
            • Тогда это надо сделать как-то так. Добавить только еще проверку на размер инта.

              function is_greater_than($left, $right) {
                if ($left < 0) {
                  if ($right >= 0)
                    return true;
                  else
                    return $left > $right;
                } else {
                  if ($right < 0)
                    return false;
                  else
                    return $left > $right;
                }
              }


              Все правильно ведь сделал?
              Ответить
            • Ниже я привёл выдержку из исходников самого PHP. Его ядро при сравнении строк пытается сначала выделить из каждой строки числовое значение типа дабл и типа лонг, после чего сравнивает их как даблы или как лонги (именно как лонги, хотя для числовых операций long в PHP нигде не используется) и лишь в случае, когда числа из строк выделить нельзя, сравнивает их как обычные строки (т. е. по самому короткому аргументу).
              Ответить
            • Впрочем, хуета. Как видно ниже, там попытка каста в дабл и все должно работать как ожидается. И хотя условия погонять будет быстрее, чем строку форматировать, разницы большой нет.

              Сам отвечу на свой вопрос: да, это нормальная практика.
              Ответить
    • Всё намного сложнее. Есть два варианта интерпретатора PHP: с 32-битным целочисленным типом и с 64-битным целочисленным типом. Для 32-битной ОС есть только PHP с 32-битным целым.

      Странно при этом, что сравнение больших чисел, приведённых к строкам в десятичной системе счисления, работает.
      Ответить
      • Ну я сверху описал, как сравнение строк по-идее должно работать. Я себе это всегда так представлял, но, честно, не удивлюсь, если попытки кастов где-нибудь есть в PHP. Про размеры интов знаю. Там, кажется, конфиг править надо на 64-битной ОС, чтобы он подлиннее использовал числа.
        Ответить
        • А я кое-что нашёл:
          ZEND_API void zendi_smart_strcmp(zval *result, zval *s1, zval *s2) /* {{{ */
          {
          	int ret1, ret2;
          	long lval1, lval2;
          	double dval1, dval2;
          
          	if ((ret1=is_numeric_string(Z_STRVAL_P(s1), Z_STRLEN_P(s1), &lval1, &dval1, 0)) &&
          		(ret2=is_numeric_string(Z_STRVAL_P(s2), Z_STRLEN_P(s2), &lval2, &dval2, 0))) {
          		if ((ret1==IS_DOUBLE) || (ret2==IS_DOUBLE)) {
          			if (ret1!=IS_DOUBLE) {
          				dval1 = (double) lval1;
          			} else if (ret2!=IS_DOUBLE) {
          				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;
          			}
          			Z_DVAL_P(result) = dval1 - dval2;
          			ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
          		} else { /* they both have to be long's */
          			ZVAL_LONG(result, lval1 > lval2 ? 1 : (lval1 < lval2 ? -1 : 0));
          		}
          	} else {
          string_cmp:
          		Z_LVAL_P(result) = zend_binary_zval_strcmp(s1, s2);
          		ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(result)));
          	}
          }

          PHP 5.3.
          Ответить
          • А что делает is_numeric_string с большими числами, которые не входят в лонг, типа "10000000000"? Всё-таки парсит как дабл?
            Ответить
            • Парсит как лонг. Если натыкается на точку или экспоненту, то парсит как дабл. Если длина выходит больше MAX_LENGTH_OF_LONG, то false.
              Ответить
            • Там код на >120 строк. Сейчас разберу.

              1. Сначала он ищет 0x или 0X и в случае успеха интерпретирует строку как шестнадцатеричное число.

              2. Если длина шестнадцатеричной строки >= SIZEOF_LONG * 2, парсит её как шестнадцатеричную, но приводит к даблу.

              3. Потом он пытается найти точку или e или E. Если находит, интерпретирует строку как дабл.

              5. Сравнивает число знаков с MAX_LENGTH_OF_LONG. Если больше, то тоже интерпретирует как дабл.

              6. Потом делает ещё непонятное сравнение для случая, когда число знаков точно равно MAX_LENGTH_OF_LONG - 1.

              7. Если все тесты провалены, то парсит строку как лонг.
              Ответить
              • P.S. Для интересующихся, кто все эти константы:
                #if SIZEOF_LONG == 4
                #define MAX_LENGTH_OF_LONG 11
                static const char long_min_digits[] = "2147483648";
                #elif SIZEOF_LONG == 8
                #define MAX_LENGTH_OF_LONG 20
                static const char long_min_digits[] = "9223372036854775808";
                #else
                #error "Unknown SIZEOF_LONG"
                #endif
                Ответить
    • > Ну, сравнение строк-то работает как надо. Во-от.
      А если 0.0.7.0 и 0.0.69.0 (1792 и 17664, если я правильно посчитал).
      Ответить
      • А не, нашёл коммент inkanus-gray про каст в число. Тогда да, прокатит)
        Используем всю магию языка по максимому)))
        Ответить
        • Оказывается, и long в PHP есть, но чтобы его откопать, нужно число перевести в строку. Поле чудес...
          Ответить
          • Это лонг в С++, я правильно понимаю? И они его для своих темных дел используют, но интерпретатор понятия о нем не имеет, получается.
            Ответить

Добавить комментарий

Переведи на "PHP", guest!

    А не использовать ли нам bbcode?


    8