Сообщения веб-службы PHP усекаются буферизацией вывода

Я разрабатываю веб-службу на PHP, где я использую стандартный класс PHP SoapServer с файлом WSDL, предоставленным моим клиентом, но мне также нужно использовать буферизацию вывода, чтобы «убрать» ответ SOAP, который производит PHP (добавляя некоторые отсутствующие пространства имен , добавляя xmlns="" к нескольким тегам и другие мелкие, но необходимые детали).
Проблема в том, что когда я использую явную буферизацию вывода (ob_start(), ob_end_flush() ...), любое исходящее сообщение с моего сервера усекается, независимо от его размера ( Я тестирую в диапазоне около 2КБ-50КБ). Когда я не использую явную буферизацию вывода, клиент получает полный SOAP-ответ, но, поскольку упомянутые мной детали не были исправлены, формат не совсем правильный.
Вот код моего сервера:

if (!extension_loaded("soap")) {
    dl("php_soap.dll");
}

//Disable wsdl caching
ini_set("soap.wsdl_cache_enabled", "0");

//Get SOAP message from client
$input_msg = file_get_contents("php://input");

//Create SoapServer object with url to the .wsdl file
$server = new SoapServer(WSDL_FILE_URL, array('encoding' => 'UTF-8', 'actor' => SERVER_URL));

//Set the class that will handle every possible request form the client
$server->setClass("ServerHandler",$input_msg);

//Before I let the server handle the request and return the response to the client,
//I turn on output buffering so I can fix its format
ob_start(NULL, 1<<20);
$server->handle(); //let the server handle the request and write the response to the buffer
$soapXml = ob_get_contents(); //I get the response from the output buffer
ob_end_clean(); //clear the output buffer of its content

//this function does all those small format fixes to the SOAP response that PHP produces
$soapXml=fixSoapResponseFormat($soapXml);

echo $soapXml;//put the correct SOAP response in the output buffer
ob_end_flush();//flush the output buffer and turn off buffering
flush();//this supossedly flushes lower level buffers, just in case

Как видите, я попытался использовать высокое значение параметра $chunk_size для ob_start() и на всякий случай добавить flush() после ob_end_flush(). Я также пытался установить output_buffering = On в php.ini (который предположительно включает неограниченный выходной буфер) и установить LimitRequestFieldSize и LimitRequestLine на 5500000 в Apache, но безрезультатно.
Я пытался сбросить на диск содержимое $soapXml после буфер очищается, и сообщение завершено, поэтому я предполагаю, что проблема должна быть в строке echo $soapXml; или в очистке буфера сразу после нее.
Я использую Apache с PHP 5.3 для запуска сервера и SoapUI 5 как клиент, чтобы протестировать его. Я также использую WS-Security во всех сообщениях между сервером и клиентом (если это уместно).

Кто-нибудь знает, как решить эту проблему с буферизацией сообщений SOAP? Может быть, есть другой способ поймать и изменить ответ SOAP, который создает PHP, прежде чем он будет отправлен клиенту? Буду благодарен за любой совет.


