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)
Ответ на: комментарий от bk_

>Возможность появления \0 в середине строки - лишняя сущность, не несущая в себе никакого практического смысла. Все остальные языке наворачивают сложности, позволяя это делать.

4.2. Присвоение \0 дополнительного значения — вот это лишняя сущность.

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

Вот как раз передача \0 в качестве последнего символа строки парсеру json никакого смысла не несёт. Не передавай \0 в качестве последнего символа строки парсеру json.

А пример простой. Злые какеры насовали в файл \0-ей в используемой тобой кодировке и сделали твоей программе больно.

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

О, да, админу локалхоста усераться не нужно. А вот пересылать лишних 100 ГБ траффика в день - на нагруженном проекте - плохо.

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

Я специально передаю strlen+1 для пущей безопасности

Одни костыли порождают другие костыли.

Моя позиция такова, что в utf-8 и в asciiz символ \0 - символ конца строки.

В _кодировке_ не бывает «символа конца строки». Кроме того, про UTF-8 тебе уже не раз писали. http://en.wikipedia.org/wiki/UTF-8

\0 - не такой же символ, как и все

Кто сказал?

Отлично, но мне никто до сих пор не может ответить на простой вопрос: какой смысл от \0 в середине строки?

Оккам, например. Зачем какому-то символу выделять особую сущность, если это не требуется?

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

У тебя Си головного мозга.

О! Наконец-то диагноз озвучен! :)

KRoN73 ★★★★★
()

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

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

О, да. Зато у остальных php головного мозга :)

Это ты про Java, Haskell, Форт, Паскаль, JavaScript, Ruby, Python и далее по списку? Если одиночка плюнет на мир, то мир утрётся, если мир плюнет на одиночку, то он утонет.

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

Ага, его код 0xfeff1f00 в utf-16. То есть, уже нельзя говорить о том, что, встречая _байт_ 0x00 нужно что-то сразу завершать.

Кстати, «\0» в utf-16 будет 0xfeff0000.

Ну это к разговору о работе со строками в стиле Си. Можно назначить специальный смысл для U+0000. Но это будет введение новой сущности. Зачем?

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

Я специально передаю strlen+1 для пущей безопасности.

То есть ты на языке, где можно выйти за границы массива, специально выходишь за границы массива?

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

Если пошла философия, то сущность сама по себе значения не имеет. Имеет значения ОДЗ конкатенаций всех сущностей. А при введении ограничения на \0, условивишись, что он означает конец строки, и все - мы уменьшаем ОДЗ. Убираем это ограничение - и ОДЗ увеличивается.

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

То есть ты на языке, где можно выйти за границы массива, специально выходишь за границы массива?

Подумай еще раз, в это каком месте я выхожу за границы массивы.

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

Как раз таки бритва говорит не городить лишних сущностей

Особая трактовка \0 - это лишняя сущность.

Возможность появления \0 в середине строки - лишняя сущность

Пока \0 — обычный символ, он может появляться где угодно. Его поведение не порождает особенностей. Как только мы начинаем его рассматривать особо — это новая сущность.

Все остальные языке наворачивают сложности, позволяя это делать.

Как раз введение особой обработки символа в языках, где она не нужна — это особая сущность. Скажем, языки со счётной строкой. Копирование. Мы читаем в начале строки её длину и делаем N циклов копирования. Или сразу строковую пересылку, если позволяет архитектура процессора. А теперь, вдруг, ты хочешь сделать посимвольное копирование с проверкой каждого байта? А жирно не будет?

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

То есть, уже нельзя говорить о том, что, встречая _байт_ 0x00 нужно что-то сразу завершать.

Речь шла о utf-8 и asciiz.

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

Допустим (скорее всего ты сам неявно это подразумеваешь), что у тебя строка в ASCII, то есть массив символов, причём внутреннее представление таково, что байт однозначно соответствует символу и наоборот. То есть если массивсимволов[strlen] соответствует последнему символу, то массивсимволов[strlen+1] любой херне, которая случайно оказалась за массивсимволов в памяти.

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

А ничего, что даже задрипанный 8086 имел операнды работы с asciiz-строками? То ты говоришь про php, то про такты процессора.

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

base64 предлагал не я, на чистом json-rpc оверхеда не будет. А вот делать отдельный скрипт, с авторизацией и всем прочим просто чтобы «не было \0» не нужно

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

У меня строка - это сами символы + нулевой символ. strlen дает длину «самих символов».

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

Нулевой символ к строке не относится. Это байт, который в си следует сразу за ASCII строкой ради совместимости с древними функциями. В самой строке этого символа нет и тупо быть не может из-за того, что в си такие строки.

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

