[翻訳] [php 拡張機能と埋め込み] 第 10 章 - php4 オブジェクト

WBOY
リリース: 2016-06-13 12:50:18
オリジナル
1011 人が閲覧しました

[翻訳][php 拡張機能と埋め込み] 第 10 章 - php4 のオブジェクト

完全な翻訳コンテンツ PDF ドキュメントのダウンロード アドレス: http://download.csdn.net/detail/lgg201/5107012

この本は現在、laruence (http://www.laruence.com) と walu (http://www.walu.cc) によって github 上で翻訳されています。翻訳プロジェクトのアドレスは https://github.com です。 /walu/phpbook

Github 上のこの本のアドレス: https://github.com/goosman-lei/php-eae

将来的には、この本は独立したバージョンを保持したまま、部分的に phpbook プロジェクトに統合される可能性があります。


元のタイトル:

原作者:サラ・ゴーレモン

翻訳者: goosman.lei (Lei Guo)

翻訳者のメールアドレス: lgg860911@yahoo.com.cn

翻訳者ブログ: http://blog.csdn.net/lgg201

php4 オブジェクト

かつて、非常に初期のバージョンでは、php はオブジェクト指向プログラミング構文をサポートしていませんでした。Zend エンジン (ZE1) は php4 で導入され、オブジェクト データ型を含むいくつかの新機能が登場しました。

PHP オブジェクト型の進化

最初のオブジェクト指向プログラミング (OOP) サポートは、オブジェクト関連付けのセマンティクスのみを実装しました。これは、php カーネル開発者の言葉を借りれば、「php4 オブジェクトは、配列といくつかのメソッドをバインドするだけです。」今すぐ勉強したいです。

Zend Engine (ZE2) の 2 番目のメジャー バージョン リリースは php5 にあり、php の OOP 実装にいくつかの新機能が導入されています。たとえば、プロパティとメソッドはアクセス修飾子を使用して、クラス定義内でそれらをマークできます。外部可視性については、関数を使用します。オーバーロードは内部言語構造のカスタム動作を定義するために使用でき、インターフェイスは複数のクラスの呼び出しチェーン間の API 標準化を実装するために使用できます。第 11 章「php5 オブジェクト」を学習すると、これらの機能を実装することでこの知識の認識が高まります。 PHP5 クラス定義内。

実装クラス

OOP の世界に入る前に、軽く移動する必要があります。そのため、拡張機能を第 5 章「最初の拡張機能」で構築したばかりのスケルトン フォームに戻してください。

元の演習から独立させるために、このバージョンに「sample2」という名前を付けることができます。次の 3 つのファイルを php ソース コードの ext/sample2 ディレクトリに置きます。

config.m4

PHP_ARG_ENABLE(sample2,
  [Whether to enable the "sample2" extension],
  [  enable-sample2       Enable "sample2" extension support])

if test $PHP_SAMPLE2 != "no"; then
  PHP_SUBST(SAMPLE2_SHARED_LIBADD)
  PHP_NEW_EXTENSION(sample2, sample2.c, $ext_shared)
fi
ログイン後にコピー

php_saple2.h

sample2.c

#include "php_sample2.h"

static function_entry php_sample2_functions[] = {
    { NULL, NULL, NULL }
};

PHP_MINIT_FUNCTION(sample2)
{
    return SUCCESS;
}

zend_module_entry sample2_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_SAMPLE2_EXTNAME,
    php_sample2_functions,
    PHP_MINIT(sample2),
    NULL, /* MSHUTDOWN */
    NULL, /* RINIT */
    NULL, /* RSHUTDOWN */
    NULL, /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
    PHP_SAMPLE2_EXTVER,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_SAMPLE2
ZEND_GET_MODULE(sample2)
#endif
ログイン後にコピー

现在, 就像在第5章时一样, 你可以执行phpize, ./configure, make去构建你的sample2.so扩展模块.

你之前的config.w32做与这里给出的config.m4一样的修改也可以正常工作.

定义类条目

在用户空间中, 定义一个类如下:

<?php
class Sample2_FirstClass {
}
?>
ログイン後にコピー

毫无疑问, 你会猜到, 在扩展中实现它还是有一点难度的. 首先, 你需要在你的源代码文件中, 像上一章定义int le_sample_descriptor一样, 定义一个zend_class_entry指针:

zend_class_entry *php_sample2_firstclass_entry;
ログイン後にコピー

现在, 就可以在MINIT函数中初始化并注册类了.

PHP_MINIT_FUNCTION(sample2)
{
    zend_class_entry ce; /* 临时变量 */

    /* 注册类 */
    INIT_CLASS_ENTRY(ce, "Sample2_FirstClass", NULL);
    php_sample2_firstclass_entry =
            zend_register_internal_class(&ce TSRMLS_CC);

    return SUCCESS;
}
ログイン後にコピー

构建这个扩展, 测试get_declared_classes(), 将会看到Sample2_FirstClass现在在用户空间可用了.

定义方法的实现

此刻, 你实现的只是一个stdClass, 当然它是可用的. 但实际上你是希望你的类可以做一些事情的.

要达成这个目的, 你就需要回到第5章学到的另外一个知识点了. 将传递给INIT_CLASS_ENTRY()的NULL参数替换为php_sample2_firstclass_functions, 并直接在MINIT函数上面如下定义这个结构:

static function_entry php_sample2_firstclass_functions[] = {
    { NULL, NULL, NULL }
};
ログイン後にコピー

看起来熟悉吗? 当然. 这和你原来定义过程函数的结构相同. 甚至, 设置这个结构的方式也很相似:

PHP_NAMED_FE(method1, PHP_FN(Sample2_FirstClass_method1), NULL)
ログイン後にコピー

当然, 你也可以选用PHP_FE(method1, NULL). 不过回顾一下第5章, 这样做期望找到的函数实现的名字是zif_method1, 它可能潜在的回合其他的method1()实现冲突. 为了函数的名字空间安全, 我们将类名作为方法名的前缀.

PHP_FALIAS(method1, Sample2_FirstClass_method1, NULL)的格式也是可以的; 但它有点不直观, 你以后回过头来看代码的时候可能会产生疑问"为什么当时没有使用PHP_FE()?"

现在, 你已经将一个函数列表附加到类的定义上了, 是时候定义一些方法了. 在php_sample2_firstclass_functions结构上面创建下面的函数:

PHP_FUNCTION(Sample2_FirstClass_countProps)
{
    RETURN_LONG(zend_hash_num_elements(Z_OBJPROP_P(getThis())));
}
ログイン後にコピー

相应的, 在它的函数列表中增加一条PHP_NAMED_FE()条目:

static function_entry php_sample2_firstclass_functions[] = {
    PHP_NAMED_FE(countprops,
            PHP_FN(Sample2_FirstClass_countProps), NULL)
    { NULL, NULL, NULL }
};
ログイン後にコピー

要注意, 这里暴露给用户空间的函数名是全部小写的.为了确保方法和函数名都是大小写不敏感的, 就要求内部函数给出全部小写的名字.

这里唯一的新元素就是getThis(), 在所有的php版本中, 它都会被解析为一个宏, 展开是this_ptr. this_ptr从本质上来说就和用户空间对象方法中的$this含义相同. 如果没有可用的对象实例, 比如方法被静态化调用, 则getThis()返回NULL.

对象方法的数据返回语义和过程函数一致, 参数接受以及arg_info都是同一套东西.

PHP_FUNCTION(Sample2_FirstClass_sayHello)
{
    char *name;
    int name_len;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
                        &name, &name_len) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("Hello");
    PHPWRITE(name, name_len);
    php_printf("!\nYou called an object method!\n");
    RETURN_TRUE;
}
ログイン後にコピー

