Donnerstag, 19. November 2015

[PHP+GDB] Debugging in PHP

Writing a server in Python that does SOAP and WSDL a simple PHP-client should be able to do requests. The WSDL is selfmade-generated in version 1.1. The analyzer on wsdl-analyzer.com is telling me that everything is OK. In fact I already have 3 clients in Python running, using SOAPpy, suds (jurko-edition) and twisted.web.soap (which is using SOAPpy). 

And 2 in PHP, one using pear SOAP and the other using the internal PHP SOAP.


First I tried the internal one and got "Could not find any usable binding services in WSDL." After lots of trying I wanted to test another library and found pear SOAP.
That one didn't work at first too.
 <?php
    require_once 'SOAP/Client.php';
    $wsdl = new SOAP_WSDL("http://localhost:8080/soap/?wsdl", array(), False);
    $client = $wsdl->getProxy();
This crashed in (/usr/share/php/)SOAP/WSDL.php:798 on 
eval($proxy);
Looking at the $proxy-string I saw generated PHP-code with classes and methods and so on, utilizing the names specified in the WSDL for variables etc. To bad those names contained dots ('.') because of how the WSDL-generator read its information from decorated classes and methods. Changing the generator to convert dots to double-underscorse ('__') helped.

And the normal PHP SOAP?
It still doesn't work. And still throws the same error message.
The code:
<?php
    $client = new SoapClient("http://localhost:8080/soap/?wsdl",
        array(
            'trace'=>True,
            'soap_version' => SOAP_1_1,
            'cache_wsdl' => WSDL_CACHE_NONE,
            'exceptions' => True,
            'features' => SOAP_SINGLE_ELEMENT_ARRAYS | SOAP_USE_XSI_ARRAY_TYPE)
    );
The error: 
PHP Fatal error:  SOAP-ERROR: Parsing WSDL: Could not find any usable binding services in WSDL. in soap_test_client.php on line 9
PHP Fatal error:  Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Could not find any usable binding services in WSDL. in soap_test_client.php:9
Stack trace:
#0 soap_test_client.php(9): SoapClient->SoapClient('http://localhos...', Array)
#1 {main}
  thrown in soap_test_client.php on line 9
Trying with a wrong URI I get a different error:
PHP Fatal error:  SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:8080/SOAP/?wsdl' : failed to load external entity "http://localhost:8080/SOAP/?wsdl"
So the WSDL gets loaded from the server. The request is also visible in the server-log. Why is it having parsing problems?
Debugging this would be nice.
How to debug PHP code? Xdebug should work, I am told by looking for answers to this question. But it just works for own PHP code afaik. As it is not crashing in my but in internal compiled PHP code this is useless. GDB should be helpful I thought but a didn't really find anything useful about that combination. Some said GDB can't be utilized for debugging own PHP code. That I didn't want.
So I just tried it and got the following working:
gdb --args php -e soap_test_client.php
This will start GDB wanting to debug "php" with arguments set for it to "-e soap_test_client.php".
Useful commands for now are:
r | run -- run the program
b | break -- set a breakpoint
c | continue -- continue from a breakpoint
n | next -- go to the next line
s | step -- "step" inside the current line
q | quit -- quit the debugger
l | list -- list code lines
p | print -- print a variable or expression
Before running the program it is recommended to set at least one breakpoint. But where? The error-message is a good indication. Because the used PHP comes compiled the source code isn't there. On Debian apt-source helps:
apt-get source php5
That will download the source and patches and extract those in the current directory. On previous trail-and-error I already had found the function where the breakpoint should be and GDB wanted the source files being at:
/build/php5-MTNO_I/php5-5.6.15+dfsg
 On Specifying Source Directories from the GDB documentation it is said:
For example, suppose an executable references the file /usr/src/foo-1.0/lib/foo.c, and our source path is /mnt/cross. The file is first looked up literally; if this fails, /mnt/cross/usr/src/foo-1.0/lib/foo.c is tried; if this fails, /mnt/cross/foo.c is opened; if this fails, an error message is printed.
Assuming the PHP script is in ~/projects/php_soap_test/ and from the same directory the debugger is started the source files should be in ~/projects/php_soap_test/build/php5-MTNO_I/ 
With PHPs source download via apt-source being in ~/projects/php_source/ a symbolic link woks:
ln -s ~/projects/php_source/php5-5.6.15+dfsg ~/projects/php_soap_test/build/php5-MTNO_I/
The creation of missing directories might be needed (mkdir ~/projects/php_soap_test/build/php5-MTNO_I/). (There is also the dir-command in GDB that adds a directory to the path where sources are searched for.)

In the source directory of PHP a grep to the error message results in one interesting hit:
$ grep -Rn "Could not find any usable binding services in WSDL"
ext/soap/php_sdl.c:1171:                soap_error0(E_ERROR, "Parsing WSDL: Could not find any usable binding services in WSDL.");
Looking in the code and searching for a start the function in which this grep-hit shows up is:
731: static sdlPtr load_wsdl(zval *this_ptr, char *struri TSRMLS_DC)
There are now different possibilities to set the breakpoint:
b load_wsdl
break php_sdl.c:731
break /build/php5-MTNO_I/php5-5.6.15+dfsg/ext/soap/php_sdl.c:731
With the breakpoint set the program can be started/run. It will stop at the breakpoints where list can be used to see the code around the current point. Sometimes it is necessary to not just 'run' to the next line but to step inside the (mostly) function-call and trace what happens there. Looking at variables via print is also possible which includes contents of structs and dereferencing pointers and so on.


And now back inside the debugger finding out why PHP SOAP doesn't like my WSDL...