В фортране (в котором не было динамических строк) некоторыми компиляторами пустые символы после инициализации забивались \0. При этом было валидно записать в сечение после \0.

Так что это сущность уже присутствовала, как свершившийся факт. Лет эдак за 15 до твоей любимой стандартизованной сишечки.

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

А ничего, что даже задрипанный 8086 имел операнды работы с asciiz-строками?

А ничего, что в 8086 есть MOVSB/MOVSW?

А ничего, что даже на процессорах, где строковые операции начинают сосать (с 80386 и выше), пересылка байта (которая неизбежна при поиске терминатора) будет происходить раз в 6 медленнее, чем пересылка слова (4 цикла вместо одного + штрафы за отсутствие выравнивания).

То ты говоришь про php, то про такты процессора.

С позиции «Си — наше всё», конечно, непонятно, как его правилам могу несоответствовать столь разные языки :D Ведь он один идёт в ногу, это все остальные идут не в ногу!

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

«строка» у тебя - это абстракция, в структуру которой мы оба помним. А у меня «строка» - это последовательность символов в utf-8 + нулевой байт. Кони в вакуумах типа utf-16, utf-32 меня не интересуют.

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

А у меня «строка» - это последовательность символов в utf-8 + нулевой байт.

Короче ты передаёшь в пыхпых 4 символа вместо 3 и жалуешься, что он обработал 4 символа, а не 3.

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

А у меня «строка» - это последовательность символов в utf-8 + нулевой байт

А у меня — это последовательность символов в utf-8, в середине которой есть U+0000. Я с помощью него передаю что-то нужное. Кто прав?

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

А ничего, что в 8086 есть MOVSB/MOVSW?

Есть. И что?

А ничего, что даже на процессорах, где строковые операции начинают сосать (с 80386 и выше), пересылка байта (которая неизбежна при поиске терминатора) будет происходить раз в 6 медленнее, чем пересылка слова (4 цикла вместо одного + штрафы за отсутствие выравнивания).

1. Пруф, что начинают сосать.

2. Наверняка читается все слово, а не по одному байту из памяти.

3. Это все давно происходит в кэше, там «4 цикла вместо одного» уже совсем не так работают.

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

>> Какой смысл \0 в середине строки? ЗАЧЕМ создавать такую строку

Чтобы обрабатывать JSON

Дооо. Смысл всех смыслов.

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

Да, что он неправильно обработал 4 символ, пытаясь вытянуть из него не то, что нужно.

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

Да, что он неправильно обработал 4 символ, пытаясь вытянуть из него не то, что нужно.

> Ну да, а на слаке можно удалить любую либу без зависимых от неё пакетов, в то время как эти 10-20 пакетов не будут работать

Знаешь Миша, а можно еще хер себе дверью специально прищемить и потом всем рассказывать про неправильные двери.

PolarFox ★★★★★
()

В общем, всем спасибо за дискуссию. Вашу позицию я понял, теперь мне надо обдумать свою с учетом новой информации с 6 страниц.

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

Пруф, что начинают сосать.

OMFG. Растактовку посчитай. Или на функции копирования строки в ассемблерном выводе компилятора оптимизированный вариант оцени.

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

Ога. А как мы проверяем, что один из байтов там — нулевой?

Это все давно происходит в кэше

Уже не только «Си головного мозга», но и «Pentium головного мозга.

там „4 цикла вместо одного“ уже совсем не так работают.

В лучшем и идеальном случае не будет штрафов. А вот от циклов ты никуда не денешься. В общем, к предыдущим двум диагнозам ещё и „незнание матчасти“ добавляется. И эти люди ещё лезут критиковать PHP...

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

А как мы проверяем, что один из байтов там — нулевой?

Да уж явно быстрее, чем читать по байту из ОЗУ.

Уже не только «Си головного мозга», но и «Pentium головного мозга.

Конструктивные доводы уже закончились.

В общем, к предыдущим двум диагнозам ещё и „незнание матчасти“ добавляется.

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

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

Конструктивные доводы уже закончились.

Ещё в начале темы.

Если ты такой знаток матчасти, ну так ткни пальцем в ман, где это расписано

Без понятия, где оно в нынешних манах. Я-то всё это учил ещё по бумажной литературе.

Все, о чем говорил я, четко расписано в соответствующих стандартах.

Ты, кажется, потерялся. Ты отвечал на комментарий по архитектуре процессора. А в «стандартах» там вполне определённо даётся растактовка операций и влияние всех особых случаев.

Только люди на пальцах это уже больше 15 лет не считают. Уже во времена Watcom 10 и MSVC4 компиляторы Си научились делать это лучше хороших программистов на маш. коде. Именно в те времена я и завязал с низкоуровневым программированием, перейдя на высокоуровневое (да, Си тогда считался ЯВУ).

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

