how to convert zval to vector for php extension? - php

i'm writing a php extension for my c++ library which is defined something like this:
bool getPids(map<string,string> pidsMap, vector<string> ids);
now, i'm writing a php wrapper for above function like this.
ZEND_METHOD(myFInfo, get_pids)
{
zval *idsArray;
if (zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "a",
&idsArray ) == FAILURE )
{
RETURN_NULL();
}
}
now i want to call getPids(), but i don't know the proper way to pass idsArray as vector into the c++ function.
after some search on web, i found an example where zval array is iterated to read each values, and i thought maybe i can use this to create a vector.
PHP_FUNCTION(hello_array_strings)
{
zval *arr, **data;
HashTable *arr_hash;
HashPosition pointer;
int array_count;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
RETURN_NULL();
}
arr_hash = Z_ARRVAL_P(arr);
array_count = zend_hash_num_elements(arr_hash);
php_printf("The array passed contains %d elements", array_count);
for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS;
zend_hash_move_forward_ex(arr_hash, &pointer)) {
if (Z_TYPE_PP(data) == IS_STRING) {
PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data));
php_printf("
");
}
}
RETURN_TRUE;
}
but is this the best approach? or is there a better way to do this?
thanks!

To populate a std::vector<std::string> from a PHP array, here's how I'd do it (the short version):
std::vector<std::string> vec;
HashTable *arr_hash = Z_ARRVAL_P(arr);
zval **arr_value;
for(zend_hash_internal_pointer_reset(arr_hash);
zend_hash_get_current_data(arr_hash, (void **)&arr_value) == SUCCESS;
zend_hash_move_forward(arr_hash))
{
vec.push_back(Z_STRVAL_PP(arr_value));
}
...where arr is your input zval *, and vec is your output vector.

Related

How to return $this in a php Extension?

For example this method of Dataset object returns NULL, How do I make it to return $this
PHP_METHOD(TSet, nextLine)
{
TSet *MySet;
tset_object *obj = (tset_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
MySet = obj->DataSet;
if (MySet != NULL) {
MySet->nextLine();
}
RETURN_NULL();
}
Tried
zval *object = getThis();
RETURN_ZVAL(object,false,false);
Gave me segfault
And just to be sure also this
RETURN_ZVAL(getThis(),false,false);
With same result
RETURN_ZVAL(getThis(), 1, 0);
Is the correct answer, not sure why though.
Got it from http://www.snailinaturtleneck.com/blog/2011/08/11/php-extensions-made-eldrich-classes/#comment-466980122

php extension value not correct printed why?

I want to send an object through reference. The object represents a class
[test.php]
$car= new Car();
$car->l=1000;
$car2 = new Car2();
$car2->method($car);
[php_cod.cc]
PHP_METHOD(Car2, method)
{
Car2 *car;
Car obj11;
zend_class_entry ce2;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj11) == FAILURE) {
RETURN_NULL();
}
car2_object *obj = (car2_object *)zend_object_store_get_object(
getThis() TSRMLS_CC);
car2 = obj->car2;
if (car2 != NULL) {
//cout<<"in car 2 ref"<<endl;
//car2->reference(s);
(car2->method(obj11));
}
}
[test.cc]
void Car2::method(Car &carr)
{
cout<<"IN THE REFERENCE CLASS"<<carr.l<<endl;
}
Where am I wrong?
THX! Appreciate
When I run php test.php the value vor carr.l is equal with
155160836. WHY? Where am I wrong
In the php part of code, obj11 is an uninitalized pointer and that is being passed to Car2::method ( (car2->method(*obj11)); ). So-
cout<<"IN THE REFERENCE CLASS"<<carr.l<<endl;
// carr is a garbage reference and is causing the segmentation fault

Convert Zval to char*

I want to convert Zval to char*. how do i do that in my php extension?
It the zval represents a string, you can use Z_STRVAL (or Z_STRVAL_P/Z_STRVAL_PP if you have a zval*/zval**).
Otherwise, you may have to convert the zval before :
zval *var;
char *cstr;
int cstrlen;
/* ... */
if (Z_TYPE_P(var) != IS_STRING) {
convert_to_string(var);
}
cstr = Z_STRVAL_P(var);
cstrlen = Z_STRLEN_P(var);
If you don't want to change the original zval and you want to change the resulting C string, you can do:
zval *var, *varcopy;
char *cstr;
int cstrlen;
if (Z_TYPE_P(var) != IS_STRING) {
ALLOC_INIT_ZVAL(varcopy);
*varcopy = *var;
INIT_PZVAL(varcopy); /* reset refcount and clear is_ref */
zval_copy_ctor(varcopy);
convert_to_string(varcopy);
} else {
varcopy = var;
}
cstrlen = Z_STRLEN_P(varcopy);
cstr = estrndup(Z_STRVAL_P(varcopy), cstrlen);
if (varcopy != var) {
zval_ptr_dtor(&varcopy);
}

list of registered shutdown functions

Is there any method to access to list of registered shutdown functions?
You can write an extension and look at BG(user_shutdown_function_names). Probably easier is to make a wrapper for register_shutdown_function that saves the shutdown functions to some array and call it instead.
(Untested)
#include "ext/standard/basic_functions.h"
//usual include suspects here
typedef struct _php_shutdown_function_entry {
zval **arguments;
int arg_count;
} php_shutdown_function_entry;
static void _shutdown_function_dtor(php_shutdown_function_entry *shutdown_function_entry) /* {{{ */
{
int i;
for (i = 0; i < shutdown_function_entry->arg_count; i++) {
zval_ptr_dtor(&shutdown_function_entry->arguments[i]);
}
efree(shutdown_function_entry->arguments);
}
static int _build_shutdown_array(php_shutdown_function_entry *entry, zval *arr TSRMLS_DC)
{
zval *inner;
zval *args;
int i;
array_init(inner);
array_init(args);
Z_ADDREF_P(entry->arguments[0]);
add_assoc_zval(inner, "callback", entry->arguments[0]);
for (i = 1; i < entry->arg_count; i++) {
Z_ADDREF_P(entry->arguments[i]);
add_next_index_zval(args, entry->arguments[i]);
}
add_assoc_zval(inner, "arguments", args);
add_next_index_zval(arr, inner);
}
PHP_FUNCTION(list_shutdown_functions)
{
if (zend_parse_parameters_none() == FAILURE)
return;
if (!BG(user_shutdown_function_names)) {
ALLOC_HASHTABLE(BG(user_shutdown_function_names));
zend_hash_init(BG(user_shutdown_function_names), 0, NULL,
(void (*)(void *)) _shutdown_function_dtor, 0);
}
array_init(return_value);
zend_hash_apply_with_argument(BG(user_shutdown_function_names),
(apply_func_arg_t) _build_shutdown_array, return_value TSRMLS_CC);
}
Other than keeping track yourself, no. The list of registered function names is not exposed to your PHP scripts. If you're open to extending PHP itself (this would be a simple task) then see Artefacto's answer.

PHP extension: convert an object to string with __toString()

Writing a PHP extension in C, I want to convert a userland object (IS_OBJECT) to a string through __toString() if it has one, and fail otherwise. What should I use?
I don't need another zval on output, just a char *.
zval *zo;
switch (Z_TYPE_P(zo)) {
case IS_STRING:
... Z_STRVAL_P(zo) ...
break;
case IS_OBJECT:
... ???(zo) ...
break;
...
}
The reflection module does something like
ZVAL_STRINGL(&fname, "__tostring", sizeof("__tostring") - 1, 1);
result= call_user_function_ex(NULL, &object, &fname, &retval_ptr, 0, NULL, 0, NULL TSRMLS_CC);
zval_dtor(&fname);
if (result == FAILURE) {
_DO_THROW("Invocation of method __toString() failed");
/* Returns from this function */
}
And then you'd extract the char* with Z_STRVAL_P().
But I guess you could also use
case IS_OBJECT:
if ( SUCCESS==zend_std_cast_object_tostring(uservar, uservar, IS_STRING TSRMLS_CC) ) {
int len = Z_STRLEN_P(uservar);
char* pValue = Z_STRVAL_P(uservar);
...
}
zend_std_cast_object_tostring() is implemented in zend/zend_object_handlers.c. You might want to check if it really does what you want

Categories