LINUX.ORG.RU
решено ФорумTalks

Я люблю тебя, PHP!

 ,


0

2

Написал серверное приложение на чистом С. Оно слушает порт, а PHP с ним соединяется и получает данные в json-формате.

Несколько часов убил на то, чтобы понять почему PHP не может сделать json_decode. Наконец, написал минимальный пример:

$ cat test.php 
<?php

$s1 = '["1", "2"]';
echo "1 ", var_dump(json_decode($s1)), "\n";

$s2 = $s1 . "\0";
echo "2 ", var_dump(json_decode($s2)), "\n";

$ php test.php
1 array(2) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
}

2 NULL

Да это же просто феерично! Я добавил нулевой символ в конец строки, а она перестала быть корректным json, твою мать! Я даже сравнил побайтово две строки, вывев их в файл: отличие только в \0 на конце.

Что за специалист писал такой парсер json-а в php?!

---

Отныне это тред о впечатлениях, оставшихся после встречи с неочевидными, глупыми и неграмотными решениями в разных ЯП. Делись своими впечатлениями, лоровец.

★★

Последнее исправление: CYB3R (всего исправлений: 1)
Ответ на: комментарий от vasily_pupkin

Нет, ты ошибаешься. Вместо того, чтобы _остановиться_ и проверить, все ли токены закрыты, оно пытается _продолжить_ парсинг и натыкается на «некорректный» с позиции грамматики символ.

Отсюда вопрос: какого хера в грамматике языка отсутствует символ '\0'?

Изучив сей документ http://www.ietf.org/rfc/rfc4627.txt , в котором действительно отсутствует описание поведения при встрече с символом \0, следует сказать, что разработчики явно *недосмотрели* эту очевидную ситуацию, и это камень в их огород.

bk_ ★★
() автор топика
Ответ на: комментарий от tiandrey

Вот именно поэтому стандарт содержит недоработку, так как пропускает такую очевидную ситуацию.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Ну возьми да напиши разрабам, что их стандарт содержит такую существенную (по твоему профессиональному мнению) недоработку. А пока этого нет в стандарте, глупо жаловаться, что оно не работает - оно и не должно работать.

tiandrey ★★★★★
()
Ответ на: комментарий от tiandrey

Судя по твоему тону, ты принципиально не согласен со мной в том, что это недоработка именно стандарта?

Стандарт пишут такие же люди, которые могут допустить и допускают ошибки, недосмотры и т.п. Этот пример - один из них.

Твоя же позиция звучит как «раз есть стандарт - плевать что он содержит очевидную недоработку! Плевать, что решение этого сходу неочевидно! Плевать, ведь разработчики стандарта умнее нас!».

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Судя по твоему тону, ты принципиально не согласен со мной в том, что это недоработка именно стандарта?

Я тоже не согласен. Это не дает абсолютно никаких преимуществ. Или, может, любые мусорные символы разрешить?

note173 ★★★★★
()
Ответ на: комментарий от bk_

Где сказано, что \0 инициирует *такое* поведение?

«потоки YAML используют печатаемые Unicode-символы, как UTF-8, так и UTF-16»

Всё, что не укладывается в эту доктрину - это и есть проблема.

Вам остаётся лишь смириться и следовать этим правилам. Либо продолжать впустую и безрезультатно кричать на ЛОРе об якобы проблеме в реализации сторонних парсеров, которые «спотыкаются» на сгенерированном Вами контента.

P.S. есть ещё третий путь: выработать свои правила и заставить окружающих следовать им.

Slavaz ★★★★★
()
Ответ на: комментарий от bk_

По-честному, '\0' останавливать парсер.

Почему? Не во всех системах \0 это конец строки, в некоторых используются другие символы, другие символы тоже должны останавливать парсер?
Для си это работает, потому что си воспринимает \0 как конец строки, для других языков это просто часть строки.
И к слову, есть вероятность, что си неправильно будет парсить json, в середине которого есть \0.

Tark ★★
()
Ответ на: комментарий от note173

А вариант написать автору стандарта и спросить? Я так и сделаю, ведь наверняка он лучше всех аналитиков лора вместе взятых знает, почему так было сделано.