Да уж явно быстрее, чем читать по байту из ОЗУ.

И уж явно медленнее, чем просто прочесть длину строки откуда-нибудь из памяти.

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

Если ты такой знаток матчасти, ну так ткни пальцем в ман, где это расписано.

Танненбаум Э. Архитектура Компьютера.

aedeph_ ★★
()

lua ещё не было?

Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> json = require("json")
> t = [[ { "one":1, "two":2 } ]]
> o = json.decode(t)
> table.foreach(o,print)
one	1
two	2
> t = [[ { "one":1, "two":2\0\0 } ]]
> o = json.decode(t)
/usr/share/lua/5.1/json/decode.lua:106: Invalid JSON data
stack traceback:
	[C]: in function 'assert'
	/usr/share/lua/5.1/json/decode.lua:106: in function </usr/share/lua/5.1/json/decode.lua:104>
	(tail call): ?
	(tail call): ?
	stdin:1: in main chunk
	[C]: ?

DELIRIUM ☆☆☆☆☆
()

Кстати, перловый парсер тоже ругается. Все null-bytes внутри объекта json являются ошибкой. С учетом того, что в RFC ни слова по этому поводу - фильтрацию входных данных должен делать человек, а не парсер. У меня только вопрос, как запихнуть строку с нулл-символом в json объект? :)

#!/usr/bin/perl

use strict;
use warnings;

use JSON::XS;
use Data::Dumper;

my $parser = JSON::XS->new->ascii->allow_nonref(1);
my $json_good = '{"v1":1,"v2":2}' . "\0";
my $json_bad1 = "\0" . '{"v1":1,"v2":2}';
my $json_bad2 = '{"v1":1,"v2":' . "\"2\x00abc\"}";

print Dumper $parser->decode($json_good);

eval {
  print Dumper $parser->decode($json_bad1);
};
print $@ if ($@);

eval {
  print Dumper $parser->decode($json_bad2);
};
print $@ if ($@);

eval {
  print Dumper $parser->decode( $parser->encode({"nul"=>"123\x00abc"}) );
};
print $@ if ($@);
$VAR1 = {
          'v1' => 1,
          'v2' => 2
        };
malformed JSON string, neither array, object, number, string or atom, at character offset 0 (before "\x{0}{"v1":1,"v2":2}") at t_json.pl line 17.
unexpected end of string while parsing JSON string, at character offset 15 (before "\x{0}abc"}") at t_json.pl line 22.
$VAR1 = {
          'nul' => '123abc'
        };
gh0stwizard ★★★★★
()
Ответ на: комментарий от vasily_pupkin

А местный петросян все острит и острит.

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

Я говорю о реальном примере, когда я открыл файл, а там нулевой символ в конце. А пхп зафейлился его парсить, хотя \0 нечитабельный.

чо правда? откуда там \0 в конце?

qnikst ★★★★★
()

хотя в общем-то налицо ещё и особенность парсера, который считает невалидные данные поводом для вылета, несмотря на то, что часть данных была валидной и распаршена полностью. Имеют смысл и другие парсеры:

Напр:

{"foo":"\u0000\u0000\u0000"} > "{\"foo\":\"\\u0000\\u0000\\u0000\"}"
Done "" Object fromList [("foo",String "\NUL\NUL\NUL")]

{"foo":"\u0000\u0000\u0000"} > "{\"foo\":\"\\u0000\\u0000\\u0000\"}\NUL"
Done "\NUL" Object fromList [("foo",String "\NUL\NUL\NUL")]

{"foo":"\u0000\u0000\u0000" > "{\"foo\":\"\\u0000\\u0000\\u0000\"\NUL"
Fail "\NUL" ["}"] "Failed reading: satisfyWith"

выше приведены JSON строка затем побайтный вывод, затем результат парсинга, тут видно, что в первом случае разбирается вся строка, во-втором (лишний \NULL) после сообщения - разбирается строка NULL игнорируется и выводится в списке неразобранного, в третьем случае (кривой JSON) выдаётся ошибка с описанием

qnikst ★★★★★
()

Тред не читай, сразу отвечай: а в чем проблема-то? Если json не валидный, то как он его будет парсить?

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

В мане написано зачем. \0 такой же символ как и любой другой и программист волен выбирать его в качестве разделителя на равне с пробельными символами.

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

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

Да. Потому что сокету сказано отдать из буфера данные размером n, и принимающая сторона считает что получает плайн-текст размером n, но по факту прилетает n-1 текста + символ считающийся нетекстовым. Просто это не валит перл поскольку он поощряет мелкие косяки и несоответствия.

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