Extension Writing Part II: Parameters, Arrays, and ZVALs [continued]

http://devzone.zend.com/318/extension-writing-part-ii-parameters-arrays-and-zvals-continued/#Heading9

 Introduction
Accepting Values
The ZVAL
Creating ZVALs
Arrays
Symbol Tables as Arrays
Reference Counting
Copies versus References
Sanity Check
What’s Next?


Copies versus References

There are two ways to reference a zval. The first, demonstrated above, is known as copy-on-write referencing. The second form, full referencing, is what a userspace script writer is more familiar with in relationship to the word “reference” and occurs with userspace code like: $a = &$b;.

In a zval, these two types are differentiated with the member value is_ref,which will be 0 for copy references, and non-zero for full references. Note that it’s not possible for a zval to be both a copy style reference and a full reference. So if a variable starts off being is_ref, and is then assigned to a new variable as a copy, a full copy must be performed. Consider the following userspace code:


<?php

    $a = 1;
    
$b = &$a;
    
$c = $a;

?>

In this block of code, a zval is created for $a, initially with is_ref set to 0 and refcount__gc set to 1. When $a is referenced to $bis_ref gets changed to 1, and refcount__gc increases to 2. When a copy is made into $c, the Zend Engine can’t just simply increase the refcount__gc to 3 since $c would then be seen as a full reference to $a. Turning off is_ref wouldn’t work either since $bwould now be seen as a copy of $a, rather than a reference. So at this point a new zval is allocated, and the value of the original is copied into it withzval_copy_ctor(). The original zval is left with is_ref==1 and refcount__gc==2, while the new zval has is_ref=0 and refcount__gc=1. Now look at that same code block done in a slightly different order:


<?php

    $a = 1;
    
$c = $a;
    
$b = &$a;

?>

The end result is the same, with $b being a full reference to $a, and $cbeing a copy of $a. This time, however, the internal actions are slightly different. As before, a new zval is created for $a at the beginning withis_ref==0 and refcount__gc=1. The $c = $a; statement then assigns the same zvalto the $c variable while incrementing the refcount__gc to 2, and leaving is_refas 0. When the Zend Engine encounters $b = &$a; it wants to just set is_refto 1, but of course can’t since that would impact $c. Instead it creates a new zval and copies the contents of the original into it withzval_copy_ctor(), then decrements refcount__gc in the original zval to signify that $a is no longer using that zval. Instead, it sets the new zval‘s is_refto 1, its refcount__gc to 2, and updates the $a and $b variables to refer to it.


Sanity Check

As before, the complete code listing for our three primary files is provided whole and intact below:

config.m4


PHP_ARG_ENABLE(hello, [whether to enable Hello World support],
[ --enable-hello   Enable Hello World support])

if test "$PHP_HELLO" = "yes"; then
  AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
  PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

php_hello.h


#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1

#ifdef ZTS
#include "TSRM.h"
#endif

ZEND_BEGIN_MODULE_GLOBALS(hello)
    long counter;
    zend_bool direction;
ZEND_END_MODULE_GLOBALS(hello)

#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif

#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);

PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);
PHP_FUNCTION(hello_greetme);
PHP_FUNCTION(hello_add);
PHP_FUNCTION(hello_dump);
PHP_FUNCTION(hello_array);
PHP_FUNCTION(hello_array_strings);
PHP_FUNCTION(hello_array_walk);
PHP_FUNCTION(hello_array_value);
PHP_FUNCTION(hello_get_global_var);
PHP_FUNCTION(hello_set_local_var);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif

hello.c


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_hello.h"

ZEND_DECLARE_MODULE_GLOBALS(hello)

static zend_function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    PHP_FE(hello_long, NULL)
    PHP_FE(hello_double, NULL)
    PHP_FE(hello_bool, NULL)
    PHP_FE(hello_null, NULL)
    PHP_FE(hello_greetme, NULL)
    PHP_FE(hello_add, NULL)
    PHP_FE(hello_dump, NULL)
    PHP_FE(hello_array, NULL)
    PHP_FE(hello_array_strings, NULL)
    PHP_FE(hello_array_walk, NULL)
    PHP_FE(hello_array_value, NULL)
    PHP_FE(hello_get_global_var, NULL)
    PHP_FE(hello_set_local_var, NULL)
    {NULL, NULL, NULL}
};

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    PHP_MINIT(hello),
    PHP_MSHUTDOWN(hello),
    PHP_RINIT(hello),
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_HELLO
    ZEND_GET_MODULE(hello)
