Skip to content

Instantly share code, notes, and snippets.

@datibbaw
Last active August 29, 2015 14:24
Show Gist options
  • Save datibbaw/49652b00dc06d4ef65ca to your computer and use it in GitHub Desktop.
Save datibbaw/49652b00dc06d4ef65ca to your computer and use it in GitHub Desktop.
unified array traversal hack
typedef zend_bool (*zend_iterator_each_t)(zval *value, zval *key, void *context);
static void php_traverse(zval *t, zend_iterator_each_t each_func, void *context)
{
zend_bool should_continue;
zval *value;
zval key;
if (Z_TYPE_P(t) == IS_ARRAY) {
zend_ulong num_key;
zend_string *string_key;
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(t), num_key, string_key, value) {
if (string_key) {
ZVAL_STR_COPY(&key, string_key);
} else {
ZVAL_LONG(&key, num_key);
}
should_continue = each_func(value, &key, context);
zval_ptr_dtor(&key);
if (!should_continue) break;
} ZEND_HASH_FOREACH_END();
} else {
zend_class_entry *ce = Z_OBJCE_P(t);
zend_object_iterator *iter = ce->get_iterator(ce, t, 0);
if (EG(exception)) goto fail;
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
}
while (!EG(exception) && iter->funcs->valid(iter) == SUCCESS) {
value = iter->funcs->get_current_data(iter);
if (EG(exception) || value == NULL) break;
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, &key);
if (EG(exception)) break;
} else {
ZVAL_NULL(&key);
}
should_continue = each_func(value, &key, context);
zval_ptr_dtor(&key);
if (!should_continue) break;
iter->index++;
iter->funcs->move_forward(iter);
}
fail:
zend_iterator_dtor(iter);
}
}
typedef struct _php_traverse_until_data {
zval *traversable;
zend_fcall_info fci;
zend_fcall_info_cache fci_cache;
int result, stop_value;
} php_traverse_until_data;
static zend_bool php_traverse_until(zval *value, zval *key, void *context)
{
zval args[3];
zval retval;
int call_res;
php_traverse_until_data *data = context;
data->fci.retval = &retval;
ZVAL_COPY(&args[0], value);
ZVAL_COPY(&args[1], key);
ZVAL_COPY(&args[2], data->traversable);
data->fci.params = args;
data->fci.param_count = 3;
data->fci.no_separation = 0;
call_res = zend_call_function(&data->fci, &data->fci_cache);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[2]);
data->result = call_res == SUCCESS ? zend_is_true(&retval) : 0;
zval_ptr_dtor(&retval);
return data->result != data->stop_value; // stop condition
}
static void php_array_until(INTERNAL_FUNCTION_PARAMETERS, int stop_value)
{
php_traverse_until_data data = {
NULL,
empty_fcall_info,
empty_fcall_info_cache,
!stop_value,
stop_value
};
if (zend_parse_parameters(ZEND_NUM_ARGS(), "tf", &data.traversable, &data.fci, &data.fci_cache) == FAILURE) {
return;
}
data.stop_value = stop_value;
php_traverse(data.traversable, php_traverse_until, &data);
RETURN_BOOL(data.result);
}
/* {{{ proto bool array_every(array input, mixed predicate)
Determines whether the predicate holds for all elements in the array. */
PHP_FUNCTION(array_every)
{
php_array_until(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto array array_filter(array input, mixed predicate)
Determines whether the predicate holds for at least one element in the array. */
PHP_FUNCTION(array_some)
{
php_array_until(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
<?php
function test()
{
yield 'foo' => 123;
yield 456;
}
function debug($value, $key, $traversable)
{
var_dump($value);
var_dump($key);
var_dump($traversable);
return true;
}
var_dump(array_some(test(), 'debug'));
var_dump(array_some(['foo' => 123, 456], 'debug'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment