霜天部落 | 关注LAMP高性能、高并发架构的设计与研究

用C实现PHP类扩展开发

上次写了个 初步介绍php扩展开发 ,简单介绍了PHP扩展函数的实现方法,最近有个朋友问到类扩展怎么实现,关于类扩展的示例,网上很少,于是写了个PHP类扩展的例子。

需要实现的类为:

    class Vector2D
    {
            private  $_x;
            private  $_y;

            /**
             * Constructor.
             */
            public function __construct($x = 0, $y = 0)
            {
                    $this->_x = $x;
                    $this->_y = $y;
            }

            /**
             * Generates a copy of $this vector.
             * @return Vector2D A copy of $this vector.
             */
            public function mycopy()
            {
                    return new Vector2D($this->_x, $this->_y);
            }

            /**
             * Sets $this vector's x and y values, and thus length, to zero.
             * @return Vector2D A reference to $this vector.
             */
            public function zero()
            {
                    $this->_x = 0;
                    $this->_y = 0;
                    return $this;
            }

            /**
             * Whether or not $this vector is equal to zero, i.e. its x, y, and length are zero.
             * @return Boolean True if vector is zero, otherwise false.
             */
            public function isZero()
            {
                    return $this->_x == 0 && $this->_y == 0;
            }
            public function getX()
            {
                    return $this->_x;
            }
            public function getY()
            {
                    return $this->_y;
            }
    }

环境:PHP 5.2.14 CentOS 5.5

第一步:建立扩展骨架

cd php-5.2.14/ext./ext_skel
–extname=laiwenhui

第二步:修改编译参数

cd php-5.2.14/ext/laiwenhui
vi config.m4

第三步:编写代码

vim php_laiwenhui.h

在 PHP_FUNCTION(confirm_laiwenhui_compiled); /* For testing, remove later. */ 后面增加声明函数

PHP_METHOD(Vector2D, __construct);
PHP_METHOD(Vector2D, mycopy);
PHP_METHOD(Vector2D, zero);
PHP_METHOD(Vector2D, isZero);
PHP_METHOD(Vector2D, getX);
PHP_METHOD(Vector2D, getY);

vim laiwenhui.c

声明方法的参数,注册到函数表中

    ZEND_BEGIN_ARG_INFO(arg_construct, 2)    //声明arg_construct为含有x,y的2个参数   
    ZEND_ARG_INFO(0, x)   
    ZEND_ARG_INFO(0, y)   
    ZEND_END_ARG_INFO()   
      
    zend_function_entry laiwenhui_functions[] = {   
        PHP_FE(confirm_laiwenhui_compiled,      NULL)           /* For testing, remove later. */  
        PHP_ME(Vector2D, __construct, arg_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)//   
        PHP_ME(Vector2D, mycopy, NULL, ZEND_ACC_PUBLIC)   
        PHP_ME(Vector2D, zero, NULL, ZEND_ACC_PUBLIC)   
        PHP_ME(Vector2D, isZero, NULL, ZEND_ACC_PUBLIC)   
        PHP_ME(Vector2D, getX, NULL, ZEND_ACC_PUBLIC)   
        PHP_ME(Vector2D, getY, NULL, ZEND_ACC_PUBLIC)   
        {NULL, NULL, NULL}      /* Must be the last line in laiwenhui_functions[] */  
      };  

ZEND_ACC_CTOR 表示为构造函数
ZEND_ACC_PUBLIC为访问权限标记,表示public访问权限

在模块初始化函数中注册并初始化类

    zend_class_entry *Vector2D_ce; //类结构变量
    PHP_MINIT_FUNCTION(laiwenhui)
    {
            /* If you have INI entries, uncomment these lines
            REGISTER_INI_ENTRIES();
            */
            zend_class_entry Vector2D;

            INIT_CLASS_ENTRY(Vector2D, "Vector2D", laiwenhui_functions);//第二个参数为类名,第三个参数为函数表
            Vector2D_ce = zend_register_internal_class_ex(&Vector2D, NULL, NULL TSRMLS_CC);//注册类

            zend_declare_property_null(Vector2D_ce, ZEND_STRL("_x"), ZEND_ACC_PRIVATE TSRMLS_CC);//初始化_x属性
            zend_declare_property_null(Vector2D_ce, ZEND_STRL("_y"), ZEND_ACC_PRIVATE TSRMLS_CC);//初始化_y属性

            return SUCCESS;
    }