bk_ ★★
() автор топика
Ответ на: комментарий от Tark

есть вероятность, что си неправильно будет парсить json, в середине которого есть \0.

Абсолютно верно.

http://www.digip.org/jansson/doc/2.3/apiref.html#string

Normal null terminated C strings are used, so JSON strings may not contain embedded null characters. All other Unicode codepoints U+0001 through U+10FFFF are allowed.

Но *где* используются строки с \0 в середине?

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Именно. Он должен быть человекочитаемым, это его главная фича. А всякие управляющие символы там ни к чему. А ты почему-то вцепился в null-terminated strings, и не можешь сдвинуть точку зрения.

tiandrey ★★★★★
()

PHP виноват в том, что твой говнокод на C вставил '\0' куда не положено?

Deleted
()
Ответ на: комментарий от bk_

Я имел ввиду *зачем* и *почему* используют \0 в середине?

В utf-16 легко можно встретить нулевые байты в любом месте. Utf-8 от этого защищен.

note173 ★★★★★
()
Ответ на: комментарий от tiandrey

А ты почему-то вцепился в null-terminated strings, и не можешь сдвинуть точку зрения.

Правильно, не могу, потому что постоянно работаю с языком С. А там \0 имеет одно и только одно назначение, но никак не разделители в середине строки. За код в продакшене, который показал Tark выше, надо гнать ссаными тряпками вон из профессии.

У \0 *всегда* была только одна роль - символ в конце строки. Потом пришли Вирты и придумали pascal-style строки, а потом пришли php-шники, и вообще нивелировали \0.

Если автор JSON-стандарта забыл записать примитившейшую возможность экранировать нулевой символ «\0»: ведь экранируют-то двойные кавычки. Таким же образом можно экранировать и нулевой символ «blabla\„bla\0bla“. Но нет, видимо, автор на С не писал, поэтому это прошло мимо его внимания.

s/\\0/\0/ можно запилить уже вне json-парсера, кстати, но *зачем*, если это идеально встраивается в концепцию грамматики парсера, ничего не нарушая, и лишь добавляя строгости и четкости в определениях?

bk_ ★★
() автор топика
Ответ на: комментарий от note173

Здесь через раз появляется комментарий КО, который не может предугадать самый очевидный ответ.

Если я вставлю в конец строки \0\0, что соответствует концу строки для wchar_t? Будет то же самое.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

А почему к этому вопросу подходить с этой стороны? Может запретим вообще все терминирующие символы со всех систем, оставим половину байта только для передачи? Этот символ стал означать конец строки только начиная с Си и только в языках с нуль-терминированными строками, в программах написанных на других языках он не зарезервирован, и бывает использовался.
П.С.
Я не знаю зачем его используют в середине, но «%00hello» если сделать urldecode превратится в «\0hello», что может кстати неприятно удивить того, кто разрабатывал сайт на си.

Tark ★★
()
Ответ на: комментарий от bk_

У \0 *всегда* была только одна роль - символ в конце строки.

Да, при этом он (\0) оставался внутри программы. Если твоя программа гадит этим нулевым символом за своими пределами, то это проблема твоей программы, а не стандарта, парсера, расположения планет и тому подобного.

tiandrey ★★★★★
()
Ответ на: комментарий от note173

В utf-16 легко можно встретить нулевые байты в любом месте.

нет, не в любом месте. Только там, где они ожидаемы и допустимы.

Slavaz ★★★★★
()
Ответ на: комментарий от bk_

Если я вставлю в конец строки \0\0, что соответствует концу строки для wchar_t? Будет то же самое.

Это будет невалидными входными данными (или не будет, если такое разрешено в юникоде). Но, в любом случае, представление строки в памяти и сама строка — разные вещи. Zero-terminated строки — костыль, который был оправдан во времена C.

(Нет, валидными такие данные в любом случае не будут. Кому нужен токен \0?)

note173 ★★★★★
()
Последнее исправление: note173 (всего исправлений: 1)
Ответ на: комментарий от bk_

