PHP SOAP-клиент, который понимает составные сообщения? - PullRequest
13 голосов
/ 14 июля 2009

Есть ли такая зверька? Простой SOAP-клиент , который поставляется с PHP, не понимает составные сообщения. Заранее спасибо.

Ответы [ 7 ]

12 голосов
/ 14 июля 2009

Нативный класс PHP SoapClient не поддерживает составные сообщения (и строго ограничен во всех вопросах WS- *), и я также думаю, что ни одна из написанных на PHP библиотек NuSOAP nor Zend_Soap может работать с такого рода SOAP-сообщениями.

Я могу придумать два решения:

  • расширяет класс SoapClient и перезаписывает метод SoapClient::__doRequest(), чтобы получить фактическую строку ответа, которую можно затем проанализировать по своему усмотрению.

    class MySoapClient extends SoapClient
    {
        public function __doRequest($request, $location, $action, $version, $one_way = 0)
        {
            $response = parent::__doRequest($request, $location, $action, $version, $one_way);
            // parse $response, extract the multipart messages and so on
        }
    }
    

    Хотя это может быть несколько сложно, но стоит попробовать.

  • использовать более сложную клиентскую библиотеку SOAP для PHP. Первое и единственное, что мне приходит в голову, это WSO2 WSF / PHP , который включает SOAP MTOM, WS-Addressing, WS-Security, WS-SecurityPolicy, WS-Secure Conversation и WS-ReliableMessaging за счет необходимость установить собственное расширение PHP.

3 голосов
/ 06 августа 2013

Следуйте советам rafinskipg из документации PHP:

Поддержка MTOM. Добавьте этот код в ваш проект:

<?php 
class MySoapClient extends SoapClient
{
    public function __doRequest($request, $location, $action, $version, $one_way = 0)
    {
        $response = parent::__doRequest($request, $location, $action, $version, $one_way);
        // parse $response, extract the multipart messages and so on

        //this part removes stuff
        $start=strpos($response,'<?xml');
        $end=strrpos($response,'>');    
        $response_string=substr($response,$start,$end-$start+1);
        return($response_string);
    }
}

?>

Тогда вы можете сделать это

<?php
  new MySoapClient($wsdl_url);
?>
3 голосов
/ 14 августа 2009

Используя вторую идею С. Герига, мы отлично сработали.

В большинстве случаев у вас есть только одно сообщение, упакованное в сообщение MIME MultiPart. В этих случаях возникает исключение « SoapFault: [Клиент] выглядит так, как будто мы не получили документ XML ». Здесь следующий класс должен нормально работать:

class MySoapClient extends SoapClient
{
    public function __doRequest($request, $location, $action, $version, $one_way = 0)
    {
        $response = parent::__doRequest($request, $location, $action, $version, $one_way);
        // strip away everything but the xml.
        $response = preg_replace('#^.*(<\?xml.*>)[^>]*$#s', '$1', $response);
        return $response;
    }
}
2 голосов
/ 29 ноября 2016

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

class SoapClientExtended extends SoapClient
{
    /**
     * Sends SOAP request using a predefined XML
     *
     * Overwrites the default method SoapClient::__doRequest() to make it work
     * with multipart responses.
     *
     * @param string $request      The XML content to send
     * @param string $location The URL to request.
     * @param string $action   The SOAP action. [optional] default=''
     * @param int    $version  The SOAP version. [optional] default=1
     * @param int    $one_way  [optional] ( If one_way is set to 1, this method
     *                         returns nothing. Use this where a response is
     *                         not expected. )
     *
     * @return string The XML SOAP response.
     */
    public function __doRequest(
        $request, $location, $action, $version, $one_way = 0
    ) {
        $result = parent::__doRequest($request, $location, $action, $version, $one_way);

        $headers = $this->__getLastResponseHeaders();

        // Do we have a multipart request?
        if (preg_match('#^Content-Type:.*multipart\/.*#mi', $headers) !== 0) {
            // Make all line breaks even.
            $result = str_replace("\r\n", "\n", $result);

            // Split between headers and content.
            list(, $content) = preg_split("#\n\n#", $result);
            // Split again for multipart boundary.
            list($result, ) = preg_split("#\n--#", $content);
        }

        return $result;
    }
}

Это работает, только если вы инициализируете SoapClientExtended с параметром trace => true.

1 голос
/ 20 марта 2012

Просто чтобы добавить больше света к предыдущим предложенным шагам. Вы должны получать ответ в следующем формате

    --uuid:eca72cdf-4e96-4ba9-xxxxxxxxxx+id=108
Content-ID: <http://tempuri.org/0>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body> content goes here </s:Body></s:Envelope>
--uuid:c19585dd-6a5a-4c08-xxxxxxxxx+id=108--

Просто используйте следующий код (я не так хорош в регулярных выражениях, поэтому использую строковые функции)

 public function __doRequest($request, $location, $action, $version, $one_way = 0)
{
    $response = parent::__doRequest($request, $location, $action, $version, $one_way);
    // strip away everything but the xml.
    $response = stristr(stristr($response, "<s:"), "</s:Envelope>", true) . "</s:Envelope>";
return $response;
}
0 голосов
/ 19 октября 2018

Немного проще ИМХО

class SoapClientUnwrappedXml extends SoapClient
{

    const SOAP_ENVELOPE_REGEXP = '/^<soap:Envelope[^>]*>(.*)<\/soap:Envelope>/m';

    /**
     * Sends SOAP request using a predefined XML.
     *
     * Overwrites the default method SoapClient::__doRequest() to make it work
     * with multipart responses or prefixed/suffixed by uuids.
     *
     * @return string The XML Valid SOAP response.
     */
    public function __doRequest($request, $location, $action, $version, $one_way = 0): string
    {
        $result = parent::__doRequest($request, $location, $action, $version, $one_way);
        $headers = $this->__getLastResponseHeaders();

        if (preg_match('#^Content-Type:.*multipart\/.*#mi', $headers) !== 0) {
            preg_match_all(self::SOAP_ENVELOPE_REGEXP, $result, $resultSanitized, PREG_SET_ORDER, 0);
            $result = $resultSanitized[0][0] ?? $result;
        }

        return $result;
    }
}
0 голосов
/ 23 июня 2017

Код только что работал для меня с опцией "trace => true".

Спасибо, что поделились!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...