# zval "passthrough"?

- Thread: https://externals.io/message/19391
- Original archive: https://news-web.php.net/php.internals/19391
- Started by David Olsson on 2005-10-04T12:14:41+00:00
- Messages: 8
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #19391 — David Olsson — 2005-10-04T12:14:41+00:00

URL: https://externals.io/message/19391#19391

Hello

I'm writing a PHP extension in C++ which need to provide a function 
which will take as a parameter a zval *. This zval * should, when the 
function has completed its task, simply be returned unmodified. How do I 
accomplish this? I've tried something like this:

PHP_FUNCTION(my_func) {
	zval *v;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &v) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	.
	.
	.

	return_value = v;
}

The above doesn't seem to be working at all though. Any ideas? I guess 
it is a really simple task once you known how do actually do it... :-)

Thankful for all help!
David Olsson

---

## #19405 — Sara Golemon — 2005-10-04T16:14:27+00:00

URL: https://externals.io/message/19391#19405 · In reply to: #19391

> I'm writing a PHP extension in C++ which need to provide a function which 
> will take as a parameter a zval *. This zval * should, when the function 
> has completed its task, simply be returned unmodified. How do I accomplish 
> this? I've tried something like this:
>
> return_value = v;
>
There are two problems here:

#1 return_value is just a parameter in the prototype:

void zif_my_func(int ht, zval *return_value, zval **return_value_ptr, zval 
*this_ptr, int return_value_used TSRMLS_DC)

So when you set:   return_value = v;   You are in fact changing where 
return_value points, but you're not doing anything to the contents of the 
return_value zval that the calling scope (the engine) knows about.  You're 
just changing the local pointer to look at another address.

Using return_value_ptr you can both clean up the original memory allocated 
for return_value, and change what the engine knows about return_value since 
you have the pointer to the pointer which the engine uses:

/* Destroy old return_value (prevents leaks) */
zval_ptr_dtor(return_value_ptr);

/* Point return_value at the passed in val */
*return_value_ptr = v;

This brings us to the second problem:

#2 The refcount of v will have been temporarily increased by the engine when 
it was placed on the parameter stack, however when the function exits, that 
refcount will be decremented back down and there will be no refcount 
associated with the copy stored in return_value.  Long story short you need 
to add ref the value when you assign it to a new container:

ZVAL_ADDREF(v);
zval_ptr_dtor(return_value_ptr);
*return_value_ptr = v;