构造器

你的类构造器可以和其他的普通类方法一样实现, 它的命名遵循也遵循相同的规则. 特别之处在于你需要将构造器命名为类名. 其他两个ZE1魔术方法__sleep()和__wakeup()也可以以这种方式实现.

继承

php4中, 内部对象之间的继承是不完善的, 最好避免使用. 如果你确实必须继承其他对象, 需要复制下面的ZE1代码:

void php_sample2_inherit_from_class(zend_class_entry *ce,
                        zend_class_entry *parent_ce) {
    zend_hash_merge(&ce->function_table,
            &parent_ce->function_table, (void (*)(void *))function_add_ref,
            NULL, sizeof(zval*), 0);
    ce->parent = parent_ce;
    if (!ce->handle_property_get) {
        ce->handle_property_get =
                parent_ce->handle_property_get;
    }
    if (!ce->handle_property_set) {
        ce->handle_property_set =
                parent_ce->handle_property_set;
    }
    if (!ce->handle_function_call) {
        ce->handle_function_call =
                parent_ce->handle_function_call;
    }
    if (!zend_hash_exists(&ce->function_table,
                ce->name, ce->name_length + 1)) {
        zend_function *fe;
        if (zend_hash_find(&parent_ce->function_table,
                parent_ce->name, parent_ce->name_length + 1,
                (void**)fe) == SUCCESS) {
            zend_hash_update(&ce->function_table,
                ce->name, ce->name_length + 1,
                fe, sizeof(zend_function), NULL);
            function_add_ref(fe);
        }
    }
}
ログイン後にコピー