У \0 *всегда* была только одна роль - символ в конце строки. Потом пришли Вирты и придумали pascal-style строки

Тащемта сначала пришли Вирты и придумали pascal-style строки, а потом уже пришел Си.

Tark ★★
()
Ответ на: комментарий от bk_

У \0 *всегда* была только одна роль - символ в конце строки.

В корне неверное суждение. Нулевой символ, прерывающий строку - это наипервейший костыль, тянущийся со времён, когда «640 килобайт должно хватить всем» и когда экономили на каждом битике и байтике в ущерб удобности. По моему мнению, современном мире строка должна описываться структурой:
struct String {
codepage_t encoding;
size_t len;
void *data;
}

Потом пришли Вирты и придумали pascal-style строки, а потом пришли php-шники, и вообще нивелировали \0.

и, что удивительно, «всё правильно сделали». Строка должна иметь длину, а не «признак конца строки».

Slavaz ★★★★★
()
Ответ на: комментарий от bk_

Если автор JSON-стандарта забыл записать примитившейшую возможность экранировать нулевой символ «\0»: ведь экранируют-то двойные кавычки. Таким же образом можно экранировать и нулевой символ «blabla\„bla\0bla“. Но нет, видимо, автор на С не писал, поэтому это прошло мимо его внимания.

бред. символа \0 не должно быть в обрабатываемой строке, если ты его используешь для отделения сообщений одно от другого, то будь любезен написать парсер, который будет сам разбивать json сообщения. Т.к. это только твоя затея, только ты виноват и только тебе разгребать, тот говнокод (не-говно протокол), который ты используешь.

P.S. в принципе вполне могла бы иметь место функция try_json_decode(); которая бы распарсивала тот json, что может и дропала (возвращала) необработанное. Но это не пробелема стандарта.

qnikst ★★★★★
()
Ответ на: комментарий от bk_

А что, сайт используют только через веб-интерфейс? Просто это одна из распространенных дыр, и не один сайт через нее поломали.

Tark ★★
()
Ответ на: комментарий от Slavaz

В ущерб удобности, говоришь? Миллионы разработчиков на С работают, и им удобно, а у остальных сверлит в одном месте от этого.

А потом кто-то прибегает, пишет стандарт, http://www.ietf.org/rfc/rfc4627.txt, в котором говорит:

JavaScript Object Notation (JSON) is a lightweight, text-based, language-independent data interchange format.

language-independent

, но не рассматривает наличие \0 в строке и в грамматике. И после этого все хором, перекрикивая друг дружку кричат, что это правильно, и что \0 - это костыль и пережит 640-килобайтных времен.

bk_ ★★
() автор топика
Ответ на: комментарий от tiandrey

Уже пятый аналитик говорит, что \0 в строке в протоколе - это плохо и то, что мой парсер - гавно. Вопрос: почему? Дяди не осилили null-terminated строки и усердно пытаются доказать обратное?

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

\0 достаточно плохо совместим с С-быдлокодерами и приводит к печальным последствиям связанным с безопасностью

vasily_pupkin ★★★★★
()
Ответ на: комментарий от note173

Моя точка зрения в том, что:

* во-первых, нужна возможность в самой строке экранировать нулевой символ по аналогии с тем, как экранируются двойные кавычки; но это не связано с проблемой в первом посте - это продолжение следующего утверждения:

* во-вторых, \0 должен останавливать парсер. Именно останавливать, а не делать die() с ошибкой. То есть, \0 есть аналог PHP_EOF.

Именно эти два нюанса я не встретил в http://www.ietf.org/rfc/rfc4627.txt, о чем и хочу спросить у его разработчика.

bk_ ★★
() автор топика
Ответ на: комментарий от vasily_pupkin

Прочти еще раз суть моего вопроса. У меня как раз таки есть \0. И php мою быдлостроку с \0 на конце пропарсить не может. Приходится trim-ом этот символ удалять.

Конечно, мой быдлокод наверняка не сравнится с твоим по любому критерию.

bk_ ★★
() автор топика
Ответ на: комментарий от Breton

Абсолютный оффтоп: если не секрет, почему у тебя такой ник?