Now for problem number 3 (What, you thought I'd stop at two?)

If v was an is_ref value, with a refcount of only 2 at the time of leaving 
your internal function, and you're using PHP 5.0, the function return will 
break the reference set.  This is a known bug and is fixed in the 5.1 
branch.

And problem #3a (PHP4 compatability)

PHP4 doesn't even have the return_value_ptr parameter so you can't even do 
that stuff with it.  What you can do is perform a deep copy on the value:

*return_value = *v;
zval_copy_ctor(return_value);

While it's less memory/cpu efficient, it is usable across all versions.

-Sara

---

## #19429 — David Olsson — 2005-10-06T07:49:35+00:00

URL: https://externals.io/message/19391#19429 · In reply to: #19405

Thanks! That was very useful information. Had to go with the #3a 
solution as I need to be compatible with PHP4.

I still have some problems with my extension though. The extension 
maintains a module global C++ std::map allocated in my MINIT function 
and deallocated in my MSHUTDOWN function. This map stores some zval * 
added to the map like this:

	int nameLen;
	char *name;
	zval *v;
	zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz!", &v);
	zval *newv = new zval[1];
	*newv = *v;
	zval_copy_ctor(newv);
	(*reinterpret_cast<map<string, zval *> *>(PSV_G(psvs)))[name] = newv;

where PSV_G(psvs) is a pointer to the map. The problem is that the 
content of the map may change or dissapear without my extension 
accessing it at all. Does the Zend engine do anything with the memory I 
have allocated? Currently i allocate memory for the map with new instead 
of emalloc but should that make any difference (except for larger risks 
for memory leaks)?

Thanks in advance!
David Olsson

Sara Golemon wrote:
> > […]
> There are two problems here:
> 
> #1 return_value is just a parameter in the prototype:
> 
> void zif_my_func(int ht, zval *return_value, zval **return_value_ptr, 
> zval *this_ptr, int return_value_used TSRMLS_DC)
> 
> So when you set:   return_value = v;   You are in fact changing where 
> return_value points, but you're not doing anything to the contents of 
> the return_value zval that the calling scope (the engine) knows about.  
> You're just changing the local pointer to look at another address.
> 
> Using return_value_ptr you can both clean up the original memory 
> allocated for return_value, and change what the engine knows about 
> return_value since you have the pointer to the pointer which the engine 
> uses:
> 
> /* Destroy old return_value (prevents leaks) */
> zval_ptr_dtor(return_value_ptr);
> 
> /* Point return_value at the passed in val */
> *return_value_ptr = v;
> 
> This brings us to the second problem:
> 
> #2 The refcount of v will have been temporarily increased by the engine 
> when it was placed on the parameter stack, however when the function 
> exits, that refcount will be decremented back down and there will be no 
> refcount associated with the copy stored in return_value.  Long story 
> short you need to add ref the value when you assign it to a new container:
> 
> ZVAL_ADDREF(v);
> zval_ptr_dtor(return_value_ptr);
> *return_value_ptr = v;
> 
> 
> Now for problem number 3 (What, you thought I'd stop at two?)
> 
> If v was an is_ref value, with a refcount of only 2 at the time of 
> leaving your internal function, and you're using PHP 5.0, the function 
> return will break the reference set.  This is a known bug and is fixed 
> in the 5.1 branch.
> 
> And problem #3a (PHP4 compatability)
> 
> PHP4 doesn't even have the return_value_ptr parameter so you can't even 
> do that stuff with it.  What you can do is perform a deep copy on the 
> value:
> 
> *return_value = *v;
> zval_copy_ctor(return_value);
> 
> While it's less memory/cpu efficient, it is usable across all versions.
> 
> -Sara

---

## #19452 — Sara Golemon — 2005-10-06T18:14:48+00:00

URL: https://externals.io/message/19391#19452 · In reply to: #19429

> Thanks! That was very useful information. Had to go with the #3a solution 
> as I need to be compatible with PHP4.
>
> I still have some problems with my extension though. The extension 
> maintains a module global C++ std::map allocated in my MINIT function and 
> deallocated in my MSHUTDOWN function. This map stores some zval * added to 
> the map like this:
>
Normal userspace variables (zvals) are allocated "non-persistently".  This 
means that, for example, creating a string variable consists of emalloc() 
calls:

foo = emalloc(sizeof(zval));
foo->value.str.val = emalloc(sizeof("bar"));

emalloc()'d pointers are forcibly freed between requests (as part of Zend's 
garbage collection), so even though you've taken care of all the other bits 
or placing the zval* into your map in regards to reference counting and 
creating a true copy, the zval itself is allocated non-persistently and will 
be quietly killed by the engine at the end of your request. (Well, no so 
quietly if you have --enable-debug turned on).

In order to hold a zval between requests you'll have to make a persistent 
copy of it (using pemalloc() which, I'm afraid, is a bit more complicated 
than it sounds).  Take a look at apc_store() and apc_fetch() which 
do...well....pretty much what you're talking about here.

> Currently i allocate memory for the map with new instead of emalloc but 
> should that make any difference (except for larger risks for memory 
> leaks)?
>
new will end up using malloc() which is what a persistent allocation would 
use anyway.  You actually *can't* use emalloc() in your MINIT code because 
emalloc() is only to be used within a request (which you're not inside of at 
that point).

-Sara

---

## #19544 — David Olsson — 2005-10-10T08:50:11+00:00

URL: https://externals.io/message/19391#19544 · In reply to: #19452

> Normal userspace variables (zvals) are allocated "non-persistently".  
> This means that, for example, creating a string variable consists of 
> emalloc() calls:
>
> foo = emalloc(sizeof(zval));
> foo->value.str.val = emalloc(sizeof("bar"));
>
> emalloc()'d pointers are forcibly freed between requests (as part of 
> Zend's garbage collection), so even though you've taken care of all 
> the other bits or placing the zval* into your map in regards to 
> reference counting and creating a true copy, the zval itself is 
> allocated non-persistently and will be quietly killed by the engine at 
> the end of your request. (Well, no so quietly if you have 
> --enable-debug turned on).

So that means that all the zval * I store in my map simply point to 
invalid memory once the request is completed? Well, that would explain 
some of the strange behaviour I have observed (such as string changing 
content between requests). The solution to this would be to allocate the 
zval struct using pemalloc and allocate memory for the data the zval 
(whatever type it is) uses with pemalloc as well?

>
> In order to hold a zval between requests you'll have to make a 
> persistent copy of it (using pemalloc() which, I'm afraid, is a bit 
> more complicated than it sounds).  Take a look at apc_store() and 
> apc_fetch() which do...well....pretty much what you're talking about 
> here.
>
As far as I can tell, the APC extension doesn't use pemalloc but rather 
malloc. Furthermore, pemalloc simply seems to be a macro which uses 
malloc to allocate memory. This means that my memory allocations using 
c++ new should allocate persistent memory chunks. So the 
persistent/non-persistent memory issue doesn't explain why my std::map 
simply is empty. The fact that it contain zval pointers pointing to 
invalid memory I understand but I don't get why it gets emptied. Any ideas?

Best regards
David Olsson

---

## #19545 — ondrej@kmit.sk — 2005-10-10T09:01:16+00:00

URL: https://externals.io/message/19391#19545 · In reply to: #19544

David Olsson wrote:
> malloc to allocate memory. This means that my memory allocations using 
> c++ new should allocate persistent memory chunks. So the 
> persistent/non-persistent memory issue doesn't explain why my std::map 
> simply is empty. The fact that it contain zval pointers pointing to 
> invalid memory I understand but I don't get why it gets emptied. Any ideas?

Overload new and delete operators?

-- 
Ondrej Ivanic
(ondrej@kmit.sk)

---

## #19552 — Sara Golemon — 2005-10-10T16:39:36+00:00

URL: https://externals.io/message/19391#19552 · In reply to: #19544

> So that means that all the zval * I store in my map simply point to
> invalid memory once the request is completed? Well, that would explain
> some of the strange behaviour I have observed (such as string changing
> content between requests).
>
Yep.

> The solution to this would be to allocate the
> zval struct using pemalloc and allocate memory for the data the zval
> (whatever type it is) uses with pemalloc as well?
>
When you get the zval* in your strore_var_in_map() function it's 
non-persistent.  You need to copy all of its data (and dependent data) into 
persistent storage when you store it in your map.   Then when you take it 
out of map to return it back to userspace (possibly in another request), you 
need to copy it again to place it in non-persistent storage.

> As far as I can tell, the APC extension doesn't use pemalloc but rather
> malloc. Furthermore, pemalloc simply seems to be a macro which uses
> malloc to allocate memory. This means that my memory allocations using
> c++ new should allocate persistent memory chunks.
>
That's correct.  It's not allocating the persistent memory chunks that's 
hard, it's duplicating a generic zval (in all the various forms that a zval 
can take) that's tricky (particularly in the case of IS_OBJECT and 
IS_RESOURCE).

