[ PHP 内核与扩展开发系列] 类与面向对象:接口实现与类的继承

定义接口

定义一个接口还是很方便的,我先给出一个 PHP 语言中的实现

<?php
    interface i_myinterface
    {
        public function hello();
    }

其在扩展中的实现是这样的:

zend_class_entry *i_myinterface_ce;

static zend_function_entry i_myinterface_method[] = {
    ZEND_ABSTRACT_ME(i_myinterface, hello, NULL) //注意这里的 null 指的是arginfo
    {NULL,NULL,NULL}
};

ZEND_MINIT_FUNCTION(test)
{   
    zend_class_entry ce;
    INIT_CLASS_ENTRY(ce, "i_myinterface", i_myinterface_method);

    i_myinterface_ce = zend_register_internal_interface(&ce TSRMLS_CC);
    return SUCCESS;
}

我们使用 ZEND_ABSTRACT_ME() 宏函数来为这个接口添加函数,它的作用是声明一个似虚函数的东西,不用实现。也就是说我们不用为其添加 ZEND_METHOD(i_myinterface,hello){...} 的实现。但是这个宏函数只能为我们实现 public 类型方法的声明,如果有其它特殊需要,需要使用 ZEND_FENTRY() 宏函数来实现,因为 ZEND_ABSTRACT_ME 只不过是后者的一种封装。

下面我们在 PHP 语言中使用这个接口:

<?php
    class sample implements i_myinterface
    {
        public $name = "hello world!";
        public function hello()
        {
                echo $this->name."\n";
        }
    }

    $obj = new sample();
    $obj->hello();

类的继承与接口实现

在定义一个类时往往会使其继承某个父类或者实现某个接口,在扩展中实现这个功能非常方便。下面我先给出 PHP 语言中对应的实现代码:

<?php
    interface i_myinterface
    {
            public function hello();
    }

    class parent_class implements i_myinterface
    {
            public function hello()
            {
                echo "Good Morning!\n";
        }
    }

    final class sub_class extends parent_class
    {
        public function call_hello()
        {
                $this->hello();
        }
    }

上面的代码我们已经非常熟悉了,它们在 PHP 扩展中的实现是这样的:

// 三个zend_class_entry
zend_class_entry *i_myinterface_ce, *parent_class_ce, *sub_class_ce;

// parent_class的hello方法
ZEND_METHOD(parent_class, hello)
{
    php_printf("hello world!\n");
}

// sub_class的call_hello方法
ZEND_METHOD(sub_class, call_hello)
{
    //这里涉及到如何调用对象的方法,详细内容下一章叙述
    zval *this_zval;
    this_zval = getThis();
    zend_call_method_with_0_params(&this_zval, sub_class_ce, NULL, "hello", NULL);
}

// 各自的zend_function_entry
static zend_function_entry i_myinterface_method[] = {
    ZEND_ABSTRACT_ME(i_myinterface, hello, NULL)
    {NULL,NULL,NULL}
};

static zend_function_entry parent_class_method[] = {
    ZEND_ME(parent_class, hello, NULL, ZEND_ACC_PUBLIC)
    {NULL,NULL,NULL}
};

static zend_function_entry sub_class_method[]={
    ZEND_ME(sub_class, call_hello, NULL, ZEND_ACC_PUBLIC)
    {NULL,NULL,NULL}
};

ZEND_MINIT_FUNCTION(test)
{
    zend_class_entry s_ce, p_ce, i_ce;
    INIT_CLASS_ENTRY(i_ce, "i_myinterface", i_myinterface_method);
    i_myinterface_ce = zend_register_internal_interface(&i_ce TSRMLS_CC);


    //定义父类,最后使用zend_class_implements函数声明它实现的接口
    INIT_CLASS_ENTRY(p_ce, "parent_class", parent_class_method);
    parent_class_ce = zend_register_internal_class(&p_ce TSRMLS_CC);
    zend_class_implements(parent_class_ce TSRMLS_CC, 1, i_myinterface_ce);

    // 定义子类,使用zend_register_internal_class_ex函数
    INIT_CLASS_ENTRY(s_ce, "sub_class", sub_class_method);
    sub_class_ce = zend_register_internal_class_ex(&s_ce, parent_class_ce, "parent_class" TSRMLS_CC);
    //注意:ZEND_ACC_FINAL是用来修饰方法的,而ZEND_ACC_FINAL_CLASS是用来修饰类的
    sub_class_ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
    return SUCCESS;
}

这样,当我们在 PHP 语言中进行如下操作时,便会得到预期的输出:

<?php
    $obj = new sub_class();
    $obj->call_hello();

输出内容如下:

hello world!

这里的 ZEND_ABSTRACT_ME() 宏函数比较特殊,它会声明一个 abstract public 类型的函数,这个函数不需要我们实现,因此也就不需要相应的 ZEND_METHOD(i_myinterface,hello){...} 的实现。一般来说,一个接口是不能设计出非 public 类型的方法的,因为接口暴露给使用者的都应该是一些公开的信息。不过如果你非要这么设计,那也不是办不到,只是别用 ZEND_ABSTRACT_ME() 宏函数就行了,而是用它的底层实现 ZEND_FN() 宏函数:

//它可以对应<?php ...public static function apply_request();...的接口方法声明
static zend_function_entry i_myinterface[]=
{
    ZEND_FENTRY(apply_request, NULL, NULL, ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_PUBLIC)
    {NULL,NULL,NULL}
};

这样,只要掩码中有 ZEND_ACC_ABSTRACT,便代表是一个不需要具体实现的方法。ZEND_FENTRY 其实是 ZEND_MEZEND_FE 的最终实现,现在我们把这一组宏罗列一次展开,供你参考使用:

#define ZEND_FENTRY(zend_name, name, arg_info, flags)   { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags },

#define ZEND_FN(name) zif_##name

#define ZEND_MN(name) zim_##name

#define ZEND_FE(name, arg_info)                     ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)

#define ZEND_ME(classname, name, arg_info, flags)   ZEND_FENTRY(name, ZEND_MN(classname##_##name), arg_info, flags)

学院君 has written 703 articles

资深PHP工程师,Laravel学院院长

One thought on “[ PHP 内核与扩展开发系列] 类与面向对象:接口实现与类的继承

发表评论

标记为*的字段是必填项(邮箱地址不会被公开)

你可以使用这些HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>