beresk_let ★★★★★
()
Ответ на: комментарий от vasily_pupkin

Потому что это стандарт, а не твоя писулька на лоре, которую каждый местный аналитик может интерпретировать как ему угодно.

bk_ ★★
() автор топика
Ответ на: комментарий от vasily_pupkin

Такой толстый смайликовый тролль, что аж уныло. die() - это условно-названная функция фатальной ошибки парсера.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Миллионы разработчиков на С работают, и им удобно, а у остальных сверлит в одном месте от этого.

не скажу за всех, но лично мне это сильно неудобно. Чудовищно неудобно. Я вынужден работать в рамках доктрины «у строки есть символ конца строки: \0». И изменить эту доктрину я не в силах :( Она «вшита» в C-компиляторы. С удовольствием работал бы строками, которые «знали» бы свою кодировку и длину вне зависимости от контента.

, но не рассматривает наличие \0 в строке и в грамматике.

Да, и это правильно. '\0' - это Си-специфичное правило. Зачем его тянуть в остальные языки, в которых давно отказались от этого условия конца строки? В конце концов, на Си свет клином не сошёлся.

И после этого все хором, перекрикивая друг дружку кричат, что это правильно, и что \0 - это костыль и пережит 640-килобайтных времен.

Да. Разве они не правы? По крайней мере, я отношусь к их стану и был бы рад услышать аргументированные ответы, почему такие «крикуны» не правы.

Slavaz ★★★★★
()
Ответ на: комментарий от bk_

Прочти еще раз грамматику. json текст не оканчивается на \0. Прочти еще раз сообщения об ошибках вменяемых парсеров. Прочти еще раз php — Unexpected control character found. Он может её пропарсить и сообщает что там инвалидное говно.

Почему разработчики грамматики не сделали \0? А зачем он там? Чем он отличается от любого другого говна? Почему в том же C \0 не валиден? Можешь тоже у разработчиков спросить ))

vasily_pupkin ★★★★★
()
Ответ на: комментарий от bk_

Нулевой символ можно и как \u0000.

А остановка парсера при первом \0 — странная идея. Опять же, в строках может встретиться нулевой байт в utf-16. В utf-8 \0 вообще быть не может, в utf-16 \0 как отдельный символ не существует, соответственно появление его на конце строки делает весь текст невалидным. Как предлагаешь решить эту проблему?

note173 ★★★★★
()
Ответ на: комментарий от bk_

нет это не проблема стандарта, это проблема твоей дурной башки. В стандарту не важно какой тип строк будет использовать программист, хоть null-terminated, хоть length-prefixed, хоть linked-list над чарами. И стандарт не должен заботиться о том, что чья-то дурья башка не приемлет ничего кроме null-terminated. А ты если бы не был дурнем, должен был бы из С-строки «выкусывать» данные и использовать их у себя.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

корректировка: получаешь в php С-строку => переводишь ещё в native строку => парсишь.

Почему ты считаешь, что строка S в С это тоже самое, что строка S+null в php мне совершенно не понятно.

qnikst ★★★★★
()
Ответ на: комментарий от bk_

миллионы разрабочтиков использют pascal строки, миллионы разрабочтиков используют java-строки, миллионы разрабочтиков используют StringBuilder(Buffer) и C# аналоги, миллионы разрабочтиков используют php строки, много разработчиков используют String в haskell, там же ByteString/Text. И все они могут хотеть использовать сериализацию в JSON, и стандарт общий для всех, он позволяет всем им пользоваться библиотеками для их строк и передавать сериализованные данные и десерелиализовать в другом языке. Но нет! тут приходит принц на белом коне bk_ и он спасёт их всех, он научит их счастью, и все будут ходить строем и использовать null terminated C strings, и всё равно, что эти json данные могут быть частью другой строки, и пофиг, что они будут в середине текстового файла! МЫ используем только null-terminated strings, всегда и везде мы!

qnikst ★★★★★
()

Ну не пользуйся php, делов то. Искренне не понимаю людей, выбирающих этот legacy-язык для написания нового кода.

PolarFox ★★★★★
()
Ответ на: комментарий от Slavaz