定义这样一个函数, 你就可以在MINIT中zend_register_internal_class下面对其进行调用:

INIT_CLASS_ENTRY(ce, "Sample2_FirstClass", NULL);
/* 假定php_saple2_ancestor是一个已经注册的zend_class_entry */
php_sample2_firstclass_entry =
        zend_register_internal_class(&ce TSRMLS_CC);
php_sample2_inherit_from_class(php_sample2_firstclass_entry
                            ,php_sample2_ancestor);
ログイン後にコピー

尽管这种方式的继承可以工作, 但还是应该避免ZE1中的继承, 因为它并没有设计内部对象的继承处理. 对于php中的多数OOP实践, ZE2和它修订的对象模型是健壮的, 鼓励所有的OOP相关任务都直接使用它来处理.

使用实例工作

和其它用户空间变量一样, 对象存储在zval *容器中. 在ZE1中, zval *包含了一个HashTable *用于保存属性, 以及一个zend_class_entry *指针, 指向类的定义. 在ZE2中, 这些值被一个句柄表替代, 增加了一个数值的对象ID, 它和资源ID的用法类似.

很幸运, ZE1和ZE2的这些差异被第2章"变量的里里外外"中介绍的Z_*()族宏隐藏了, 因此在你的扩展中不需要关心这些. 下表10.1列出了两个ZE1的宏, 与非OOP的相关宏一致, 它们也有对应的_P和_PP版本, 用来处理一级或两级间访.


含义

Z_OBJPROP(zv)

組み込みプロパティの HashTable *

を取得します

Z_OBJCE(zv)

関連付けられた zend_class_entry *

を取得します


创建实例

大部分时间, 你的扩展都不需要自己创建实例. 而是用户空间调用new关键字创建实例并调用你的类构造器.

但你还是有可能需要创建实例, 比如在工厂方法中, ZEND_API中的object_init_ex(zval *val, zend_class_entry *ce)函数可以用于将对象实例初始化到变量中.

要注意, object_init_ex()函数并不会调用构造器. 当在内部函数中实例化对象时, 构造器必须手动调用. 下面的过程函数重演了new关键字的功能逻辑:

PHP_FUNCTION(sample2_new)
{
    int argc = ZEND_NUM_ARGS();
    zval ***argv = safe_emalloc(sizeof(zval**), argc, 0);
    zend_class_entry *ce;
    if (argc == 0 ||
        zend_get_parameters_array_ex(argc, argv) == FAILURE) {
        efree(argv);
        WRONG_PARAM_COUNT;
    }
    /* 第一个参数是类名 */
    SEPARATE_ZVAL(argv[0]);
    convert_to_string(*argv[0]);
    /* 类名存储为小写 */
    php_strtolower(Z_STRVAL_PP(argv[0]), Z_STRLEN_PP(argv[0]));
    if (zend_hash_find(EG(class_table),
            Z_STRVAL_PP(argv[0]), Z_STRLEN_PP(argv[0]) + 1,
            (void**)&ce) == FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING,
            "Class %s does not exist.",
            Z_STRVAL_PP(argv[0]));
        zval_ptr_dtor(argv[0]);
        efree(argv);
        RETURN_FALSE;
    }
    object_init_ex(return_value, ce);
    /* 如果有构造器则调用, 额外的参数将传递给构造器 */
    if (zend_hash_exists(&ce->function_table,
            Z_STRVAL_PP(argv[0]),Z_STRLEN_PP(argv[0]) + 1)) {
        /* 对象有构造器 */
        zval *ctor, *dummy = NULL;

        /* 构造器名字是类名 */
        MAKE_STD_ZVAL(ctor);
        array_init(ctor);
        zval_add_ref(argv[0]);
        add_next_index_zval(ctor, *argv[0]);
        zval_add_ref(argv[0]);
        add_next_index_zval(ctor, *argv[0]);
        if (call_user_function_ex(&ce->function_table,
                NULL, ctor,
                &dummy, /* 不关心返回值 */
                argc - 1, argv + 1, /* 参数 */
                0, NULL TSRMLS_CC) == FAILURE) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING,
                 "Unable to call constructor");
        }
        if (dummy) {
            zval_ptr_dtor(&dummy);
        }
        zval_ptr_dtor(&ctor);
    }
    zval_ptr_dtor(argv[0]);
    efree(argv);
}
ログイン後にコピー

