用C语言扩展PHP功能


用c语言扩展php功能

建议读者群:熟悉c,linux,php
   php经过最近几年的发展已经非常的流行,而且php也提供了各种各样非常丰富的函数。
但有时候我们还是需要来扩展php。比如:我们自己开发了一个数据库系统,而且有自己的
库函数来操作数据库,这时候,如果想在php中来操作我们自己的数据库的话,就必须自己
扩展php了,像mysql,postgresql,之所以php能够提供这些数据库操作函数,也都是扩展了
php的结果。
    先看看php的源代码结构:
    $ cd php-4.4.2/ext
    $ ls
    会显示出目前该php发行版本中所有的扩展模块。
    如果想深入学习的话,可以去看看mysql或者postgresql的php扩展实现。
   
    下面,我们通过一个简单的模块(mypg)来实现对postgresql的数据库操作。
    $ cd php-4.4.2/ext
    $ ./ext_skel –extname=mypg
    该程序会自动生成mypg目录
    $ cd mypg
    $ ls
    config.m4  credits  experimental  mypg.c  mypg.php  php_mypg.h  tests
   
    php已经自动为我们生成了一些必要的文件和示范代码。
    我们需要作一些修改才能正常的编译和使用该mypg模块。
    $ vi config.m4
    修改成如下内容:
php_arg_enable(mypg, whether to enable mypg support,
               [  –enable-mypg           enable mypg support])

if test “$php_mypg” != “no”; then
  dnl write more examples of tests here…

  dnl # –with-mypg -> check with-path
  dnl search_path=”/usr/local /usr”     # you might want to change this
  dnl search_for=”/include/mypg.h”  # you most likely want to change this
  dnl if test -r $php_mypg/; then # path given as parameter
  dnl   mypg_dir=$php_mypg
  dnl else # search default path list
  dnl   ac_msg_checking([for mypg files in default path])
  dnl   for i in $search_path ; do
  dnl     if test -r $i/$search_for; then
  dnl       mypg_dir=$i
  dnl       ac_msg_result(found in $i)
  dnl     fi
  dnl   done
  dnl fi
  dnl
  dnl if test -z “$mypg_dir”; then
  dnl   ac_msg_result([not found])
  dnl   ac_msg_error([please reinstall the mypg distribution])
  dnl fi

  dnl # –with-mypg -> add include path
  dnl php_add_include($mypg_dir/include)

  dnl # –with-mypg -> check for lib and symbol presence
  dnl libname=mypg # you may want to change this
  dnl libsymbol=mypg # you most likely want to change this

  dnl php_check_library($libname,$libsymbol,
  dnl [
  dnl   php_add_library_with_path($libname, $mypg_dir/lib, mypg_shared_libadd)
  dnl   ac_define(have_mypglib,1,[ ])
  dnl ],[
  dnl   ac_msg_error([wrong mypg lib version or lib not found])
  dnl ],[
  dnl   -l$mypg_dir/lib -lm -ldl
  dnl ])
  dnl
  dnl php_subst(mypg_shared_libadd)

  php_new_extension(mypg, mypg.c, $ext_shared)
fi
 
dnl开头的为注释,其实我们也只是把某些注释去掉了。

   然后修改php_mypg.h,内容为:
#ifndef php_mypg_h
#define php_mypg_h

extern zend_module_entry mypg_module_entry;
#define phpext_mypg_ptr &mypg_module_entry

#ifdef php_win32
#define php_mypg_api __declspec(dllexport)
#else
#define php_mypg_api
#endif

//模块初始化时调用函数
php_minit_function(mypg);

//我们的数据库连接函数
php_function(mypg_connect);
//我们的数据库操作函数
php_function(mypg_execute);
//我们的数据库关闭函数
php_function(mypg_close);

#ifdef zts
#include “tsrm.h”
#endif

#endif  /* php_mypg_h */   

继续修改mypg.c,内容改为:
#ifdef have_config_h
#include “config.h”
#endif

#include “php.h”
#include “php_ini.h”
#include “ext/standard/info.h”
#include “php_mypg.h”
#include “libpq-fe.h”

int le_link;
function_entry mypg_functions[] = {
        php_fe(mypg_connect,                                                    null)
        php_fe(mypg_execute,                                                    null)
        php_fe(mypg_close,                                                      null)
        {null, null, null}
};
zend_module_entry mypg_module_entry = {
        standard_module_header,
        “mypg”, mypg_functions, php_minit(mypg), null, null, null,
        null, no_version_yet, standard_module_properties
};

zend_get_module(mypg)