По крайней мере, я отношусь к их стану и был бы рад услышать аргументированные ответы, почему такие «крикуны» не правы.

Я уже несколько раз повторял это выше, но скажу еще раз.

Открываем http://www.ietf.org/rfc/rfc4627.txt

JavaScript Object Notation (JSON) is a lightweight, text-based, language-independent data interchange format.

language-independent

Есть два вариант, что имел ввиду автор: language как ЯП или как человеческий язык.

Если ЯП, то это мой ответ на твое утверждение

Да, и это правильно. '\0' - это Си-специфичное правило. Зачем его тянуть в остальные языки, в которых давно отказались от этого условия конца строки? В конце концов, на Си свет клином не сошёлся.

Далее. Так как это *стандарт*, а стандарт должен *по максимуму* описывать все возможные ситуации, по минимуму оставляя на домысел «имплементорам» стандарта.

Мое мнение таково, что если я пишу стандарт, то я стараюсь покрыть все возможные случаи. Это как разработка архитектуры приложения. А отсутствие указаний в стандарте, что делать, когда встречается нулевой символ - меня лично удивляет, поскольку для языка С это *стандартный* универсальный формат строки.

Далее небольшой домысел, но для большего взаимопонимания напишу: если бы я писал стандарт, то я бы автоматически расписал правило поведения при встрече \0 - это на уровне интуиции. Отсюда вопрос: почему автор стандарта json не проработал это правило?

Ведь *самое очевидное и грамотное решение* - сделать \0 аналогом EOF. Но нет: EOF оно принимает как должное, а \0 - как ошибочный символ.

А по поводу удобства использования \0-терминированных строк: лично мне очень удобно, так как указатель на строку - это сама строка, и больше ничего! Все остальное:

* локаль берется из LC_ALL

* длина берется из strlen

В твоем случае на любую строку идет расход как минимум 2 байта на локаль + 4, а то и 8 байт на длину. Итак, пустая строка жрет 6-10 байт.

Да, на каком-нибудь высокоуровневом ЯП, который живет в одном потоке и убивается со временем, типа php - такие жертвы простительны.

Но в низкоуровневых ЯП это - херня. На то они и низкоуровневые. Ведь там ты можешь сделать для своего приложения такой формат строки и использовать его, пожалуйста! Но представь, сколько геморроя прибудет тебе, если ты захочешь сделать это стандартом во всей С среде.

Подводя итог, твоя абстракция хороша для относительно небольшого количества строк (или на блейд-серверах со 128 ГБ ОЗУ). Представим, что вся Linux-среда, в том числе ядро, оперируют строками в виде твоей структуры. Тогда как ты сделаешь аналог NSMutableString из Cocoa или struct strbuf из git? Нужен параметр capacity.

Твоя абстракция протекает, когда мне нужна строковый буфер, а не строка в чистом виде. А строка в чистом виде без примесей и есть null-terminated строка. А строковый буфер уже будет хранить, например, длину и емкость.

Потом, человеку куда проще читать предложения, зная что в конце стоит точка, а имея в начале предложения число, обозначающее количество слов в предложении. Если ты заговорил об интуитивной понятности, то знание наперед, сколько символов предстоит читать - нужно компьютеру, а не человеку.

Коротко говоря, твоя абстракция хороша в одном примере и плоха в другом. А null-terminated строки - универсальны и интуитивно понятны, в этом их прелесть.

bk_ ★★
() автор топика
Ответ на: комментарий от note173

А остановка парсера при первом \0 — странная идея. Опять же, в строках может встретиться нулевой байт в utf-16

парсер работает с символами, а не байтами

wota ★★
()
Ответ на: комментарий от PolarFox

Я бы это гогно не выбрал, если бы он не использовался там, где надо деплоить приложение.

bk_ ★★
() автор топика

А вот обработка ошибок в PHP действительно дерьмо. Почему оно не выдало ексепшен или каким-либо иным образом не заявило об ошибке парсинга, молча выдав NULL? В конце концов это наверняка корректный вариант парсинга, к примеру, пустого json.

PolarFox ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.