> S the persistent/non-persistent memory issue doesn't explain why my 
> std::map
> simply is empty. The fact that it contain zval pointers pointing to
> invalid memory I understand but I don't get why it gets emptied. Any 
> ideas?
>
Post the code you have so far.  I'm working with a pretty limited pinhole 
view here....

-Sara

---

## #19559 — Marcus Boerger — 2005-10-10T18:44:22+00:00

URL: https://externals.io/message/19391#19559 · In reply to: #19429

Hello David,

Thursday, October 6, 2005, 9:49:35 AM, you wrote:

> Thanks! That was very useful information. Had to go with the #3a 
> solution as I need to be compatible with PHP4.

> I still have some problems with my extension though. The extension 
> maintains a module global C++ std::map allocated in my MINIT function 
> and deallocated in my MSHUTDOWN function. This map stores some zval * 
> added to the map like this:

>         int nameLen;
>         char *name;
>         zval *v;
>         zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz!", &v);
>         zval *newv = new zval[1];
>         *newv = *v;
>         zval_copy_ctor(newv);
>         (*reinterpret_cast<map<string, zval *> *>(PSV_G(psvs)))[name] = newv;

 do you have any idea what a reinterpret_cast<>() does? So probably change
 your code so that you are actually use what you think you do.

 apart from that you should consider storing the zval's rather than pointers.

> where PSV_G(psvs) is a pointer to the map. The problem is that the 
> content of the map may change or dissapear without my extension 
> accessing it at all. Does the Zend engine do anything with the memory I 
> have allocated? Currently i allocate memory for the map with new instead 
> of emalloc but should that make any difference (except for larger risks 
> for memory leaks)?

> Thanks in advance!
> David Olsson

> Sara Golemon wrote:
> > […]




Best regards,
 Marcus
