Как правильно обработать NUL символ?

256
23 августа 2021, 18:40

Удаленно хранятся данные в ASCII.

Пример данных (во втором столбце в 16ой системе для понимания):

1) ]�1�*..�          5D 83 31 91 2A 12 00 D4
2) ]�1�*.�           5D 83 31 91 2A 1C 00 B9
3) ]�1�*‑��         5D 83 31 91 2A 1E FF F0
4) ]�1�*­.�           5D 83 31 91 2A 1F 0E F9

Я получаю эти данные, преобразую их в 16ые при помощи функции bin2hex() и сохраняю в таблице. В другом месте вытаскиваю из таблицы, преобразую обратно функцией hex2bin() и отправляю на обработку.

Пункты 1 и 2 содержат '00' (NUL символ), который никак не отображается в PHP, хотя и идет в счетчик символов var_dump(). В итоге записи, которые содержат этот символ не могут корректно обработаться. Насколько я смог понять, это связано с тем, что NUL символ не учитывается/теряется/удаляется/не может обработаться php (в редакторе даже не отображается <0x00>).

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

Совершенно зашел в тупик. Буду рад решению данной проблемы или идеям в какую сторону копать. Удаленные данные изменять нет возможности.

Код отвечающий за отправку в MQ строки такого вида "]�1�*..�":

    $rcvQueue = self::getInstance()->openQueue(MQ_REASON_QUERY_ID);
    if (!$rcvQueue instanceof MQObject) {
        throw new \Exception(' $rcvOueue не MQObject - не получилось открыть очередь $rcvQueue');
    }
    $getOpts = [
        'Version' => MQSERIES_MQGMO_VERSION_2,
        'Options' => [MQSERIES_MQGMO_FAIL_IF_QUIESCING, MQSERIES_MQGMO_WAIT],
        'WaitInterval' => $waitInterval, //10 sec
        'MatchOptions' => [MQSERIES_MQMO_MATCH_CORREL_ID]
    ];
    $MQMD = [
        'MsgId' => MQSERIES_MQMI_NONE,
        'CorrelId' => $messageId
    ];
    $inMessage = $rcvQueue->get($getOpts, $MQMD);
    if ($inMessage instanceof MQMessage)   {
        $oData = new \SimpleXMLElement($inMessage->data());
    } else {
        throw new \Exception(' MQ сервер вернул $inMessage который не MQMessage ');
    }
Answer 1

PHP имеет несколько вариантов функций, выполняющих одно и то же действие. При обработке бинарных данных Вам стоит обращать внимание на описание функций, которые Вы используете. В частности, среди встроенных функций PHP есть помеченные как "бинарно-безопасная", т.е. корректно обрабатывающая двоичные данные и не пытающаяся как-либо их интерпретировать. Например, strcasecmp — Бинарно-безопасное сравнение строк без учета регистра.

Не забудьте также о возможности обращаться к любому байту в строке как элементу массива.

В то же время если Вы хотите далее обрабатывать данные как строку обычными функциями PHP, Вам действительно необходимо избавиться от 0x00 в ее теле. Для этого можно воспользоваться функцией str_replace():

$cleanString = str_replace("\0", '', $originalString);

На самом деле сложно дать какие-либо более конкретные рекомендации без уточнения по интерпретации пресловутых "данных", которые "обрабатываются". Уточните, может быть это данные в какой-либо конкретной кодировке, отличной от ASCII? И именно из-за этого у Вас проблемы с их обработкой, а вот если знать кодировку и использовать mbstring, то проблем не будет? И, кстати, среди функций mbstring есть и mb_detect_encoding — Определение кодировки символов - попробуйте прогнать свои входные данные через нее, вдруг она определит кодировку и "эти данные" обретут вменяемый смысл?

Answer 2

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

Суть проблемы:

На сайте происходит взаимодействие с IBM WebSphere MQ через PHP библиотеку https://github.com/rstmpw/ibmmq с использованием pecl расширения mqseries https://github.com/php/pecl-networking-mqseries.

Общение происходит через сообщения с уникальными генерируемыми MsgId состоящими из байтов. По-умолчанию генерация происходит в pecl расширении на основании текущих даты и времени. И здесь начинается сама проблема - в некоторые моменты генерируются MsgId, которые содержат нулевые байты, причем иногда они идут сплошным потоком до 12 недель.

Расширение mqseries работает с сишными null-терминированными строками и при копировании строки прекращает обработку на первом нулевом байте (файл mqseries_helper.c, макрос MQSERIES_SETOPT_RESBYTES, строка 87).

Соответственно, MsgId обрезается и возможности корректно его обработать не остается.

Решение:

Генерация MsgId со стороны сайта.

У меня на проекте есть обертка над библиотекой rstmpw/ibmmq для основных методов работы с ней. Нас интересует отправка сообщения в MQ:

public static function putMessageToMQ($outMessage)
{
    $sendQueue = self::getInstance()->openQueue(MQ_SEND_QUERY_ID);
    if (!($sendQueue instanceof MQObject)) {
        throw new \Exception('$sendQueue не MQObject - не получилось открыть очередь отправки сообщения');
    }
    $outMessage->property('MsgId', hex2bin('414d5120') . openssl_random_pseudo_bytes(20));
    $sendQueue->put($outMessage);
    $messageId = $outMessage->property('MsgId');
    return !empty($messageId) ? $messageId : false;
}

$sendQueue - объект класса MQObject из библиотеки.

$outMessage - объект класса MQMessage из библиотеки.

Интересующая нас строка в которой мы заранее задаём свойство MsgId для нашего сообщения отправляемого в MQ:

$outMessage->property('MsgId', hex2bin('414d5120') . openssl_random_pseudo_bytes(20));

Из документации MQ часть hex2bin('414d5120') - "AMQ " обязательна. А через openssl_random_pseudo_bytes мы генерируем псевдослучайную последовательность байт необходимой длины (в моем случае было 24 байта). Базируйтесь на своем MsgId.

Отступление:

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

Также ещё раз спасибо ответу andreymal, который помог обратить внимание на конкретную область.

READ ALSO
Доступ к mysql через php скрипт в linux

Доступ к mysql через php скрипт в linux

Решил наконец освоить что-то новое, перешел на linuxПеренёс файлы проекта на ноутбук, развернул xampp, открываю проект, и вижу, что данные из таблиц...

166
Передать значение URL в Iframe

Передать значение URL в Iframe

Есть сайтСделана костылем отправка формы(СМS древняя)

184
Регистрация и авторизация в мобильном приложении, подходит ли для этого RESTfull

Регистрация и авторизация в мобильном приложении, подходит ли для этого RESTfull

Как правильно реализовать регистрацию и авторизацию в мобильном приложении, подходит ли для этого RESTful веб приложение, какой протокол для...

96