10

i'm rewriting a soap web service from .net to php. by default, php is giving me tags that look like this:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"><SOAP-ENV:Header><ns1:FindAllCategories/></SOAP-ENV:Header><SOAP-ENV:Body><ns1:FindAllCategoriesResponse><ns1:FindAllCategoriesResult><ns1:ArtistCategoryDto>

etc...

but i need this:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><FindAllCategoriesResponse xmlns="http://tempuri.org/"><FindAllCategoriesResult><ArtistCategoryDto>

This is similar to the question here: PHP AND SOAP. Change envelope however i'd like to not hack it the way he did. Also, i am creating a soap service that will be consumed by an existing iphone app, not using PHP to consume a soap service using SoapClient. The iphone app just parses the raw xml and i can't change the iphone app right now.

Community
  • 1
  • 1
fregas
  • 3,192
  • 3
  • 25
  • 41
  • 1
    Can you tell what specifically you don't like with the other solution? Where do you see a shortcomming? Also "I just need this to work" is hard to accomplish, because the Iphone App obviously has a shortcomming. Maybe it's not the only one, so how could somebody answer your question if not guessing only? – hakre Dec 30 '11 at 20:58
  • Sure. So a couple of problems. First, I am building a soap service, and the example is using a soap client. I am not using a php soap client at all--the app is the client. Second, I don't like using RegEx to hack the xml. I'd rather do it with some native api, or if absolutely necessary, using an xml parser to parse the namespaces, but I dont' know how to get a hold of the response to parse the XML. The app definitely has a shortcoming, because its not using soap the way it was intended. But its the way it was build because there were no good soap parsers at the time. – fregas Jan 03 '12 at 16:17

2 Answers2

5

After re-reading what it is you want and searching through the php documentation here is my solution and a couple of assumptions that I made

Assumptions

  • If I'm correct you are aware that the SOAP prefix itself isn't the issue (you are allowed to use any prefix as long as it is consistent).
  • We need to create a work-around for this specific Iphone app that makes use of a (xml) parser that currently can not be modified/upgraded by you

What do you want?

  1. You would like to use a native API to alter the SOAP prefix
  2. You want to catch the SoapServer response so you can alter the SOAP prefix before it is being returned

Solution

  1. Currently there is no native SoapServer API method to alter the SOAP prefix
  2. You can catch the SoapServer response and process the response either by regex or xml parser see example below
<?php

// Create you parse function - Regex
function SoapServerRegexParser($input)
{
    // $input contains your XML Response
    // Do str_replace or preg_replace   
    $request = preg_replace({do replace});

    //return modified output to client
    return $request;
}

// OR create you parse function - Regex XML Parser
function SoapServerXMLParser($input)
{
    // $input contains your XML Response
    // Use any xml parser that you would like   
    $xml = new DOMDocument();
    $xml->formatOutput = true;
    $xml->preserveWhiteSpace = false;
    $xml->loadXML($input);
    //Do replacement have a looke at: DOMNode::replaceChild

    //return modified output to client
    return $xml->saveXML();
}

// Make php buffer all output
// Send all output to a callBack function

// Replace 'SoapServerRegexParser' with the callback function name of choice
ob_start('SoapServerRegexParser'); //buffer output and set callback function

// Create SoapServer
$server = new SoapServer('wsdlfile.wsdl');
$server->handle(); //Handle incoming request
ob_end_flush(); //Release buffer, but send through callback function first

?>

This should do the trick, I haven't created the regex part or the actual xlm node replacement but I figure you can do that yourself

hakre
  • 193,403
  • 52
  • 435
  • 836
Cecil Zorg
  • 1,478
  • 13
  • 15
  • Actually, I do need the namespace prefixes to be very specific. The iphone app is looking for them. However, if i can change the output in the buffer the way you are doing here, I think i'm good. So instead of: ob_start('SoapServerRegexParser'); I would use: SoapServerXMLParser'); ... if i wanted to parse it using xml? – fregas Jan 06 '12 at 22:09
  • Yes that is correct. Have a look at http://nl2.php.net/manual/en/domnode.replacechild.php on how to replace a child node. If the strings you are searching for, and you want to replace them, I think it's easier to just use str_replace – Cecil Zorg Jan 07 '12 at 09:55
  • Very cool - just what I needed to quickly write a flexible soap server. – Olaf Aug 23 '16 at 13:27
2

If you don't want to use Regular expressions, I've done this quick class implementation which uses DomXPath and DomDocument to cleanup the XML and append the namespace attribute at the node level.

<?php

public BetterSoapClient extends SoapClient {

    public function __construct($wsdl, $options = null) {
        parent::__construct($wsdl, $options);
    }

    public function __doRequest($request, $location, $action, $version) {

        $dom = new DOMDocument('1.0');

        // loads the SOAP request to the Document
        $dom->loadXML($request);

        // Create a XPath object
        $path = new DOMXPath($dom);

        // Search the nodes to fix
        $nodesToFix = $path->query('//SOAP-ENV:Envelope/SOAP-ENV:Body/*', null, true);

        // Remove unwanted namespaces
        $this->fixNamespace($nodesToFix, 'ns1', 'http://tempuri.org/');

        // Save the modified SOAP request
        $request = $dom->saveXML();

        return parent::__doRequest($request, $location, $action, $version);
    }

    public function fixNamespace(DOMNodeList $nodes, $namespace, $value) {
        // Remove namespace from envelope
        $nodes->item(0)
                ->ownerDocument
                ->firstChild
                ->removeAttributeNS($value, $namespace);

        //iterate through the node list and remove namespace

        foreach ($nodes as $node) {

            $nodeName = str_replace($namespace . ':', '', $node->nodeName);
            $newNode = $node->ownerDocument->createElement($nodeName);

            // Append namespace at the node level
            $newNode->setAttribute('xmlns', $value);

            // And replace former node
            $node->parentNode->replaceChild($newNode, $node);
        }
    }
}
Christophe Eblé
  • 8,071
  • 3
  • 33
  • 32
  • this doesn't help me. i'm build a soap web service, using SoapServer, not consuming another service using SoapClient. I have modified the original post to make this more clear, although I did originally mention it and also in the comments. – fregas Jan 04 '12 at 19:46