//数据库链接关闭函数
static void _close_mypg_link(zend_rsrc_list_entry *rsrc tsrmls_dc)
{
        pgconn *link = (pgconn *)rsrc->ptr;
        pqfinish(link);
}
php_minit_function(mypg)
{
    //注册资源回收函数,如果没有显示用mypg_close关闭数据库连接的化,php会自动调用该函数释放资源
    le_link = zend_register_list_destructors_ex(_close_mypg_link, null, “mypg link”, module_number);
    return success;

}

//连接数据库
static void php_mypg_do_connect(internal_function_parameters)
{
        pgconn *link;

        //只接受一个函数参数
        if(zend_num_args() != 1)
        {
            wrong_param_count;
        }

        zval **connect_info;

        /* get the connection information string */
        if (zend_get_parameters_ex(1, &connect_info) == failure) {
            return_false;
        }

        /* create our resource hash key */
        convert_to_string_ex(connect_info);

        //调用libpq, 执行数据库连接
        if ((link=pqconnectdb(z_strval_pp(connect_info))) && pqstatus(link)!=connection_ok) {
            return_false;
        }
       
        //将return_value注册为得到的数据库连接
        /* add it to the list */
        zend_register_resource(return_value, link, le_link);
}
php_function(mypg_connect)
{
    php_mypg_do_connect(internal_function_param_passthru);
}

//我们自己定义的数据库操作函数
php_function(mypg_execute)
{
        zval **query, **link = null;
        int id;
        pgconn *conn;
        pgresult *res;

        //参数为2, 1:执行的sql  2:数据库链接句柄
        switch(zend_num_args()) {
                case 2:
                        if (zend_get_parameters_ex(2, &query, &link)==failure) {
                                wrong_param_count;
                        }
                        break;
                default:
                        wrong_param_count;
                        break;
        }
       
        //取得数据库链接
        zend_fetch_resource(conn, pgconn *, link, -1, “mypg link”,  le_link);

        convert_to_string_ex(query);
      
        //通过libpq执行sql
        res = pqexec(conn, z_strval_pp(query));

        if (pqresultstatus(res) != pgres_command_ok)
        {
                return_false;
        }

        pqclear(res);
        
        return_true;

}
php_function(mypg_close)
{
        zval **link;
        int id;
        pgconn *conn;

        switch (zend_num_args()) {
                case 1:
                        if (zend_get_parameters_ex(1, &link)==failure) {
                                return_false;
                        }
                        break;
                default:
                        wrong_param_count;
                        break;
        }
        if(link == null)
        {
                return_false;
        }

        //根据资源句柄取得资源
        zend_fetch_resource(conn, pgconn *, link, -1,”mypg link”,  le_link);
       
        //删除该资源,php自动调用前面注册的函数来关闭数据库链接
        zend_list_delete(z_resval_pp(link));

        return_true;

}
    mypg模块就基本开发完成了,我们需要重新为php生成configure文件。
   
    $ cd php-4.4.2
    $ rm -rf autom4te.cache/; rm -f configure
    $ ./buildconf  –force
    此时php会读取所有ext/子目录下的config.m4,并集成到新生成的configure脚本中。
    如果没有意外,运行如下命令会得到如下结果:
    $ ./configure –help | grep mypg
      –enable-mypg           enable mypg support
    编译php:
    $ ./configure —enable-mypg
    由于要链接libpq.so,可以vi makefile
    在extra_libs后面加上:-lpq 来把libpq编译进去,当然也可以通过修改mypg的config.m4来实现,
    这里不在啰嗦。
    $ make
    $ make install
   
    编写我们的模块测试脚本:testmypg.php
/*
* this is the sample php code
* to invoke our module: mypg
*/
$link = mypg_connect(”hostaddr=172.16.19.8 dbname=pgsql user=pgsql password=12345″);
if($link)
{
        echo “successfully connected  to postgresql.\n”;
}
else
{
        die(”connect error.\n”);
}

$sql = “insert into test values(’12345′,’23145′)”;

mypg_execute($sql, $link);

$link2 = $link;

mypg_execute($sql, $link2);
mypg_execute($sql, $link);
mypg_close($link);

echo “database query ok.\n”;

?>
运行该php程序,如果在postgresql的pgsql库中有table: test (col1 varchar(100), col2 varchar(100))
里面应该已经有2条记录了。

    编写php模块扩展需要很多php源码的知识,可以通过参考其他module或者直接阅读php代码来逐步提高自己
的开发能力。
    php官方的站点上也有一些文章可供参考:http://cn2.php.net/manual/en/internals2.php
    希望这篇文章能够给想扩展php的兄弟一个大概的方向!


申明:本区内容收集自网络,如有署名问题请速与我们(luokelong#it168.com)联系,感谢您的支持。
7上一页  下一页8

制作:罗可龙 电邮:luokelong(at)it168.com