person Miguel A. Romero    schedule 29.10.2014    source источник
comment
Куда вы выводите $soapXml? Прямо в браузере? Или это приложение CLI?   -  person Valentin Rodygin    schedule 29.10.2014
comment
Я вывожу прямо в браузер, так сказать, но на самом деле это клиентская программа (SoapUI). Моя серверная программа получает запрос SOAP от клиента через http и также возвращает ответ через http. Этот ответ - результат, с которым у меня проблемы.   -  person Miguel A. Romero    schedule 29.10.2014
comment
Возможно, браузер некорректно отображает теги xml и вы их не экранируете. Вы сказали, что успешно сбросили документ на диск. Вы пытались открыть исходный код страницы?   -  person Valentin Rodygin    schedule 29.10.2014
comment
Это хорошее предложение, но я не думаю, что здесь проблема, потому что SoapUI не совсем браузер и должен правильно отображать неэкранированный XML. На самом деле, когда я не использую буферизацию вывода, SoapUI получает полное XML-сообщение и отображает его должным образом, и я ничего не экранирую. В любом случае, я попытаюсь создать свою собственную быструю клиентскую программу, чтобы протестировать сервер и на всякий случай выгрузить ответ на диск.   -  person Miguel A. Romero    schedule 29.10.2014
comment
На всякий случай, если это неясно, я успешно сбросил сообщение ($soapXml) на диск на сервере, что означает, что данные предположительно завершены до того, как они будут записаны в выходной буфер, по крайней мере.   -  person Miguel A. Romero    schedule 29.10.2014
comment
Тогда я подозреваю, что с fixSoapResponseFormat что-то не так. Вы можете опубликовать это здесь?   -  person Valentin Rodygin    schedule 29.10.2014
comment
Ммм... это действительно странно. Если я удалю строку $soapXml=fixSoapResponseFormat($soapXml);, сообщение будет отправлено правильно. Однако, если я оставлю эту строку, сообщение будет отправлено усеченным, но если я сброшу его на диск на сервере сразу после отправки, оно будет завершено и правильно изменено fixSoapResponseFormat()! Эта функция состоит из цикла, в котором я делаю ряд замен в сообщении с помощью $soapXml=str_replace($original_tag, $replacement_tag, $soapXml);. Я всегда заменяю целые теги XML их более полными эквивалентами, чтобы избежать нежелательных замен.   -  person Miguel A. Romero    schedule 29.10.2014
comment
Вот и все. Если вы можете опубликовать свою реализацию здесь, мы придем к лучшему/надежному решению.   -  person Valentin Rodygin    schedule 29.10.2014
comment
Хорошо, я наконец нашел проблему: HTTP-заголовок для ответа был сгенерирован в строке $server->handle();, и когда я вызвал fixSoapResponseFormat(), сообщение стало больше, но я не обновил данные о размере в заголовке. Я опубликую рабочий код в качестве ответа, чтобы люди могли легко его найти. Спасибо большое за то, что наставили меня на правильный путь, Валентин! Что я могу сделать, чтобы улучшить вашу репутацию? (Я новичок на этом сайте)   -  person Miguel A. Romero    schedule 29.10.2014
comment
Добро пожаловать, Мигель! Возможно, вы сможете проголосовать за один из моих комментариев.   -  person Valentin Rodygin    schedule 29.10.2014


Ответы (1)


Очевидно, проблема заключалась в том, что я позволял PHP генерировать SOAP-сообщение с его http-заголовком в строке $server->handle();, но затем я перехватывал его в буфер и менял его размер без обновления Content-Length http-заголовка, поэтому сообщение было отправлено, но урезано до исходного размера.
Итак, вот правильно работающий код:

if (!extension_loaded("soap")) {
    dl("php_soap.dll");
}

//Disable wsdl caching
ini_set("soap.wsdl_cache_enabled", "0");

//Get SOAP message from client
$input_msg = file_get_contents("php://input");

//Create SoapServer object with url to the .wsdl file
$server = new SoapServer(WSDL_FILE_URL, array('encoding' => 'UTF-8', 'actor' => SERVER_URL));

//Set the class that will handle every possible request form the client
$server->setClass("ServerHandler",$input_msg);

//Before I let the server handle the request and return the response to the client,
//I turn on output buffering so I can fix its format
ob_start(NULL, 1<<20);
$server->handle(); //let the server handle the request and write the response to the buffer
$soapXml = ob_get_contents(); //I get the response from the output buffer
ob_end_clean(); //clear the output buffer of its content

//this function does all those small format fixes to the SOAP response that PHP produces
$soapXml=fixSoapResponseFormat($soapXml);

header('Content-Length: '.strlen($soapXml));//**UPDATE THE LENGTH HEADER**

echo $soapXml;//put the correct SOAP response in the output buffer
ob_end_flush();//flush the output buffer and turn off buffering
flush();//this supossedly flushes lower level buffers, just in case

Большое спасибо Валентину Родыгину за огромную помощь в поиске источника моих бед!

person Miguel A. Romero    schedule 29.10.2014