在文件最后面增加具体类方法的实现代码:

       
    PHP_METHOD(Vector2D, __construct) {
            long int x,y;
            //获取x,y参数
            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &x, &y) == FAILURE) {
                    WRONG_PARAM_COUNT;
            }

            //更改_x,_y属性值
            zend_update_property_long(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_x"), x TSRMLS_CC);
            zend_update_property_long(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_y"), y TSRMLS_CC);

            RETURN_TRUE;
    }

    PHP_METHOD(Vector2D, mycopy) {
            zval *copy_obj;
            zval *x_value,*y_value;

            MAKE_STD_ZVAL(copy_obj);
            //初始化对象,对象所属的类为Vector2D_ce
            object_init_ex(copy_obj, Vector2D_ce);

            //获得_x,_y的属性值
            x_value = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_x"), 0 TSRMLS_CC);
            y_value = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_y"), 0 TSRMLS_CC);

            //更新Vector2D_ce类对象copy_obj的属性值_x,_y
            zend_update_property(Z_OBJCE_P(getThis()), copy_obj, ZEND_STRL("_x"), x_value TSRMLS_CC);
            zend_update_property(Z_OBJCE_P(getThis()), copy_obj, ZEND_STRL("_y"), y_value TSRMLS_CC);
            //返回该对象
            RETURN_ZVAL(copy_obj, 1, 0);
    }

    PHP_METHOD(Vector2D, zero) {
           //更改_x,_y的属性值为0
            zend_update_property_long(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_x"), 0 TSRMLS_CC);
            zend_update_property_long(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_y"), 0 TSRMLS_CC);
            //返回当前类实例
            RETURN_ZVAL(getThis(), 1, 0);;
    }

    PHP_METHOD(Vector2D, isZero) {

            zval *zx, *zy;
            long x,y;
            // 获得_x,_y的属性值
            zx = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_x"), 0 TSRMLS_CC);
            zy = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_y"), 0 TSRMLS_CC);

            x = Z_LVAL_P(zx);
            y = Z_LVAL_P(zy);

            if(x==0 && y==0){
                    RETURN_BOOL(1);
            }else{
                    RETURN_BOOL(0);
            }

    }

    PHP_METHOD(Vector2D, getX) {

            zval *zx;
            long x;

            zx = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_x"), 0 TSRMLS_CC);
            x = Z_LVAL_P(zx);

            RETURN_LONG(x);
    }

    PHP_METHOD(Vector2D, getY) {

            zval *zy;
            long y;

            zy = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_y"), 0 TSRMLS_CC);
            y = Z_LVAL_P(zy);

            RETURN_LONG(y);
    }

第四步:编译代码

      
    cd php-5.2.6/ext/laiwenhui   
    /opt/module/php/bin/phpize   
    ./configure --with-php-config=/opt/module/php/bin/php-config   
    make   
    make install  

我的PHP安装路径为:/opt/module/php
这个时候会生成一个文件 /opt/module/php/lib/php/extensions/no-debug-non-zts-20060613/laiwenhui.so

编辑PHP配置文件php.ini,添加扩展:

vim php.ini

在[PHP]模块下增加:extension = laiwenhui.so

;extension=php_zip.dll
extension = laiwenhui.so

把php.ini 文件中的 extension_dir 修改为该目录:
extension_dir = “/usr/local/php/lib/php/extensions/no-debug-non-zts-20060613/”

; Directory in which the loadable extensions (modules) reside.
extension_dir = “/opt/module/php/lib/php/extensions/no-debug-non-zts-20060613/”

第五步:检查安装结果

1.重启apache或者php-fpm
2./opt/module/php/bin/php -m

看下是否有包含laiwenhui扩展。

第六步:执行测试代码

在网站根目录创建test.php

vim test.php

代码内容如下:

    $a = new Vector2D(1,3);
    var_dump($a->getX());
    var_dump($a->getY());
    var_dump($a->sZero());
    echo "
"; $b = $a->mycopy(); var_dump($b->getX()); var_dump($b->getY()); var_dump($b->isZero()); echo "
"; $c = $a->zero(); var_dump($c->getX()); var_dump($c->getY()); var_dump($c->isZero());

执行后结果为:

int(1) int(3) bool(false)
int(1) int(3) bool(false)
int(0) int(0) bool(true)

怎么样,类扩展也很简单吧,呵呵。

原文链接: http://developerblog.org/?p=202