不要忘了在php_sample2_functions中增加一个引用. 它是你的扩展的过程函数列表, 而不是类方法的列表. 为了使用php_strtolower()函数, 还需要增加#include "ext/standard/php_string.h".

这个函数是目前你实现的最复杂的一个, 其中有几个全新的特性. 首先就是SEPARATE_ZVAL(), 实际上它的功能你已经实现过很多次, 利用zval_copy_ctor()赋值值到一个临时的结构体, 避免修改原始的内容. 不过它是一个宏版本的封装.

php_strtolower()用于将类名转换为小写, 这样做是为了达到php类名和函数名不区分大小写的目的. 这只是附录B中列出的众多PHPAPI工具函数的其中一个.

EG(class_table)是一个全局变量, 所有的zend_class_entry定义都注册到它里面. 要注意的是在ZE1(php4)中这个HashTable存储了一级间访的zend_class_entry *结构体. 而在ZE2(php5)中, 它被存储为两级间访. 这应该不会是一个问题, 因为对这个HashTable的直接访问并不常见, 但知道这一点总归是有好处的.

call_user_function_ex()是你将在第20章"高级嵌入式"中看到的ZENDAPI调用的一部分. 这里你将从zend_get_parameters_ex()接收到的zval **参数栈第一个元素拿走, 这样做就是为了原封不动的将剩余的参数传递给构造器.

译注: 原著中的代码在译者的环境(php-5.4.9)中不能运行, 需要将zend_class_entry *ce修改为二级间访. 下面给出译者测试通过的代码.

PHP_FUNCTION(sample_new)
{
    int                 argc    = ZEND_NUM_ARGS();
    zval                ***argv = safe_emalloc(sizeof(zval **), argc, 0); 
    zend_class_entry    **ce;       /* 译注: 这里在译者的环境(php-5.4.9)是二级间访 */

    /* 数组方式读取所有传入参数 */
    if ( argc == 0 ||  
            zend_get_parameters_array_ex(argc, argv) == FAILURE ) { 
        efree(argv);
        WRONG_PARAM_COUNT;
    }   

    /* 隔离第一个参数(隔离为了使下面的类型转换不影响原始数据) */
    SEPARATE_ZVAL(argv[0]);
    /* 将第一个参数转换为字符串类型, 并转为小写(因为php的类名是不区分大小写的) */
    convert_to_string(*argv[0]);
    php_strtolower(Z_STRVAL_PP(argv[0]), Z_STRLEN_PP(argv[0]));
    /* 在类的HashTable中查找提供的类是否存在, 如果存在, ce中就得到了对应的zend_class_entry * */
    if ( zend_hash_find(EG(class_table), Z_STRVAL_PP(argv[0]), Z_STRLEN_PP(argv[0]) + 1, (void **)&ce) == FAILURE ) { 
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class %s does not exist.", Z_STRVAL_PP(argv[0]));
        zval_ptr_dtor(argv[0]);
        efree(argv);
        RETURN_FALSE;
    }   

    /* 将返回值初始化为查找到的类的对象 */
    object_init_ex(return_value, *ce);
    /* 检查类是否有构造器 */
    if ( zend_hash_exists(&(*ce)->function_table, Z_STRVAL_PP(argv[0]), Z_STRLEN_PP(argv[0]) + 1) ) { 
        zval    *ctor, *dummy = NULL;

        /* 将ctor构造为一个数组, 对应的用户空间形式为: array(argv[0], argv[0]), 
         * 实际上对应于用户空间调用类的静态方法时$funcname的参数形式:
         * array(类名, 方法名)
         */
        MAKE_STD_ZVAL(ctor);
        array_init(ctor);
        zval_add_ref(argv[0]);
        add_next_index_zval(ctor, *argv[0]);
        zval_add_ref(argv[0]);
        add_next_index_zval(ctor, *argv[0]);
        /* 调用函数 */
        if ( call_user_function_ex(&(*ce)->function_table, NULL, ctor, &dummy, argc - 1, argv + 1, 0, NULL TSRMLS_CC) == FAILURE ) { 
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call constructor");
        }   
        /* 如果有返回值直接析构丢弃 */
        if ( dummy ) {
            zval_ptr_dtor(&dummy);
        }
        /* 析构掉临时使用(用来描述所调用方法名)的数组 */
        zval_ptr_dtor(&ctor);
    }
    /* 析构临时隔离出来的第一个参数(类名) */
    zval_ptr_dtor(argv[0]);
    /* 释放实参列表空间 */
    efree(argv);
}
ログイン後にコピー