#endif

PHP_INI_BEGIN()
    PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL)
    STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)
PHP_INI_END()

static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
    hello_globals->direction = 1;
}

PHP_RINIT_FUNCTION(hello)
{
    HELLO_G(counter) = 0;

    return SUCCESS;
}

PHP_MINIT_FUNCTION(hello)
{
    ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);

    REGISTER_INI_ENTRIES();

    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(hello)
{
    UNREGISTER_INI_ENTRIES();

    return SUCCESS;
}

PHP_FUNCTION(hello_world)
{
    RETURN_STRING("Hello World", 1);
}

PHP_FUNCTION(hello_long)
{
    if (HELLO_G(direction)) {
        HELLO_G(counter)++;
    } else {
        HELLO_G(counter)--;
    }

    RETURN_LONG(HELLO_G(counter));
}

PHP_FUNCTION(hello_double)
{
    RETURN_DOUBLE(3.1415926535);
}

PHP_FUNCTION(hello_bool)
{
    RETURN_BOOL(1);
}

PHP_FUNCTION(hello_null)
{
    RETURN_NULL();
}

PHP_FUNCTION(hello_greetme)
{
    zval *zname;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zname) == FAILURE) {
        RETURN_NULL();
    }

    convert_to_string(zname);

    php_printf("Hello ");
    PHPWRITE(Z_STRVAL_P(zname), Z_STRLEN_P(zname));
    php_printf("
");

    RETURN_TRUE;
}

PHP_FUNCTION(hello_add)
{
    long a;
    double b;
    zend_bool return_long = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ld|b", &a, &b, &return_long) == FAILURE) {
        RETURN_NULL();
    }

    if (return_long) {
        RETURN_LONG(a + b);
    } else {
        RETURN_DOUBLE(a + b);
    }
}

PHP_FUNCTION(hello_dump)
{
    zval *uservar;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &uservar) == FAILURE) {
    RETURN_NULL();
    }

    switch (Z_TYPE_P(uservar)) {
    case IS_NULL:
        php_printf("NULL
");
        break;
    case IS_BOOL:
        php_printf("Boolean: %s
", Z_LVAL_P(uservar) ? "TRUE" : "FALSE");
        break;
    case IS_LONG:
        php_printf("Long: %ld
", Z_LVAL_P(uservar));
        break;
    case IS_DOUBLE:
        php_printf("Double: %f
", Z_DVAL_P(uservar));
        break;
    case IS_STRING:
        php_printf("String: ");
        PHPWRITE(Z_STRVAL_P(uservar), Z_STRLEN_P(uservar));
        php_printf("
");
        break;
    case IS_RESOURCE:
        php_printf("Resource
");
        break;
    case IS_ARRAY:
        php_printf("Array
");
        break;
    case IS_OBJECT:
        php_printf("Object
");
        break;
    default:
        php_printf("Unknown
");
    }

    RETURN_TRUE;
}

PHP_FUNCTION(hello_array)
{
    char *mystr;
    zval *mysubarray;

    array_init(return_value);

    add_index_long(return_value, 42, 123);

    add_next_index_string(return_value, "I should now be found at index 43", 1);

    add_next_index_stringl(return_value, "I'm at 44!", 10, 1);

    mystr = estrdup("Forty Five");
    add_next_index_string(return_value, mystr, 0);

    add_assoc_double(return_value, "pi", 3.1415926535);

    ALLOC_INIT_ZVAL(mysubarray);
    array_init(mysubarray);
    add_next_index_string(mysubarray, "hello", 1);
    php_printf("mysubarray->refcount__gc = %d
", mysubarray->refcount__gc);
    mysubarray->refcount__gc = 2;
    php_printf("mysubarray->refcount__gc = %d
", mysubarray->refcount__gc);
    add_assoc_zval(return_value, "subarray", mysubarray);

    php_printf("mysubarray->refcount__gc = %d
", mysubarray->refcount__gc);
}

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)) {

        zval temp;
        char *key;
        int key_len;
        long index;

        if (zend_hash_get_current_key_ex(arr_hash, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) {
            PHPWRITE(key, key_len);
        } else {
            php_printf("%ld", index);
        }

        php_printf(" => ");

        temp = **data;
        zval_copy_ctor(&temp);
        convert_to_string(&temp);
        PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
        php_printf("
");
        zval_dtor(&temp);
    }

    RETURN_TRUE;
}

static int php_hello_array_walk(zval **element TSRMLS_DC)
{
    zval temp;

    temp = **element;
    zval_copy_ctor(&temp);
    convert_to_string(&temp);
    PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
    php_printf("
");
    zval_dtor(&temp);

    return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_arg(zval **element, char *greeting TSRMLS_DC)
{
    php_printf("%s", greeting);
    php_hello_array_walk(element TSRMLS_CC);

    return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_args(zval **element, int num_args, va_list args, zend_hash_key *hash_key)
{
    char *prefix = va_arg(args, char*);
    char *suffix = va_arg(args, char*);
    TSRMLS_FETCH();

    php_printf("%s", prefix);
    php_hello_array_walk(element TSRMLS_CC);
    php_printf("%s
", suffix);

    return ZEND_HASH_APPLY_KEEP;
}

PHP_FUNCTION(hello_array_walk)
{
    zval *zarray;
    int print_newline = 1;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zarray) == FAILURE) {
        RETURN_NULL();
    }

    zend_hash_apply(Z_ARRVAL_P(zarray), (apply_func_t)php_hello_array_walk TSRMLS_CC);
    zend_hash_internal_pointer_reset(Z_ARRVAL_P(zarray));
    zend_hash_apply_with_argument(Z_ARRVAL_P(zarray), (apply_func_arg_t)php_hello_array_walk_arg, "Hello " TSRMLS_CC);
    zend_hash_apply_with_arguments(Z_ARRVAL_P(zarray), (apply_func_args_t)php_hello_array_walk_args, 2, "Hello ", "Welcome to my extension!");

    RETURN_TRUE;
}

PHP_FUNCTION(hello_array_value)
{
    zval *zarray, *zoffset, **zvalue;
    long index = 0;
    char *key = NULL;
    int key_len = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &zarray, &zoffset) == FAILURE) {
    RETURN_NULL();
    }

    switch (Z_TYPE_P(zoffset)) {
    case IS_NULL:
        index = 0;
        break;
    case IS_DOUBLE:
        index = (long)Z_DVAL_P(zoffset);
        break;
    case IS_BOOL:
    case IS_LONG:
    case IS_RESOURCE:
        index = Z_LVAL_P(zoffset);
        break;
    case IS_STRING:
        key = Z_STRVAL_P(zoffset);
        key_len = Z_STRLEN_P(zoffset);
        break;
    case IS_ARRAY:
        key = "Array";
        key_len = sizeof("Array") - 1;
        break;
    case IS_OBJECT:
        key = "Object";
        key_len = sizeof("Object") - 1;
        break;
    default:
        key = "Unknown";
        key_len = sizeof("Unknown") - 1;
    }

    if (key && zend_hash_find(Z_ARRVAL_P(zarray), key, key_len + 1, (void**)&zvalue) == FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined index: %s", key);
        RETURN_NULL();
    } else if (!key && zend_hash_index_find(Z_ARRVAL_P(zarray), index, (void**)&zvalue) == FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined index: %ld", index);
        RETURN_NULL();
    }

    *return_value = **zvalue;
    zval_copy_ctor(return_value);
}

PHP_FUNCTION(hello_get_global_var)
{
    char *varname;
    int varname_len;
    zval **varvalue;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &varname, &varname_len) == FAILURE) {
        RETURN_NULL();
    }

    if (zend_hash_find(&EG(symbol_table), varname, varname_len + 1, (void**)&varvalue) == FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined variable: %s", varname);
        RETURN_NULL();
    }

    *return_value = **varvalue;
    zval_copy_ctor(return_value);
}

PHP_FUNCTION(hello_set_local_var)
{
    zval *newvar;
    char *varname;
    int varname_len;
    zval *value;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &varname, &varname_len, &value) == FAILURE) {
        RETURN_NULL();
    }

    ALLOC_INIT_ZVAL(newvar);
    *newvar = *value;
    zval_copy_ctor(newvar);

    zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, &newvar, sizeof(zval*), NULL);

    RETURN_TRUE;
}


What’s Next?

In this tutorial, Part Two of the Extension Writing series, you learned how to accept
function parameters, you created and used arrays, and, most importantly, you took a
look at the inner workings of the zval. In Part Three, you’ll take a look
at the resource data type and start working with more complex data structures.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值