Загадочный баг в ПХП

Во всех современных интерпретаторов вёдра багов, я обычно и внимания на них не обращаю, но свежезаведённый баг в ПХП какой-то забавный:

bolk@Bolk ~  $ php -r 'var_dump(0x0 + 2); var_dump(0x0+2); var_dump(0x0 +10);'
int(2)
int(4)
int(26)

Как видно, ПХП (у меня на 5.3.8 работает) считает сложение с шестадцатеричным нулём (обязательно слева и обязательно без пробела перед вторым слагаемым) какой-то интересной операцией.

С чем связано пока непонятно, я посмотрел какие токены выдаёт интерпретатор на парсинге, ничего подозрительного не увидел.

Добавлено позднее: с минусом тоже работает. Например «0x0 -10» даёт -26.

Добавлено ещё позднее: разобрался что это за интересная операция.

Читайте также

POSIX-регулярки в PCRE
Для меня это просто открытие какое-то, что POSIX regexp работают внутри PCRE: bolk.dev ~ $ php <<PHP
2009

Комментарии 14

SunChaser (sunchaser.info) № 1
может он считает его указателем?
Евгений Степанищев (bolknote.ru) автор № 4
Комментарий для sunchaser.info:

Смысл покопаться в коде есть, но я бы не рискнул предположить что там не так, не глядя. Все числа до 10 умножаются на два, дальше какая-то каша, не пытался разобраться.
Евгений Степанищев (bolknote.ru) автор № 6
Комментарий для sunchaser.info:

Я назвал это «кашей» потому что знание о правиле «с переходом на следующую 10-ку добавляется 8, дальше опять добавляется по 2» не даёт ни понимания почему так происходит, ни сколько добавится при переходе с «0x0 +99» к «0x0 +100».
Евгений Степанищев (bolknote.ru) автор № 9
Комментарий для sunchaser.info:

Посмотрел в код, вот что там происходит.

Когда ПХП встречает «0x…» он пропускает два байта («0x»), потом пропускает ведущие нули и передаёт остаток на конвертацию в функцию strtol (из stdlib). strtol применяется для преобразования строкового представления числа к long с указанным основанием системы счисления. ПХП вызывает её с параметром 16.

Ошибка в том, что не проверяется, что число не кончилось. В итоге в strtol попадает « +2», т.е. само число, которое переводится функцией strtol в шестнадцатеричный вид. Т.е. до десятки числа будут как бы умножаться на два, а дальше число будет складываться само с собой, но переведёным в шестнадцатеричный вид.

Например из десятки получится 26, так как складывается 10 + 0x10. Из сотни — 356 (100 + 0x100) и так далее.

Почему это работает только с пробелом не разбирался, спать пойду, завтра в Челябинск лететь ещё.
Евгений Степанищев (bolknote.ru) автор № 10

Комментарий для sunchaser.info:

python-style?

Ага :)

что не проверяется, что число не кончилось

тут у меня опечатка — «не проверяется, что число кончилось» должно быть.

SunChaser (sunchaser.info) № 11

Комментарий для Евгения Степанищева:

действительно,

if (len == 0) {
zendlval->value.lval = 0;
zendlval->type = IS_LONG;
return T_LNUMBER;
} else if (len < SIZEOF_LONG * 2 || (len == SIZEOF_LONG * 2 && *hex Почему это работает только с пробелом не разбирался

тогда там анализируется и преобразуется строка '+' (или '-'), из которой получается 0

вот еще интересно:

[sunchaser@maritropa cli]$ ./php -r 'var_dump(0x0 + 2); var_dump(0x0+2); var_dump(0x0 +10);'
int(2)
int(2)
int(10)
[sunchaser@maritropa cli]$ ./php -v
PHP 5.2.17 (cli) (built: Feb 24 2012 01:01:50)
Copyright © 1997-2010 The PHP Group
Zend Engine v2.2.0, Copyright © 1998-2010 Zend Technologies

код {HNUM} {}
в 5.2 и 5.3 одинаковый
почему раньше это работало? :)