Internal class, read_property() and type == BP_VAR_W

php.internals

Michael Wallner

20 years ago
Hi, I'm facing an issue I don't know how to overcome. Short story: I've an object which properties are connected to the internal struct whith get_property() and write_property() object handlers. Now if BP_VAR_W is used to access a property with read_prop() the following works fine: // $this->body is a string $this->body{0} = 'X'; ... but the following does nothing than producing a memleak: $body = &$this->body; $body = 'X'; Also, it seems impossible to connect *values* (longs or string lengths for instance) through that interface, because I cannot tell the zval that the value should point elsewhere... Any advice very much appreciated :) Do I have to disallow referenced writing access? Long story (sorry): static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC) { getObjectEx(http_message_object, obj, object); http_message *msg = obj->message; zval *return_value; #ifdef WONKY ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)+1); #else zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC); if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) { return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC); } #endif if (type == BP_VAR_W) { return_value = &EG(uninitialized_zval); return_value->refcount = 1; return_value->is_ref = 1; } else { ALLOC_ZVAL(return_value); return_value->refcount = 0; return_value->is_ref = 0; } #ifdef WONKY switch (h) #else switch (pinfo->h) #endif { case HTTP_MSG_PROPHASH_TYPE: case HTTP_MSG_CHILD_PROPHASH_TYPE: RETVAL_LONG(msg->type); break; case HTTP_MSG_PROPHASH_HTTP_VERSION: case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION: RETVAL_DOUBLE(msg->http.version); break; case HTTP_MSG_PROPHASH_BODY: case HTTP_MSG_CHILD_PROPHASH_BODY: phpstr_fix(PHPSTR(msg)); RETVAL_PHPSTR(PHPSTR(msg), 0, !return_value->is_ref); break; case HTTP_MSG_PROPHASH_HEADERS: case HTTP_MSG_CHILD_PROPHASH_HEADERS: if (return_value->is_ref) { Z_TYPE_P(return_value) = IS_ARRAY; Z_ARRVAL_P(return_value) = &msg->hdrs; } else { array_init(return_value); zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); } break; case HTTP_MSG_PROPHASH_PARENT_MESSAGE: case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE: if (msg->parent) { RETVAL_OBJVAL(obj->parent); } else { RETVAL_NULL(); } break; case HTTP_MSG_PROPHASH_REQUEST_METHOD: case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD: if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.method) { RETVAL_STRING(msg->http.info.request.method, 1); } else { RETVAL_NULL(); } break; case HTTP_MSG_PROPHASH_REQUEST_URI: if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.URI) { RETVAL_STRING(msg->http.info.request.URI, !return_value->is_ref); } else { RETVAL_NULL(); } break; case HTTP_MSG_PROPHASH_RESPONSE_CODE: case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE: if (HTTP_MSG_TYPE(RESPONSE, msg)) { RETVAL_LONG(msg->http.info.response.code); } else { RETVAL_NULL(); } break; case HTTP_MSG_PROPHASH_RESPONSE_STATUS: case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS: if (HTTP_MSG_TYPE(RESPONSE, msg) && msg->http.info.response.status) { RETVAL_STRING(msg->http.info.response.status, !return_value->is_ref); } else { RETVAL_NULL(); } break; default: #ifdef WONKY return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC); #else RETVAL_NULL(); #endif break; } return return_value; }
-- Michael - <mike(@)php.net> http://dev.iworks.at/ext-http/http-functions.html.gz

Dmitry Stogov

20 years ago
Hi Michael, You should define get_property_ptr_ptr() callback to support such assignments. Thanks. Dmitry.

Michael Wallner

20 years ago
Hi Dmitry Stogov, you wrote:
> Hi Michael, > > You should define get_property_ptr_ptr() callback to support such > assignments.
Yeah, I looked at that, but what do I gain? get_property_ptr_ptr() will be used for both described cases and instead of returning a zval* I'd have to return a zval**, but I could miss the obvious, and if that's the case, don't hesitate to drop me a quick pointer in the right direction! Thanks a lot Dmitry,
-- Michael - <mike(@)php.net> http://dev.iworks.at/ext-http/http-functions.html.gz

Dmitry Stogov

20 years ago
read_property(BP_VAR_W) will never work for all cases. Probably you can return proxy object with get() and set() callbacks, but I am not sure if it will work for references too. So if you can return zval** you should use get_property_ptr_ptr(). Dmitry.

Michael Wallner

20 years ago
Dmitry Stogov wrote:
> read_property(BP_VAR_W) will never work for all cases. > Probably you can return proxy object with get() and set() callbacks, but I > am not sure if it will work for references too. > > So if you can return zval** you should use get_property_ptr_ptr().
Hm... maybe I made not a good start with my question, let me ty again, please: typedef struct { zend_object zo; int i; } my_obj; Would you say it's possible that one can maintain i as class property with write access through references? Thank you,
-- Michael - <mike(@)php.net> http://dev.iworks.at/ext-http/http-functions.html.gz

Sara Golemon

20 years ago
> Hm... maybe I made not a good start with my question, let me ty again,
please:
> > typedef struct { > zend_object zo; > int i; > } my_obj; > > Would you say it's possible that one can maintain i as class > property with write access through references? >
Nope, because PHP variables are loose typed. Say you *did* have some way to bind my_obj->i to a userspace variable (e.g.: $i =& $this->i; ). The next instruction could say $i = 'foo';, then what is the engine supposed to do with that? If you want i to have any visibility in userspace, then it needs to be a zval, and that means it can be modified, and that means that you'll need to be prepared to re-cast it back to the right type later. Kinda sucks, but it sucks for a good reason... -Sara

Michael Wallner

20 years ago
Sara Golemon wrote:
>> Would you say it's possible that one can maintain i as class >> property with write access through references? >> > Nope, because PHP variables are loose typed. > > Say you *did* have some way to bind my_obj->i to a userspace variable > (e.g.: $i =& $this->i; ). The next instruction could say $i = > 'foo';, then what is the engine supposed to do with that? > > If you want i to have any visibility in userspace, then it needs to > be a zval, and that means it can be modified, and that means that > you'll need to be prepared to re-cast it back to the right type > later. Kinda sucks, but it sucks for a good reason...
Uhm... you're so right, thanks, that cleared my mind and makes me remember my first post to the list ;) Thank you,
-- Michael - <mike(@)php.net> http://dev.iworks.at/ext-http/http-functions.html.gz

Marcus Börger

20 years ago
Hello Michael, actually your class could have another object with get/set handlers implemented, nothing more. This could be mapped through the handlers to your int i. marcus Friday, October 28, 2005, 10:44:42 PM, you wrote:
> Sara Golemon wrote:
>>> Would you say it's possible that one can maintain i as class >>> property with write access through references? >>> >> Nope, because PHP variables are loose typed. >> >> Say you *did* have some way to bind my_obj->i to a userspace variable >> (e.g.: $i =& $this->i; ). The next instruction could say $i = >> 'foo';, then what is the engine supposed to do with that? >> >> If you want i to have any visibility in userspace, then it needs to >> be a zval, and that means it can be modified, and that means that >> you'll need to be prepared to re-cast it back to the right type >> later. Kinda sucks, but it sucks for a good reason...
> Uhm... you're so right, thanks, that cleared my mind and makes me > remember my first post to the list ;)
> Thank you, > -- > Michael - <mike(@)php.net> > http://dev.iworks.at/ext-http/http-functions.html.gz
Best regards, Marcus