Recently I was working with PHP-CPP(2.0.0 release) for my projects. When running a test, I accidentally discovered (with
debug_zval_dump()) that when an object gets out of scope, its refcount does not decrement, which causes memory leak.
This is not a normal behavior for a regular PHP object, however, this object is declared and instantiated in my C++ code instead of user space, thus its garbage collection cannot be automatically done by Zend Engine. There must be something wrong with PHP-CPP.
As we know, we can wrap an object which extends
Php::Base into a
Php::Value using constructor
Value::Value(const Base *object). And if the object is instantiated in C++, you have to make it accessible to Zend Engine by calling constructor
Object::Object(const char *name, Base *base) at least once. Make sure the object is instantiated with
new, and once wrapped, you shall never attempt to
delete it. (Similarly, never use
std::shared_ptr for the object.) Otherwise you’ll get segmentation fault.
PHP-CPP makes sure garbage collection of C++ objects wrapped within
Php::Value is handled automatically, for the
Php::Base* pointer is stored in a
std::unique_ptr in a PHP-CPP’s
Php::ObjectImpl object. The latter gets destroyed once refcount becomes zero.
And here’s the problem. Why didn’t refcount decrease when
Php::Value gets destroyed? Let’s take a peek at the destructor of
And constructor for wrapping
It seems that we discovered the cause of the problem.
GC_REFCOUNT is the reference counter for a
zend_object, while function
Z_TRY_DELREF_P() decrements the refcount for a
zval. Thus, refcount increments every time you wrap the object with
Value::Value(const Base *object), but never goes down when wrapped object is destroyed. Hence the problem.
Z_ADDREF_P(_val) will do the trick. Not long after I started writing this blog I discovered that the master branch of PHP-CPP’s GitHub repository has already fixed this bug. So it’s highly recommended that you use the master branch of PHP-CPP(currently works fine with my projects), instead of the 2.0.0 release. See here for commits since the 2.0.0 release.