接受实例

有时你的函数或方法需要接受用户空间的对象参数. 对于这种目的, zend_parse_parameters()提供了两种格式的修饰符. 第一种是o(小写字母o), 它将验证传递的参数是否是对象, 并将它设置到传递的zval **中. 下面是这种方式的一个简单的用户空间函数示例, 它返回传入对象的类名.

PHP_FUNCTION(sample2_class_getname)
{
    zval *objvar;
    zend_class_entry *objce;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o",
                                &objvar) == FAILURE) {
        RETURN_NULL();
    }
    objce = Z_OBJCE_P(objvar);
    RETURN_STRINGL(objce->name, objce->name_length, 1);
}
ログイン後にコピー

第二种修饰符是O(大写字母O), 它不仅允许zend_parse_parameters()验证zval *的类型, 还可以验证所传递对象的类. 要做到这一点, 就需要传递一个zval **容易以及一个zend_class_entry *用来验证, 比如下面的实现就期望传入的是Sample2_FirstClass类的实例:

PHP_FUNCTION(sample2_reload)
{
    zval *objvar;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O",
        &objvar, php_sample2_firstclass_entry) == FAILURE) {
        RETURN_NULL();
    }
    /* 调用假想的"reload"函数 */
    RETURN_BOOL(php_sample2_fc_reload(objvar TSRMLS_CC));
}
ログイン後にコピー

访问属性

你已经看到了, 类方法可以通过getThis()获取到当前对象实例. 将这个宏的结果或其它包含对象实例的zval *与Z_OBJPROP_P()宏组合, 得到的HashTable *就包含了该对象的所有属性.

对象的属性列表是一个包含zval *的HashTable *, 它只是另外一种放在特殊位置的用户空间变量列表. 和使用zend_hash_find(EG(active_symbol_table), ...)从当前作用域获取变量一样, 你也可以使用第8章"在数组和HashTable上工作"中学习的zend_hash-API去获取或设置对象的属性.

例如, 假设在变量rcvdclass这个zval *中包含的是Sample2_FirstClass的实例, 下面的代码块就可以从它的标准属性HashTable中取到属性foo.

zval **fooval;
if (zend_hash_find(Z_OBJPROP_P(rcvdclass),
        "foo", sizeof("foo"), (void**)&fooval) == FAILURE) {
    /* $rcvdclass->foo doesn't exist */
    return;
}
ログイン後にコピー

要向属性表中增加元素, 则是这个过程的逆向过程, 调用zend_hash_add()去增加元素, 或者也可以将第8章介绍数组时介绍的add_assoc_*()族函数的assoc替换为property来处理对象.

下面的构造器函数为Sample2_FirstClass的实例提供了一些预先设置的默认属性:

PHP_NAMED_FUNCTION(php_sample2_fc_ctor)
{
    /* 为了简洁, 同时演示函数名可以是任意的, 这里实现的函数名并不是类名 */
    zval *objvar = getThis();

    if (!objvar) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING,
                        "Constructor called statically!");
        RETURN_FALSE;
    }

    add_property_long(objvar, "life", 42);
    add_property_double(objvar, "pi", 3.1415926535);
    /* 构造器的返回值会被忽略(请回顾前面构造器的例子) */
}
ログイン後にコピー

现在可以通过php_sample2_firstclass_functions列表将它连接到对象的构造器:

PHP_NAMED_FE(sample2_firstclass, php_sample2_fc_ctor, NULL)
ログイン後にコピー

