[PHP] SoapServer resultaat opvangen

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
Ik heb een SoapServer gemaakt in PHP... alles werkt naar tevredenheid, op 1 puntje na... ik wil graag het resultaat van de SoapRequest dat teruggestuurd wordt naar de client opslaan in mijn database... echter, de SoapServer handle()-functie geeft deze mogelijkheid niet...

de handle()-functie verwerkt de SoapRequest en retourneert direct een resultaat XML naar de client... so far so good... dit is eenvoudig op te vangen met ob_start()....

in het geval dat de client een geldige XML stuurt, is dit geen enkel probleem... mijn probleem treedt echter op als de client een invalid XML stuurt (hetzij qua lay-out hetzij qua content)... normaal gesproken stuurt de handle()-functie naast de XML ook een keurige HTTP-errorcode (bijv. code 500, internal server error)... als ik ob_start() gebruik, gaat deze error-code echter verloren, en krijg je altijd een code 200-OK terug...

- weet iemand een manier om de handle()-functie zijn werk te laten doen, inclusief de gewenste HTTP-codes, en toch de inhoud van de XML op te slaan voordat deze wordt teruggestuurd naar de client?

of

- weet iemand een manier om via een ob_start achtige manier zowel de inhoud van de XML af te vangen, als ook de HTTP-errorcodes af te vangen, om deze na opslag in de database door te sturen naar de client.

Acties:
  • 0 Henk 'm!

  • TJVB
  • Registratie: Januari 2008
  • Laatst online: 16-09 16:02
ob_start laat de headers toch door? Zie php.net/ob_start ( While output buffering is active no output is sent from the script (other than headers) )
Kun je anders een stukje code laten zien?

Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
ik heb een beetje zitten schuiven met code om van alles te proberen.. had het eerst ongeveer zo:
PHP:
1
2
3
4
5
6
7
8
9
10
$serverclass = "DefinitionClass";
$server = new SoapServer($config["wsdl"]);
$server->soap_defencoding = 'UTF-8';
$server->setClass($serverclass);

ob_start();
$result = $server->handle();
ob_end_clean();

print $result;


nu heb ik zoiets:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ExtendedSoapServer extends SoapServer {
    public function handle($request = null) {
        if (is_null($request)) {
            $request = $GLOBALS['HTTP_RAW_POST_DATA'];
        }           

        ob_start();
        $result = parent::handle($request);
        ob_end_clean();
        
        print $result;
                                   // ook return $result geprobeerd ipv print
    }
}

$serverclass = "DefinitionClass";
$server = new ExtendedSoapServer($config["wsdl"]);
$server->soap_defencoding = 'UTF-8';
$server->setClass($serverclass);

$server->handle();


in beide gevallen wordt keurig de XML geretourneerd aan de client, maar wel altijd met HTTPcode 200 OK.... terwijl ik in geval van een error een XML terug wil sturen met daarin de error omschrijving en een HTTPcode die niet gelijk is aan 200, maar bijv. 500....

Acties:
  • 0 Henk 'm!

  • TJVB
  • Registratie: Januari 2008
  • Laatst online: 16-09 16:02
Geeft de handle (zonder ob_start) wel een andere header mee? Of moet je die zelf verzorgen?

Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
ja, als ik de hele ob_start zooi weghaal:
PHP:
1
2
3
4
5
6
$serverclass = "DefinitionClass"; 
$server = new SoapServer($config["wsdl"]); 
$server->soap_defencoding = 'UTF-8'; 
$server->setClass($serverclass); 

$server->handle();


dan krijg je keurig een XML terug én een header...

edit:

weet iemand misschien of ergens de broncode van de standaard SoapServer klasse te vinden is?

[ Voor 15% gewijzigd door P.O. Box op 02-05-2013 15:47 ]


Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
om te beginnen: ik denk dat mijn ob_end_clean vervangen moet worden door ob_flush... dan worden de headers wel doorgezonden...

echter heb ik nu nog een probleem met het opslaan van het resultaat in mijn database vanwege de volgende bug:
https://bugs.php.net/bug.php?id=49513

ik vind het heel belangrijk dat een malformed xml-error ook netjes wordt opgeslagen in de database... dus ik wil na de $server->fault() actie wel gewoon door kunnen gaan :(

Acties:
  • 0 Henk 'm!

  • TJVB
  • Registratie: Januari 2008
  • Laatst online: 16-09 16:02
Bij de pagina over de fault methode ( http://nl1.php.net/manual/en/soapserver.fault.php ) staat in het commentaar een manier om zelf een response te maken zonder die functie.

Daarmee kun je wel wat met je response doen.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
<?php
header("Content-Type: text/xml");
header("Status: 200");
die("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\\"http://schemas.xmlsoap.org/soap/envelope/\\">
  <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
      <faultcode>500</faultcode>
      <faultstring>".$ex->getMessage())."</faultstring>
    </SOAP-ENV:Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>");
?>

Acties:
  • 0 Henk 'm!

Verwijderd

Het lijkt mij het makkelijkst om gewoon de raw post af te vangen,
via $HTTP_RAW_POST_DATA of via file_get_contents("php://input")


deze raw post kun je als argument aan de 'handle' method van de SoapServer meegeven nadat je deze hebt opgeslagen.

Acties:
  • 0 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Haal de ?> eens weg, sommige servers kunnen daar niet mee overweg als er bijvoorbeeld nog een blanko regel of spaties erachteraan komen.

Tip: gebruik van ?> meteen afleren

En resultaten var_dump-en , debug optie inbouwen altijd handig.

[ Voor 16% gewijzigd door BoringDay op 07-05-2013 20:20 ]


Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
Verwijderd schreef op dinsdag 07 mei 2013 @ 11:11:
Het lijkt mij het makkelijkst om gewoon de raw post af te vangen,
via $HTTP_RAW_POST_DATA of via file_get_contents("php://input")


deze raw post kun je als argument aan de 'handle' method van de SoapServer meegeven nadat je deze hebt opgeslagen.
dat is het probleem hier niet... de ingekomen data wil ik ook wel opslaan, maar dat is geen enkel probleem... het probleem is de data die ik terugstuur... die wil ik ook opslaan... zodat ik altijd kan nazien welke XML de aanvrager terug heeft onvangen...
BoringDay schreef op dinsdag 07 mei 2013 @ 20:18:
Haal de ?> eens weg, sommige servers kunnen daar niet mee overweg als er bijvoorbeeld nog een blanko regel of spaties erachteraan komen.

Tip: gebruik van ?> meteen afleren

En resultaten var_dump-en , debug optie inbouwen altijd handig.
ik ken het principe van de ?> weglaten... dat is absoluut het probleem nu niet... ik krijg geen errors op mijn code ofzo...

het probleem is dat ik bij het uitvoeren van de SoapServer-->handle() functie de inhoud wil opslaan, maar daarbij worden de verzonden http headers ook tegengehouden en wordt altijd een code 200 geretourneerd...

ik heb bijvoorbeeld deze methode toegepast:
https://blog.mayflower.de...r-PHP5-for-debugging.html
(handig voor mensen die ook de returncontent willen opslaan, maar het niet boeit dat er dan altijd een code 200 wordt geretourneerd...

verder heb ik een apart stukje code gemaakt voor malformed-xml... hier dacht ik handig gebruik te maken van de functie SoapServer->fault (om zo volgens de WSDL definitie (iedere klant kan bij ons in theorie (en in de praktijk) een eigen WSDL definitie hebben) een geldige soapfault terug te geven)... bij soapfault is echter het probleem dat de executie van iedere volgende code wordt gestopt... de returncontent van soapfault kan zelf met ob_start-ob_end niet worden opgevangen.... ook fijn... maar dat is een subprobleem...
TJVB schreef op vrijdag 03 mei 2013 @ 09:51:
Bij de pagina over de fault methode ( http://nl1.php.net/manual/en/soapserver.fault.php ) staat in het commentaar een manier om zelf een response te maken zonder die functie.

Daarmee kun je wel wat met je response doen.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
<?php
header("Content-Type: text/xml");
header("Status: 200");
die("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\\"http://schemas.xmlsoap.org/soap/envelope/\\">
  <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
      <faultcode>500</faultcode>
      <faultstring>".$ex->getMessage())."</faultstring>
    </SOAP-ENV:Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>");
?>
ik wil dat de SOAPfault xml wordt gemaakt naar aanleiding van de WSDL... dus kan niet "hard" een soapfault neerzetten...

-----
aanvulling:

ik dacht de headers op te vragen "die reeds gestuurd zijn", daar dan de status code uit op te vragen en dan geforceerd die status code opnieuw te sturen...
maar als ik na mijn handle-functie de functie apache_response_headers() aanroep (of varianten daarvan, er zijn meerdere php-functies hiervoor) dan krijg ik:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
array(5) {
  ["X-Powered-By"]=>
  string(9) "PHP/5.3.9"
  ["Set-Cookie"]=>
  string(44) "PHPSESSID=c9qsiqp7pml3m75b98crk5h023; path=/"
  ["Expires"]=>
  string(29) "Thu, 19 Nov 1981 08:52:00 GMT"
  ["Cache-Control"]=>
  string(62) "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
  ["Pragma"]=>
  string(8) "no-cache"
}

en dus geen statuscode... grr...

de functie http_response_code() kan ik niet testen/gebruiken... heb versie 5.3.9 en moet minimaal 5.4 hebben... ik gebruik USB Webserver 8.5 om te testen, en heb daar nog geen mogelijkheid ontdekt om te upgraden naar 5.4....

[ Voor 30% gewijzigd door P.O. Box op 08-05-2013 12:19 ]


Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
opgelost ! ... of toch niet.... ik heb nu dit:

PHP:
1
2
3
4
5
6
function OnShutdownSaveResult() {
    global $db;
    $db->saveXMLresult(ob_get_contents());
}

register_shutdown_function("OnShutdownSaveResult");


heel weinig code dus...
ik maak gebruik van de register_shutdown_function (http://php.net/manual/en/...shutdown-function.php)... deze functie wordt aangeroepen als het script wordt gesloten... dus OOK bij een die(), exit(), of zo'n soap-handle() of soap-fault() functie.
de output en de response-headers zijn op dat moment blijkbaar al gestuurd, want die ontvang ik keurig zoals ik wil in SoapUI...

ik heb echter een dikke maar, waar ik graag jullie mening over hoor:
in de functie roep ik zomaar ob_get_contents() aan, zonder een ob_start gedaan te hebben... blijkbaar geeft deze aanroep als resultaat alle reeds ge-outputte xml... dus precies de XML die ik in mijn database wil opslaan... in de documentatie van ob_get_contents() lees ik echter niets over deze functionaliteit... sterker nog, er staat "This will return the contents of the output buffer or FALSE, if output buffering isn't active."... hetgeen ik lees als: "als er geen ob_start gedaan is, krijg je FALSE terug"...

ik twijfel nu heel erg of ik dit nu wel zo kan laten staan.. ik weet geen andere manier om de reeds geoutputte xml op te vangen, maar ik ben bang dat bij de eerste de beste upgrade van PHP (heb nu versie 5.3.9) al niet meer gaat werken...

wat vinden jullie van mijn oplossing en hebben jullie misschien nog ideeen?

Acties:
  • 0 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
P.O. Box schreef op woensdag 08 mei 2013 @ 16:15:
opgelost ! ... of toch niet.... ik heb nu dit:

PHP:
1
2
3
4
5
6
function OnShutdownSaveResult() {
    global $db;
    $db->saveXMLresult(ob_get_contents());
}

register_shutdown_function("OnShutdownSaveResult");


heel weinig code dus...
ik maak gebruik van de register_shutdown_function (http://php.net/manual/en/...shutdown-function.php)... deze functie wordt aangeroepen als het script wordt gesloten... dus OOK bij een die(), exit(), of zo'n soap-handle() of soap-fault() functie.
de output en de response-headers zijn op dat moment blijkbaar al gestuurd, want die ontvang ik keurig zoals ik wil in SoapUI...

ik heb echter een dikke maar, waar ik graag jullie mening over hoor:
in de functie roep ik zomaar ob_get_contents() aan, zonder een ob_start gedaan te hebben... blijkbaar geeft deze aanroep als resultaat alle reeds ge-outputte xml... dus precies de XML die ik in mijn database wil opslaan... in de documentatie van ob_get_contents() lees ik echter niets over deze functionaliteit... sterker nog, er staat "This will return the contents of the output buffer or FALSE, if output buffering isn't active."... hetgeen ik lees als: "als er geen ob_start gedaan is, krijg je FALSE terug"...

ik twijfel nu heel erg of ik dit nu wel zo kan laten staan.. ik weet geen andere manier om de reeds geoutputte xml op te vangen, maar ik ben bang dat bij de eerste de beste upgrade van PHP (heb nu versie 5.3.9) al niet meer gaat werken...

wat vinden jullie van mijn oplossing en hebben jullie misschien nog ideeen?
Persoonlijk zou ik het wel netjes vinden als je een aangemaakt object weer vrijgeeft.
unset($db).
Zoals ik eerder aangegeven heb, ik weet niet waar je soap client op draait.
Maar sommige servers raken in de war met ?> aan het eind van een php script als er nog blanco tekens erachter aankomen. Die fout heb ik ook in het begin gemaakt, dus laat ze dan ook altijd achterwege.

Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
ik begrijp het.. heb dat probleem eigenlijk nog nooit gehad (althans, had binnen paar seconden dat dan wel gevonden)... de ?> in de voorbeelden heb ik overigens niet zelf daar neergezet... dat doet de forum-software voor me als ik de code=php tag gebruik :)

Acties:
  • 0 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
P.O. Box schreef op woensdag 08 mei 2013 @ 16:49:
ik begrijp het.. heb dat probleem eigenlijk nog nooit gehad (althans, had binnen paar seconden dat dan wel gevonden)... de ?> in de voorbeelden heb ik overigens niet zelf daar neergezet... dat doet de forum-software voor me als ik de code=php tag gebruik :)
Ok, ik had er ook nooit rekening mee gehouden tot je keer tegenaan loopt.
Maar zo eenvoudig kom je dat probleem niet op het spoor omdat de meldingen geheel niet een relevante aanwijzing geven. Je kan je daar zo uren wel niet dagen op blind staren en afvragen waarom werkt het hier wel en daar niet.

Acties:
  • 0 Henk 'm!

Verwijderd

Als je simpelweg de output wil loggen is het het makkelijkst om de handle() method the extenden ...

zoiets dus :

class SoupServer extends SoapServer
{

function handle()
{
ob_start();
parent::handle();
$sResponse = ob_get_clean();
// @todo ... write a better log method
file_put_contents(PH_ROOT.'/data/soaplog.txt', $sResponse);
echo $sResponse;
}
}

En als je echt een andere HTTP header mee wil sturen in geval van een SoapFault kun je altijd afvangen of je response een instance van SoapFault is en dan een andere HTTP code teruggeven.

Eigenlijk is het teruggeven van een code 500 trouwens niet echt logisch, gezien je transport laag (http) verder geen fouten vertoont. Hiervoor is juist de SoapFault in het leven geroepen.

[ Voor 32% gewijzigd door Verwijderd op 09-05-2013 00:18 ]


Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
Verwijderd schreef op donderdag 09 mei 2013 @ 00:09:
Als je simpelweg de output wil loggen is het het makkelijkst om de handle() method the extenden ...

zoiets dus :

.....
dat was het probleem ook niet...
Verwijderd schreef op donderdag 09 mei 2013 @ 00:09:

En als je echt een andere HTTP header mee wil sturen in geval van een SoapFault kun je altijd afvangen of je response een instance van SoapFault is en dan een andere HTTP code teruggeven.

Eigenlijk is het teruggeven van een code 500 trouwens niet echt logisch, gezien je transport laag (http) verder geen fouten vertoont. Hiervoor is juist de SoapFault in het leven geroepen.
dat dacht ik in eerste instantie ook.. maar het default gedrag van het SoapServer object is wel dat er een code 500 wordt teruggegeven MET de SoapFault... en ik wilde het default gedrag behouden...

overigens weet ik nog steeds niet zeker of het zomaar aanroepen van ob_get_contents() zonder een ob_start() gedaan te hebben "legaal" is... alle voorbeelden die ik kan vinden beginnen altijd met een ob_start.... voor zover ik begrijp is de output buffer in levels opgebouwd... je kunt meerdere ob_start's uitvoeren... de ob_get_contents neemt dan de output buffer sinds de laatst uitgevoerde ob_start waar nog geen ob_end voor gedaan is... zodra je output doet ZONDER eerst ob_start gedaan te hebben wordt er uiteraard ook een output-buffer aangemaakt door PHP, die pas aan het eind van je script (of bij een flush) wordt ge-output... het lijkt er op dat ob_get_contents ook deze "hoofd"-output-buffer aan kan spreken... alleen als er echt nog 0,0 output is geweest dan is het resultaat van ob_get_contents false:

PHP:
1
2
3
4
5
6
7
if (ob_get_contents() === false) {
   print "false";
} else {
   print "bevat waarde";
}

// dit geeft als resultaat: false


PHP:
1
2
3
4
5
6
7
8
print "a";
if (ob_get_contents() === false) {
   print "false";
} else {
   print "bevat waarde";
}

// dit geeft als resultaat: bevat waarde


mijn vraag is dus nu:
maak ik nu gebruik van een functionaliteit van ob_get_contents of maak ik misbruik van een bug in ob_get_contents en is bij de eerstvolgende PHP update mijn code waardeloos? Iemand enig idee?

Acties:
  • 0 Henk 'm!

Verwijderd

ob_get_clean() geeft geen error als je niet eerst ob_start() hebt aangeroepen, enkel een lege string ... eigenlijk is er altijd een output buffer actief, die wordt geflushed nadat je script klaar is met executen.

dus je hoeft je eigenlijk nergens zorgen om te maken mbt errors en je gebruik van ob_get_clean() is dus eigenlijk zoals het bedoeld is.

Acties:
  • 0 Henk 'm!

  • maketime
  • Registratie: Februari 2015
  • Laatst online: 04-10-2024
Erg late to the party, maar hopelijk heeft iemand hier wat aan (getest in PHP 7.2) :

code:
1
2
3
4
5
6
7
8
9
10
11
<?php
// hiervoor heb je de soapServer gemaakt 

ob_start();
$soapServer->handle();
$body      = ob_get_clean();
$headers = headers_list();

// indien er nu geen fouten zijn opgetreden kun je nu met de body en headers doen wat je wilt
// als er WEL een fout is opgetreden, dan moet je onderstaande workaround gebruiken
?>


Er zit een bug in PHP soap die ervoor zorgt dat zodra er een fout optreedt handle() direct output returned en alles na handle negeert. Hier is een workaround voor. Zorg ervoor dat je in je class die de soap afhandelt - dus het object dat je doorgaf aan $soapServer->setObject() - een volgende magic method zit die ervoor zorgt dat ie gewone fouten omzet naar SoapFaults.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
   /**
     * Zorgt ervoor dat als er errors worden gegooid (Throwable) dat die worden omgevormd naar een soapfault,
     * want "gewone" errors zorgen ervoor dat je na handle() niets meer kunt
     * 
     * @param string $function
     * @param array $args
     * @throws SoapFault
     */
    public function __call($function, $args)
    {
        // dump($function); dump($args);

        if (!in_array($function, get_class_methods($this), true))
        {
            throw new SoapFault('CLIENT', 'function doesnt exist');
        }

        try {
            $function(...$args);
        }
        catch (Throwable $e)
        {
            throw new SoapFault('SERVER', 'Internal Error');
        }
    }
?>
Pagina: 1