Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SoapClient object doesn't respect trace property #17401

Open
HeenaBansal20 opened this issue Jan 8, 2025 · 7 comments
Open

SoapClient object doesn't respect trace property #17401

HeenaBansal20 opened this issue Jan 8, 2025 · 7 comments

Comments

@HeenaBansal20
Copy link

HeenaBansal20 commented Jan 8, 2025

Description

HI ,
I am trying to retrieve the properties of soapCLient when _trace property is set to 1. But it did not work.

client = new SoapClient(null,  array('uri' => $url, 'location' => $url,
            "exceptions" => 0, 
            "trace" => 1,
            'stream_context' => stream_context_create(array(
                'http' => array(
                    'header' => 'SomeCustomHeader: value'
                ),
            )),
        ));
<?php

Trying to access properties of soapclient as below :

  const char* cname = ZSTR_VAL(Z_OBJCE_P(object)->name); // this returned soapclient class name
  HashTable* object_properties = Z_OBJPROP_P(object);
  ZEND_HASH_FOREACH_KEY_VAL(object_properties, num_key, str_key, prop) { LOG("prop = %s ", Z_STRVAL_P(prop)); }
 ZEND_HASH_FOREACH_END();// resulted in array of 36 elements but all values are empty.

Resulted in this output:


But I expected this output instead:

055+   class name SoapClient
056+   hash prop =  
057+   hash prop =  
058+   hash prop =  
059+   hash prop =  
060+   hash prop =  
061+   hash prop =  
062+   hash prop =  
063+   hash prop =  
064+   hash prop =  
065+   hash prop =  
066+   hash prop =  
067+   hash prop =  
068+   hash prop =  
069+   hash prop =  
070+   hash prop =  
071+   hash prop =  
072+   hash prop =  
073+   hash prop =  
074+   hash prop =  
075+   hash prop =  
076+   hash prop =  
077+   hash prop =  
078+   hash prop =       
079+   hash prop =  
080+   hash prop =  
081+   hash prop =  
082+   hash prop =  
083+   hash prop =  
084+   hash prop =  
085+   hash prop =  
086+   hash prop =  
087+   hash prop =  
088+   hash prop =  
089+   hash prop =  
090+   hash prop =  
091+   hash prop = ��p1 

PHP Version

PHP 8.4

Operating System

ubuntu2204

@nielsdos
Copy link
Member

nielsdos commented Jan 8, 2025

Something you should know about the properties table is that there's a difference in how dynamic properties and non-dynamic properties are stored.
Dynamic properties are stored directly as a zval in the hash table, and can be read directly.
Non-dynamic properties are stored via an IS_INDIRECT zval. This means that the data inside the zval is a pointer to another zval. This is because the actual storage is inside the properties table, so we need that one indirection.

Fixed code (but could not test ofc as a full reproducer is missing):

const char* cname = ZSTR_VAL(Z_OBJCE_P(object)->name); // this returned soapclient class name
HashTable* object_properties = Z_OBJPROP_P(object);
ZEND_HASH_FOREACH_KEY_VAL(object_properties, num_key, str_key, prop) {
	ZVAL_DEINDIRECT(prop); // This follows the IS_INDIRECT zval pointer to the actual storage (if it is IS_INDIRECT)
	if (Z_TYPE_P(prop) == IS_STRING) LOG("prop = %s ", Z_STRVAL_P(prop));
	else LOG("prop not a string");
}
ZEND_HASH_FOREACH_END();// resulted in array of 36 elements but all values are empty.

Note that the trace option allows you to track the request and response data.
This can be accessed directly without the property table using the following macros:

php-src/ext/soap/php_soap.h

Lines 251 to 254 in cee64ed

#define Z_CLIENT_LAST_REQUEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 32))
#define Z_CLIENT_LAST_RESPONSE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 33))
#define Z_CLIENT_LAST_REQUEST_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 34))
#define Z_CLIENT_LAST_RESPONSE_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 35))

Hope this helps.

@HeenaBansal20
Copy link
Author

@nielsdos , Thanks for replying and I was trying to access this MACRO in our source code and I see that soap ext folder is not visible though I have build php-src code with --enable-soap configure option.

my php-config looks like bowl which shows that I build php-src code with --enable-soap option , However my /usr/local/include/php/ext folder doesn't have soap header files.