译注: 由于前面的sample_new()工厂函数在call_user_function_ex()调用构造器时使用的是静态方法的调用格式, 因此, 如果是使用这个工厂函数触发的构造器调用, getThis()就不会有期望的结果. 因此译者对例子进行了相应的修改, 读者如果在这块遇到问题可以参考译者的代码.

PHP_FUNCTION(sample_new)
{
    int                 argc    = ZEND_NUM_ARGS();
    zval                ***argv = safe_emalloc(sizeof(zval **), argc, 0); 
    zend_class_entry    **ce;       /* 译注: 这里在译者的环境(php-5.4.9)是二级间访 */

    /* 数组方式读取所有传入参数 */
    if ( argc == 0 ||  
            zend_get_parameters_array_ex(argc, argv) == FAILURE ) { 
        efree(argv);
        WRONG_PARAM_COUNT;
    }   

    /* 隔离第一个参数(隔离为了使下面的类型转换不影响原始数据) */
    SEPARATE_ZVAL(argv[0]);
    /* 将第一个参数转换为字符串类型, 并转为小写(因为php的类名是不区分大小写的) */
    convert_to_string(*argv[0]);
    php_strtolower(Z_STRVAL_PP(argv[0]), Z_STRLEN_PP(argv[0]));
    /* 在类的HashTable中查找提供的类是否存在, 如果存在, ce中就得到了对应的zend_class_entry * */
    if ( zend_hash_find(EG(class_table), Z_STRVAL_PP(argv[0]), Z_STRLEN_PP(argv[0]) + 1, (void **)&ce) == FAILURE ) { 
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class %s does not exist.", Z_STRVAL_PP(argv[0]));
        zval_ptr_dtor(argv[0]);
        efree(argv);
        RETURN_FALSE;
    }   

    /* 将返回值初始化为查找到的类的对象 */
    object_init_ex(return_value, *ce);
    /* 检查类是否有构造器 */
    if ( zend_hash_exists(&(*ce)->function_table, Z_STRVAL_PP(argv[0]), Z_STRLEN_PP(argv[0]) + 1) ) { 
#define DYNAMIC_CONSTRUCTOR
#ifndef DYNAMIC_CONSTRUCTOR
        zval    *ctor;
#endif
        zval    *dummy = NULL;

#ifndef DYNAMIC_CONSTRUCTOR
        /* 将ctor构造为一个数组, 对应的用户空间形式为: array(argv[0], argv[0]), 
         * 实际上对应于用户空间调用类的静态方法时$funcname的参数形式:
         * array(类名, 方法名)
         */
        MAKE_STD_ZVAL(ctor);
        array_init(ctor);
        zval_add_ref(argv[0]);
        add_next_index_zval(ctor, *argv[0]);
        zval_add_ref(argv[0]);
        add_next_index_zval(ctor, *argv[0]);
#endif
        /* 调用函数 */
        if ( call_user_function_ex(&(*ce)->function_table,
#ifndef DYNAMIC_CONSTRUCTOR
                NULL, ctor,
#else
                &return_value, *argv[0],
#endif
                &dummy, argc - 1, argv + 1, 0, NULL TSRMLS_CC) == FAILURE ) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call constructor");
        }
        /* 如果有返回值直接析构丢弃 */
        if ( dummy ) {
            zval_ptr_dtor(&dummy);
        }
#ifndef DYNAMIC_CONSTRUCTOR
        /* 析构掉临时使用(用来描述所调用方法名)的数组 */
        zval_ptr_dtor(&ctor);
#endif
    }
    /* 析构临时隔离出来的第一个参数(类名) */
    zval_ptr_dtor(argv[0]);
    /* 释放实参列表空间 */
    efree(argv);
}
ログイン後にコピー

译注: 现在, 就可以用函数中是否定义DYNAMIC_CONSTRUCTOR这个宏来切换构造器的调用方式, 以方便读者理解.

小结

尽管ZE1/php4提供的类功能最好少用, 但是由于当前php4在产品环境下还是广泛使用的, 因此做这个兼容还是有好处的. 本章涉及的技术可以让你灵活的编写各种功能的代码, 它们现在可以编译运行, 并且未来也将继续可以工作.

下一章, 你将看到php5中真正的面向对象, 如果你想要OOP, 从中你就可以得到升级的理由, 并且, 升级后你肯定再也不愿回头.


目录
上一章: 资源数据类型

下一章: php5对象

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!