How does array_keys do the search for value? - php

How does PHP array_keys do the search for value?
Example:
$array2 = array("xyz", "xyz", "abc", "abc", "xyz", "xyz", "text", "abc", "text");
print_r(array_keys($array2,"abc"));
Since they are key,value pairs. I am guessing PHP to do a search based on hash rather than iterate through each of the key-pairs in the array.
Any 'clarity thoughts' on this?
Question inspired by this question: How to get the keys of empty elements in an array if the corresponding element in a similar-sized array is a number (without iteration)?

In the php source, they iterate through each key and value, one by one.
https://github.com/php/php-src/blob/master/ext/standard/array.c#L2439
/* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
Return just the keys from the input array, optionally only for the specified search_value */
PHP_FUNCTION(array_keys)
{
zval *input, /* Input array */
*search_value = NULL, /* Value to search for */
**entry, /* An entry in the input array */
res, /* Result of comparison */
*new_val; /* New value */
int add_key; /* Flag to indicate whether a key should be added */
char *string_key; /* String key */
uint string_key_len;
ulong num_key; /* Numeric key */
zend_bool strict = 0; /* do strict comparison */
HashPosition pos;
int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) {
return;
}
if (strict) {
is_equal_func = is_identical_function;
}
/* Initialize return array */
if (search_value != NULL) {
array_init(return_value);
} else {
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
}
add_key = 1;
/* Go through input array and add keys to the return array */
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
if (search_value != NULL) {
is_equal_func(&res, search_value, *entry TSRMLS_CC);
add_key = zval_is_true(&res);
}
if (add_key) {
MAKE_STD_ZVAL(new_val);
switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 1, &pos)) {
case HASH_KEY_IS_STRING:
ZVAL_STRINGL(new_val, string_key, string_key_len - 1, 0);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
break;
case HASH_KEY_IS_LONG:
Z_TYPE_P(new_val) = IS_LONG;
Z_LVAL_P(new_val) = num_key;
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
break;
}
}
zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
}
}
/* }}} */

Please see the array_keys definition, take note also for the comments which explain it pretty well:
Return just the keys from the input array, optionally only for the specified search_value
and later on:
Go through input array and add keys to the return array
The definition as follows for PHP 5.3 on ext/standard/array.c:
2427 /* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
2428 Return just the keys from the input array, optionally only for the specified search_value */
2429 PHP_FUNCTION(array_keys)
2430 {
2431 zval *input, /* Input array */
2432 *search_value = NULL, /* Value to search for */
2433 **entry, /* An entry in the input array */
2434 res, /* Result of comparison */
2435 *new_val; /* New value */
2436 int add_key; /* Flag to indicate whether a key should be added */
2437 char *string_key; /* String key */
2438 uint string_key_len;
2439 ulong num_key; /* Numeric key */
2440 zend_bool strict = 0; /* do strict comparison */
2441 HashPosition pos;
2442 int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
2443
2444 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) {
2445 return;
2446 }
2447
2448 if (strict) {
2449 is_equal_func = is_identical_function;
2450 }
2451
2452 /* Initialize return array */
2453 if (search_value != NULL) {
2454 array_init(return_value);
2455 } else {
2456 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2457 }
2458 add_key = 1;
2459
2460 /* Go through input array and add keys to the return array */
2461 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
2462 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
2463 if (search_value != NULL) {
2464 is_equal_func(&res, search_value, *entry TSRMLS_CC);
2465 add_key = zval_is_true(&res);
2466 }
2467
2468 if (add_key) {
2469 MAKE_STD_ZVAL(new_val);
2470
2471 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 1, &pos)) {
2472 case HASH_KEY_IS_STRING:
2473 ZVAL_STRINGL(new_val, string_key, string_key_len - 1, 0);
2474 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
2475 break;
2476
2477 case HASH_KEY_IS_LONG:
2478 Z_TYPE_P(new_val) = IS_LONG;
2479 Z_LVAL_P(new_val) = num_key;
2480 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
2481 break;
2482 }
2483 }
2484
2485 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
2486 }
2487 }
2488 /* }}} */
The search that is performed is also explained on the PHP manual page for array_keys­Docs:
If the optional search_value is specified, then only the keys for that value are returned. Otherwise, all the keys from the input are returned.
See also the $strict parameter to influence how values are compared:
Determines if strict comparison (===) should be used during the search.
The two types of comparison == (Equal) and === (Identical) are documented as well.

I suspect this to be not so easy:
If you do NOT give the last parameter "strict" as true, PHP might
need to walk the array, along the way converting values to a
compareable type.
If you DO give "strict" as true, it might also be necessary to iterate the array to exclude wrong types

Related

PHP RecursiveIteratorIterator: Can you call getSubIterator() with a depth greater than your current depth?

When recursively iterating using an instance of \RecursiveIteratorIterator is it expected behaviour that in looking for a sub iterator of a depth greater than your current depth, it always returns null?
Please see: RecursiveIteratorIterator::getSubIterator
public RecursiveIterator RecursiveIteratorIterator::getSubIterator ([
int $level ] )
Example:
$innerIterator = new \RecursiveArrayIterator([/*a recursive array*/]);
$iterator = new \RecursiveIteratorIterator($innerIterator);
foreach ($iterator as $value) {
$depth = $iterator->getDepth();
$parentDepth = $depth - 1;
$childDepth = $depth + 1;
// returns \RecursiveArrayIterator
$iterator->getSubIterator($depth);
// returns null first iteration and \RecursiveArrayIterator thereafter
$iterator->getSubIterator($parentDepth);
// always returns null...should it?
$iterator->getSubIterator($childDepth);
}
The RecursiveIteratorIterator::getSubIterator() method's source code is short enough to be posted here, and is as good a place as any to find an answer.
/* {{{ proto RecursiveIterator RecursiveIteratorIterator::getSubIterator([int level])
The current active sub iterator or the iterator at specified level */
SPL_METHOD(RecursiveIteratorIterator, getSubIterator)
{
spl_recursive_it_object *object = (spl_recursive_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
long level = object->level;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &level) == FAILURE) {
return;
}
if (level < 0 || level > object->level) {
RETURN_NULL();
}
RETURN_ZVAL(object->iterators[level].zobject, 1, 0);
} /* }}} */
— From the PHP 5.5.14 source.
The key part for this question is the if statement:
if (level < 0 || level > object->level) {
RETURN_NULL();
}
The code is pretty self-explanatory, but it is saying:
If the specified level is less than zero, or greater than the current object's level, return null.

How are PHP's built-in functions implemented internally?

are these functions written the same way as user functions? I mean with PHP code and with regular expressions and stuff like that?
For example:
filter_var($email, FILTER_VALIDATE_EMAIL);
vs.
http://www.totallyphp.co.uk/code/validate_an_email_address_using_regular_expressions.htm
PHP is written in C. The PHP functions are written in high quality C code then compiled to form the PHP langugae library
if you want to extend PHP (edit / write) own functions check this out: http://www.php.net/~wez/extending-php.pdf
EDIT:
here you go :
This is the original C code for the function:
/* {{{ proto mixed filter_var(mixed variable [, long filter [, mixed options]])
* Returns the filtered version of the vriable.
*/
PHP_FUNCTION(filter_var)
{
long filter = FILTER_DEFAULT;
zval **filter_args = NULL, *data;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/|lZ", &data, &filter, &filter_args) == FAILURE) {
return;
}
if (!PHP_FILTER_ID_EXISTS(filter)) {
RETURN_FALSE;
}
MAKE_COPY_ZVAL(&data, return_value);
php_filter_call(&return_value, filter, filter_args, 1, FILTER_REQUIRE_SCALAR TSRMLS_CC);
}
/* }}} */
static void php_filter_call(zval **filtered, long filter, zval **filter_args, const int copy, long filter_flags TSRMLS_DC) /* {{{ */
{
zval *options = NULL;
zval **option;
char *charset = NULL;
if (filter_args && Z_TYPE_PP(filter_args) != IS_ARRAY) {
long lval;
PHP_FILTER_GET_LONG_OPT(filter_args, lval);
if (filter != -1) { /* handler for array apply */
/* filter_args is the filter_flags */
filter_flags = lval;
if (!(filter_flags & FILTER_REQUIRE_ARRAY || filter_flags & FILTER_FORCE_ARRAY)) {
filter_flags |= FILTER_REQUIRE_SCALAR;
}
} else {
filter = lval;
}
} else if (filter_args) {
if (zend_hash_find(HASH_OF(*filter_args), "filter", sizeof("filter"), (void **)&option) == SUCCESS) {
PHP_FILTER_GET_LONG_OPT(option, filter);
}
if (zend_hash_find(HASH_OF(*filter_args), "flags", sizeof("flags"), (void **)&option) == SUCCESS) {
PHP_FILTER_GET_LONG_OPT(option, filter_flags);
if (!(filter_flags & FILTER_REQUIRE_ARRAY || filter_flags & FILTER_FORCE_ARRAY)) {
filter_flags |= FILTER_REQUIRE_SCALAR;
}
}
if (zend_hash_find(HASH_OF(*filter_args), "options", sizeof("options"), (void **)&option) == SUCCESS) {
if (filter != FILTER_CALLBACK) {
if (Z_TYPE_PP(option) == IS_ARRAY) {
options = *option;
}
} else {
options = *option;
filter_flags = 0;
}
}
}
if (Z_TYPE_PP(filtered) == IS_ARRAY) {
if (filter_flags & FILTER_REQUIRE_SCALAR) {
if (copy) {
SEPARATE_ZVAL(filtered);
}
zval_dtor(*filtered);
if (filter_flags & FILTER_NULL_ON_FAILURE) {
ZVAL_NULL(*filtered);
} else {
ZVAL_FALSE(*filtered);
}
return;
}
php_zval_filter_recursive(filtered, filter, filter_flags, options, charset, copy TSRMLS_CC);
return;
}
if (filter_flags & FILTER_REQUIRE_ARRAY) {
if (copy) {
SEPARATE_ZVAL(filtered);
}
zval_dtor(*filtered);
if (filter_flags & FILTER_NULL_ON_FAILURE) {
ZVAL_NULL(*filtered);
} else {
ZVAL_FALSE(*filtered);
}
return;
}
php_zval_filter(filtered, filter, filter_flags, options, charset, copy TSRMLS_CC);
if (filter_flags & FILTER_FORCE_ARRAY) {
zval *tmp;
ALLOC_ZVAL(tmp);
MAKE_COPY_ZVAL(filtered, tmp);
zval_dtor(*filtered);
array_init(*filtered);
add_next_index_zval(*filtered, tmp);
}
}
AND HERE IS YOUR VALIDATE EMAIL ROUTINE:
-- this answers your question. Yes, it is done by regex internally.
void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{
/*
* The regex below is based on a regex by Michael Rushton.
* However, it is not identical. I changed it to only consider routeable
* addresses as valid. Michael's regex considers a#b a valid address
* which conflicts with section 2.3.5 of RFC 5321 which states that:
*
* Only resolvable, fully-qualified domain names (FQDNs) are permitted
* when domain names are used in SMTP. In other words, names that can
* be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
* in Section 5) are permitted, as are CNAME RRs whose targets can be
* resolved, in turn, to MX or address RRs. Local nicknames or
* unqualified names MUST NOT be used.
*
* This regex does not handle comments and folding whitespace. While
* this is technically valid in an email address, these parts aren't
* actually part of the address itself.
*
* Michael's regex carries this copyright:
*
* Copyright © Michael Rushton 2009-10
* http://squiloople.com/
* Feel free to use and redistribute this code. But please keep this copyright notice.
*
*/
const char regexp[] = "/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}#)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*#(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD";
pcre *re = NULL;
pcre_extra *pcre_extra = NULL;
int preg_options = 0;
int ovector[150]; /* Needs to be a multiple of 3 */
int matches;
/* The maximum length of an e-mail address is 320 octets, per RFC 2821. */
if (Z_STRLEN_P(value) > 320) {
RETURN_VALIDATION_FAILED
}
re = pcre_get_compiled_regex((char *)regexp, &pcre_extra, &preg_options TSRMLS_CC);
if (!re) {
RETURN_VALIDATION_FAILED
}
matches = pcre_exec(re, NULL, Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, ovector, 3);
/* 0 means that the vector is too small to hold all the captured substring offsets */
if (matches < 0) {
RETURN_VALIDATION_FAILED
}
}
/* }}} */
PHP functions are either :
Written in C -- and not in PHP
Or just wrappers to functions provided by other libraries (For instance, PHP's curl extension is just a wrapper arround the curl library).
If you are curious, you can take a look at the sources of PHP -- here's its SVN : http://svn.php.net/viewvc/
For instance, the filter_var() function should be defined somewhere in the sources of the filter extension.
Nope. PHP-internal functions are written in C, not with PHP code. Which looks quite unwieldy due to the many Zend-runtime macros and how parameters are transferred from PHP into C structures.
That particular function does use a regular expression. It also makes a nice example:
http://svn.php.net/repository/php/php-src/branches/PHP_5_3/ext/filter/logical_filters.c
Look for regexp[] somewhere in the middle.

Connect a PHP user-space callback as GLib signal handler

I'm using SWIG to generate a PHP extension over GLib which uses callbacks. To allow using PHP user-space functions as callbacks, i'm using something like:
The Wrapper (registers a unique callback dispatcher to handle all signal emissions):
/* {{{ proto void my_signal_connect(resource $instance, string $signal, mixed $callback, mixed $additional_args) }}}*/
ZEND_NAMED_FUNCTION(_wrap_my_signal_connect) {
GstObject *instance = (GstObject *) 0 ;
gchar *signal = (gchar *) 0 ;
zval *zcallback = (zval *) 0 ;
zval *zargs = (zval *) 0 ;
zval **args[4];
gulong result;
struct the_callback_struct *cb;
GType itype;
guint signal_id;
GSignalQuery *signal_info;
char *callback_name;
/* parse arguments */
SWIG_ResetError();
if(ZEND_NUM_ARGS() != 4 || zend_get_parameters_array_ex(4, args) != SUCCESS) {
WRONG_PARAM_COUNT;
}
{
if(SWIG_ConvertPtr(*args[0], (void **) &instance, 0, 0) < 0) {
if((*args[0])->type==IS_NULL) instance = 0;
else SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in argument 1. Expected SWIGTYPE_p_p_void");
}
}
if((*args[1])->type == IS_NULL) {
signal = (gchar *) 0;
} else {
convert_to_string_ex(args[1]);
signal = (gchar *) Z_STRVAL_PP(args[1]);
}
MAKE_STD_ZVAL(zcallback);
*zcallback = **args[2];
zval_copy_ctor(zcallback);
MAKE_STD_ZVAL(zargs);
*zargs = **args[3];
zval_copy_ctor(zargs);
/* query the signal system for in-depth info about the signal */
{
itype = G_TYPE_FROM_INSTANCE((GObject *) instance);
signal_id = g_signal_lookup((const gchar *) signal, itype);
if(signal_id == 0) {
SWIG_PHP_Error(E_ERROR, "The object does not emit the given signal");
}
signal_info = (GSignalQuery *) emalloc(sizeof(*signal_info));
g_signal_query(signal_id, signal_info);
}
/* get the function name or object + method name */
cb = (struct callback_struct *)emalloc(sizeof(*cb));
if(zcallback->type == IS_NULL) {
SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in callback argument.");
}
if(zcallback->type == IS_ARRAY) {
HashTable *ht = Z_ARRVAL_P(zcallback);
int n = zend_hash_num_elements(ht);
if(n == 2) {
if(zend_hash_index_find(ht, 0, (void **)&cb->target) == SUCCESS && Z_TYPE_PP(cb->target) == IS_OBJECT) {
if(zend_hash_index_find(ht, 1, (void **)&tmp2) == SUCCESS && Z_TYPE_PP(tmp2) == IS_STRING) {
MAKE_STD_ZVAL(cb->fx);
*cb->fx = **tmp2;
zval_copy_ctor(cb->fx);
}
}
}
} else if(zcallback->type == IS_STRING) {
cb->target = NULL;
MAKE_STD_ZVAL(cb->fx);
*cb->fx = *zcallback;
zval_copy_ctor(cb->fx);
} else {
SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in callback argument.");
}
/* Validate callback */
if(zend_is_callable(cb->fx, 0, &callback_name) == FAILURE) {
efree(callback_name);
SWIG_PHP_Error(E_ERROR, "Invalid callback");
}
/* copy the args into the structure */
MAKE_STD_ZVAL(cb->args);
*cb->args = *zargs;
zval_copy_ctor(cb->args);
cb->signal_id = signal_info->signal_id;
cb->signal_name = signal_info->signal_name;
cb->signal_flags = signal_info->signal_flags;
cb->itype = signal_info->itype;
cb->return_type = signal_info->return_type;
cb->n_params = signal_info->n_params;
cb->param_types = signal_info->param_types;
/* connect the signal handler */
result = (gulong)g_signal_connect(instance, signal, G_CALLBACK(my_signal_dispatcher), (gpointer) cb);
{
ZVAL_LONG(return_value,result);
}
return;
fail:
zend_error(SWIG_ErrorCode(),"%s",SWIG_ErrorMsg());
}
The callback struct:
struct callback_struct {
zval **target;
zval *fx;
zval *args;
GType itype; /* The type of object/instance which emitted the signal */
guint signal_id; /* The signal id (or 0 if the signal is unknown) */
const gchar *signal_name; /* The signal name */
GSignalFlags signal_flags; /* The signal flags (as declared when creating the signal) */
GType return_type; /* The return type for the callback */
guint n_params; /* The number of parameters of the callback */
const GType *param_types; /* The parameter types for callback arguments */
};
The signal dispatcher maps the signal handling to a PHP user space function:
static void my_signal_dispatcher(gpointer instance, ...) {
int i = 0, addr;
gpointer arg, ref;
zval retval;
zval *arglist[3];
struct callback_struct *cb;
/* add emitter instance to arg list */
SWIG_SetPointerZval(arglist[i++], (void *) instance, SWIGTYPE_p__GObject, 1);
va_list ap;
va_start(ap, instance);
/* fetch the variable list of arguments */
while((addr = va_arg(ap, int)) > 2) {
arg = (gpointer) addr;
if(G_IS_OBJECT(arg)) {
SWIG_SetPointerZval(arglist[i++], (void *) arg, SWIGTYPE_p__GObject, 1);
} else {
cb = (struct callback_struct *) arg;
MAKE_STD_ZVAL(arglist[i]);
*arglist[i] = *cb->args;
zval_copy_ctor(arglist[i]);
i++;
break;
}
}
va_end(ap);
if(cb->target == NULL) {
if(call_user_function(EG(function_table), NULL, cb->fx, &retval, i, arglist TSRMLS_CC) == SUCCESS) {
zval_dtor(&retval);
}
} else {
if(call_user_function(NULL, cb->target, cb->fx, &retval, i, arglist TSRMLS_CC) == SUCCESS) {
zval_dtor(&retval);
}
}
zval_ptr_dtor(cb->target);
zval_dtor(cb->fx);
zval_dtor(cb->args);
efree(cb);
}
I'am able to build the extension, and to connect a PHP signal handler (callback) to a given signal, for example:
<?php
//...
function cb() {
$s = array();
foreach(func_get_args() as $arg) {
$s[] = gettype($arg) == 'resource' ? 'Resource '.get_resource_type($arg) : (gettype($arg) == 'object' ? 'Object '.get_class($arg) : gettype($arg));
}
$s = implode(', ', $s);
echo " { PHP user-space: cb($s) } ";
return 1;
}
//...
myextension::my_signal_connect($instance, "child-added", array('one' => 1));
?>
so, when $instance emits the "child-added" signal i got the output from the cb() PHP function, and the following error:
{ PHP user-space: cb(Resource _p__GObject, Resource _p__GObject, array) }
*** glibc detected *** php: free(): invalid pointer: 0x095080c8 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(+0x6b591)[0xb95591]
/lib/tls/i686/cmov/libc.so.6(+0x6cde8)[0xb96de8]
/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xb99ecd]
/usr/lib/php5/20090626+lfs/myextension.so(+0x2a477)[0x7510477]
php[0x831c024]
php(zend_hash_del_key_or_index+0x112)[0x831af82]
php(_zend_list_delete+0x8c)[0x831c2ec]
php(_zval_dtor_func+0xb2)[0x830b872]
php(_zval_ptr_dtor+0x4d)[0x82ff00d]
php[0x82ff0c9]
php(zend_call_function+0x764)[0x8301694]
php(call_user_function_ex+0x64)[0x83023b4]
php(call_user_function+0x6b)[0x830242b]
/usr/lib/php5/20090626+lfs/gstreamer.so(+0x93c2d)[0x7579c2d]
/usr/lib/libgobject-2.0.so.0(g_cclosure_marshal_VOID__OBJECT+0x88)[0xd262d8]
======= Memory map: ========
00110000-0026e000 r-xp 00000000 08:04 440863 /usr/lib/libdb-4.8.so
0026e000-00270000 r--p 0015d000 08:04 440863 /usr/lib/libdb-4.8.so
00270000-00271000 rw-p 0015f000 08:04 440863 /usr/lib/libdb-4.8.so
...
I've tried to ref the GObject instances using g_object_ref() when connecting the signal before adding to the arguments list, without success
Any help?

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);
}

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