php-config
Usage: /usr/local/bin/php-config [OPTION]
Options:
  --prefix            [/usr/local]
  --includes          [-I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib]
  --ldflags           [ -L/usr/local/lib]
  --libs              [  -lrt -lldap -llber -lstdc++ -lrt -lm  -lxml2 -lssl -lcrypto -lsqlite3 -lz -lcurl -lssl -lcrypto -lxml2 -licuio -licui18n -licuuc -licudata -lonig -lsqlite3 -lxml2 -lxml2 -lxml2 -lxml2 -lxml2 -lz -lssl -lcrypto ]
  --extension-dir     [/usr/local/php/modules]
  --include-dir       [/usr/local/include/php]
  --man-dir           [/usr/local/php/man]
  --php-binary        [/usr/local/bin/php]
  --php-sapis         [ cli embed fpm cgi]
  --ini-path          [/usr/local/etc/php]
  --ini-dir           [/usr/local/etc/php/conf.d]
  --configure-options [--prefix=/usr/local --disable-phpdbg --enable-debug --enable-embed --enable-fpm --enable-intl --enable-json --enable-mbstring --enable-mysqlnd --enable-opcache --enable-pcntl --enable-pdo --enable-soap --enable-sockets --with-config-file-path=/usr/local/etc/php --with-config-file-scan-dir=/usr/local/etc/php/conf.d --with-curl --with-gnu-ld --with-ldap --with-mysqli=mysqlnd --with-openssl --with-pcre-valgrind --with-pdo-mysql=mysqlnd --with-pdo-sqlite --with-pear --with-sqlite3 --with-valgrind --with-zlib PKG_CONFIG_PATH=]
  --version           [8.3.14]
  --vernum            [80314]
/usr/local/include/php/ext# ls
cassandra  date  filter  http   json    mbstring  mysqlnd  pdo   random  session    sockets  standard
curl       dom   hash    iconv  libxml  mysqli    pcre     phar  raphf   simplexml  spl      xml

@nielsdos
Copy link
Member

nielsdos commented Jan 8, 2025

Ah right, it appears that header file is private and not public, so that's why it's not installed.
In that case the easiest thing may be to call zend_read_property to read the relevant trace properties.

@HeenaBansal20
Copy link
Author

HeenaBansal20 commented Jan 8, 2025

hmm... I see that config.m4 doesnt have PHP_INSTALL_HEADERS macro and is made private.

However , I did try to use zend_read_property to but that also doesn't seems to work and requestheaders seems to have no value with type_info UNDEF and should be of string type.

 zval* object = EX_OBJ(this);
zval* requestheaders=zend_read_property(object, "__last_request_headers");
ZVAL_DEINDIRECT(requestheaders);
if (Z_TYPE_P(requestheaders) == IS_STRING){
    LOG_D("request headers = %s", Z_STRVAL_P(requestheaders)); 
}

@nielsdos
Copy link
Member

nielsdos commented Jan 8, 2025

zend_read_property takes more arguments than just 2.
Do you have your code somewhere? It's going to be hard to debug this otherwise.

@HeenaBansal20
Copy link
Author

Yeah I defined the zend_property macro on top the php-src zend_property ,

#define _zend_read_property(scope, object, name, name_length)                                                          \
    zend_read_property(scope, Z_OBJ_P(object), name, name_length, 1, NULL)

I have registered the callback for soapcleint:: __dorequest() .. and receives soapclient as the zend_execute_data pointer.
I am able to read all 4 arguments of __dorequest() function and working on zend_execute_data* to get "__last_request_headers" property from that zend_execute_data pointer as below.

void callback_soap_client_dorequest(zend_execute_data* data)
{
    zval *location, *action, *content;

    if (ZEND_CALL_NUM_ARGS(data) < 4) {
        return;
    }

    content = ZEND_CALL_ARG(data, 1);
    location = ZEND_CALL_ARG(data, 2);
    action = ZEND_CALL_ARG(data, 3);
    get_request_headers(data);
}

void get_request_headers(zend_execute_data* data) 
{
    zval* object = EX_OBJ(data);
    zval* tmp;
    php_stream_context* context = NULL;
    zval tmp_zv;

    if (!object || Z_TYPE_P(object) != IS_OBJECT) {
        return;
    }
    const char* cname = ZSTR_VAL(Z_OBJCE_P(object)->name); // this returned soapclient class name 

    zend_class_entry* this_ce = Z_OBJCE_P(object);
    if (this_ce == NULL) {
        return;
    }
    zval* requestheaders = _zend_read_property(this_ce, object, "__last_request_headers", sizeof("__last_request_headers") - 1);
    ZVAL_DEINDIRECT(requestheaders);
    if (Z_TYPE_P(requestheaders) == IS_STRING) {
       LOG_D("request headers = %s", Z_STRVAL_P(requestheaders)); 
   }
}

@HeenaBansal20
Copy link
Author

Additionally , I can see that this_ce is populated right because I can see the first property name as url under properties table..and no of element as 36
image

However , I am unable to retrieve the __lastrequestheader property from zend_read_property. While debugging, I see that my code exits here and exited.

@damianwadley damianwadley changed the title SoapCLient object doesn't respect trace property SoapClient object doesn't respect trace property Jan 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants