2006年04月16日

fishchen <fishchen#mail.china.com>

今天看到很多刚来公司的毕业生,在研究怎么用面向对象实现一个项目。突然觉得有些东西不吐不快。现在很多人都在说什么什么是OOP,用C++/java怎么写OOP,用C就不OOP,于是就说C不能OOP了。我写下以下内容前特意上网google了一下,发现没有太多近似的文章,于是决定还是写出来。以下可能还不是一个成熟的想法,只是突发的念头写出来,有不妥还望指出。

什么是OOP?其实概念都可以说出一堆,但是说到底它只是一种思想,不会受到语言的限制,只不过用C++/java这类产生时就已经应用了这个思想的语言去实践会更简单,但并不能说用C就不能写出带有OOP思想的程序了。举一个很简单的例子,也许会引起大家的思考。

如果我们用OOP要写一个操作文件的类,用C++去写,大概会是这样:
class CFile {
    int CFile( … ); //构造函数, 传入文件路径等
    int ~CFile( … ); //析构函数
    int read( … );  //读文件的函数
    int write( … ); //写文件的函数
    int get_status( … ); //取文件状态
    …    //其它函数或属性
}


这很容易理解,在处理一个文件的时候就声明一个实际就好,这个不多说,大概会是这样:
CFile obj_file( … );
obj_file.read();
obj_file.write();
obj_file.get_status();

然后生命周期结束系统自动调用析构函数。


那在C中,是怎么处理的呢?
有一个类似: FILE
有fopen/fclose/fread/fwrite/fstat等函数,使用的时候,跟使用一个C++的类是何其的相似:
FILE fp;             //=声明一个类的实例
fp = fopen( …. );  //=调用构造函数
fread( … , fp );   //=读文件
fwrite( … , fp );  //=写文件
fstat( fp );         //=取文件状态(与实际用法有不同,这里只是做一种类似的例子)
fclose( fp );        //=调用析构函数

其实,这个只不过使用起来没有用类实现的那样方便,但是却充满了OOP的思想。这种使用的方法,其实大多用C写过程序的人都会用到,其实这就是OOP呀!理论上说,OOP上的所有思想都可以用C(当然用其它语言也是)来实现的,只不过不如C++/java这类语言有先天的的优势罢了。

也许现在有人会说,类的继承,接口函数等等在C中是没有的,其实用心想想,真的不能实现吗?
其实实现的的方法可能会多种多样,只不过的确不如C++中方便:(.

什么是OOP?只是一种编程的思想,不需要拘泥于语言的实现,发挥我们的想象力吧。

参考资料:
C语言中的面向对象

2006年04月11日

好久没看到独孤木专栏的文章了:)

獨孤木專欄─義和團式的專案計劃 (上)
獨孤木專欄─義和團式的專案計劃 (中)
獨孤木專欄─義和團式的專案計劃 (下)

2004年12月18日

  前几天和J及H等吃饭的时候聊到,现在我们很多程序都是用int类型来表示unix timestamp,但是int型的最大值是2147483647,这样最大只能表示到2038年1月左右,再住后呢?我们有多少程序不支持?这会不会引起另一个类似千年虫的问题?

    也许很多程序还不会遇到这个问题,但是前一阵已经出现了有人交会员超过了2038年然后系统溢出把用户的到期日算到1970年的案例了,是不是要提前应对了呢?

查了一下,glib是这样定义:
time.h:
typedef __time_t time_t;

bits/types.h:
typedef long int __time_t;

以后要不要用long int来表示timestamp呢?要考虑的事了。

2004年09月06日

原来还是中国人破解的。看来加密算法又要换代革新了。

http://www.freebsdchina.org/document_28_14697.html
http://www.md5crk.com/
http://www.readyresponse.org/index.php?option=content&task=view&id=94&Itemid=2
http://www.technewsworld.com/story/35926.html

但可能在简单的实际应用中,还没有太多实际的影响。

 

2004-10-13 添加:

# 回复:关于王小云破解MD5之我见 2004-09-10 8:56 PM XChilde
攻破MD5并不一定要通过使用这种方式来构造碰撞进行攻击。
王小云的结果更重要的意义在于证明了MD5的Hash值的分布是有明显的规律的。也就是说,穷举攻击的难度远低于原本相像的难度。
我看王小云的论文里边描述的方法,在构造碰撞的时候似乎对原文只有很少的几个地方有修改,其他大部分内容都是相同的。这样即使是文本内容,要伪造也是很容易的。
而且既然已经知道穷攻击的难度偏低,通过比如在文本内容的32个不同位置插入或不插入空白,就可以立刻得到2^32个意义相同的明文,从中选择合适的就可以了。从前之所以不这样做,是因为并不知道Hash值的分布有明显的规律。现在已经不一样了。
至于口令的问题,一般都允许大小写字母、数字和下划线(其实很多地方也允许其他符号),这样算起来每个字符基本相当于6个二进制位,这样只要允许超过21个字符的口令就够了。而且如果密码短到样本空间不够的时候,穷举攻击都成了可行的了。
上边有些人给了一个Hash值,让别人找到相应的明文寄给他,大概是根本没有理解王小云他们的结果到底是怎么回事。这次的结果是“已知明文攻击”,也就是说要知道原文。你可以把原文寄给他们,我想他们一定能在一小时内给你找到一个碰撞。当然现在既然知道MD5的基本设计有缺陷,可能不久就可以做“唯密文攻击”了。
我不明白为什么还有那么多人不相信这次的所公布的结果是真的。他们自己并不懂密码学,可是就是坚信MD5永远是安全的。真是不理解。

 

2004年06月10日

 C++扩展PHP

version 0.1.0

 

作者:J Smith  <dark_panda#hushmail.com> <jay#php.net>  6-May-2004

翻译:fishchen  <fishchen@mail.china.com>                10-Jun-2004

原文地址:http://bugs.tutorbuddy.com/php5cpp/php5cpp/

 

这个文档简单介绍了怎么使用C++为PHP编写一个扩展模块。我已经被问及这个问题无数次了,所以我决定为后来者写一个简单的HOWTO。这篇文章只会说明一些要点和关键之处,如果你希望学习C++或PHP,请查阅其它的文档。

顺便,在本文中我们也会看一下在PHP5中怎么把C++类影射到PHP中。文章中的例子可以在PHP4和PHP5的环境下使用,但是有一个小小的问题:扩展的PHP类只能在PHP5中使用。:P

基本上,我们会介绍怎么样为C++类创建一个可以在PHP4和PHP5下使用的结构化接口,及可以在PHP5下使用的对象化接口。

这个HOWTO是参考了一些实现得很好的PHP5扩展模块后写成的。如:Sqlite和SimpleXML扩展就是教导大家怎样在PHP5中扩展类的很好例子。但我的那些作品除外,只有cryptopp-php使用了很多特性。(还没有发布的PHP5将可以支持cryptopp-php 0.0.14。)

注意:PHP5目前仍在预发布状态,它可能还会做一些改动,尽管文档中的代码我已经在最新CVS版本的PHP 5环境下测试过,但在你读到这篇文章的时候可能还要做一些改动才能正常工作。如果文档中的代码在最新PHP 5中不能使用,请知会我,我会做相应的修正。

 1节. 开始之前

开始前,我要说明:这篇文章所描述的主要是在UNIX的PHP环境上的。当然,我会提及一些在Windows上的开发。但是我大部分的编码都是在UNIX系统上的,所以我会更多的介绍一下我所了解的那一部分。

另外一点我要说明的是:文中所介绍的方法在PHP 4.3.x和PHP 5下都是可行的。尽管我们在开始的时候会基于PHP5来介绍,但是你会发现这些方法在PHP 4.3.x中也是可行的。

我在本文中有一些约定

$PHP_HOME 是指你的PHP源代码的位置,如:你解开的PHP源代码包所放的位置。在我的系统中指的是:/home/jay/setup/php/php-x.x.x.

我们用来做例子的模块叫做php5cpp.

 

 2节.安装

在你用C++编写PHP扩展前,你先要搭建一个基本的扩展模块的架构。在UNIX下,你可以运行一个在 $PHP_HOME/ext 下叫做ext_skel shell脚本。先切换到 $PHP_HOME/ext 目录和执行那个shell脚本,并用 --extname 参数为你的扩展模块命名。

jay@monty ~ $ cd setup/php/php-5.x.x/ext

jay@month ext $ ./ext_skel –extname php5cpp

Windows系统,目前PHP CVS代码中,可以使用位于 $PHP_HOME/extext_skel_win32.php 的PHP脚本是。也许它会成为PHP5的一部分及被PHP 4.x的分支包含。但这只是我大胆的猜想,我并不知道会不会实现…

这样,在$PHP_HOME/ext/php5cpp下,我们已经有了一个基本的PHP扩展模块架构。唯一的问题是,它是为C搭建的,而不是为C++。

 3节.修改config.m4

现在我们要修改那个扩展模块的config.m4 文件以支持C++。

你不需要做太多的改动,要做的只是告诉编译PHP的系统,你的模块是使用C++的,而且需要连接C++标准库。下边是一个删去自动生成的注释后,php5cpp 扩展模块的config.m4文件的例子:

PHP_ARG_ENABLE(php5cpp, for php5cpp support,

[ --enable-php5cpp            Enable php5cpp support])

 

if test “$PHP_php5cpp” != “no” ; then

    PHP_REQUIRE_CXX()

    PHP_NEW_EXTENSION(php5cpp, php5cpp.cpp, $ext_shared)

fi

注意其中的PHP_REQUIRE_CXX(),和php5cpp.c 已经变成了 php5cpp.cpp 了。

 4节.编写代码

修改完config.m4 后,你可以编写代码了。记住把php5cpp.c 修改成C++文件的名字。根据前边 config.m4的修改,在这里我们把它改成 php5cpp.cpp.

现在你可以开始编写你的代码了。但是你如果现在编译这个扩展代码的话,可能会生一个so,并且不会产生任何编译错误,但是并不能在PHP中使用。如果你把它静态编译进PHP,则会产生连接错误。这是因为C和C++的变量空间的不一致引起的 (PHP是使用C来编写,你的扩展使用C++来编写) 。

修改的方法就是,你要告诉你的扩展模块,将把一些PHP API函数当成C函数来对待(他们是用C来写的),而不是当成C++。

你需要把一些代码用 BEGIN/END_EXTERN_C()包起来。你的php5cpp.cpp 可能要写成像下边的样子:

extern “C” {

#include “php.h”

#include “php_ini.h”

#include “ext/standard/info.h”

}

.

.

.

#ifdef COMPILE_DL_PHP5CPP

BEGIN_EXTERN_C()

ZEND_GET_MODULE(php5cpp)

END_EXTERN_C()

#endif

一般地,我们是用 BEGIN/END_EXTERN_C() 来包起头文件的那些内容,如对 ZEND_GET_MODULE(php5cpp)那样。但是在引用声明 BEGIN/END_EXTERN_C()zend.h文件前,可以通过使用extern "C" 来达到相同的作用。

Windows系统,可以使用Visual C++编译你的扩展模块。这也需要在的扩展模块的头部加上类似的声明:

#ifdef PHP_WIN32

#include

#include

#endif

这样你可以使得你的代码保持跨平台的特性。

 5节.编译扩展模块

现在可以去编译扩展模块了。如果你想把它编译成静态模块(把它做为PHP的一个部分编译进PHP中去),去到PHP的根目录$PHP_HOME,删去 configure 文件和运行buildconf (译:需要libtool的支持)。

然后用你平常用的参数运行 configure 并加上--enable-php5cpp 项。运行 make clean, make, make install ,并完成其它的一些必要操作,如:重新编译Apache。

如果你想用动态链接库的方式编译扩展模块,到你的模块的目录下,运行phpize 命令(假设你已经安装了PEAR),它会为你的模块创建一个 configure 脚本。然后运行configure, makemake install。如果你想让你的模块自动加载,你要修改php.ini 以加载正确的文件。如:加上类似的一行:extension=php5cpp.so

现在你的PHP扩展模块已经编译好了。试着运行一下在模块目录下自动生成的php5cpp.php ,看看是不是一切正常?:)

 6节.将C++类影射到PHP中

目录

MyClass

宏、函数及其它

封装代码

 

PHP 5的类支持很多新的特性。如:权限(protected, public, private),异常,interfaces,等等。在这个简单的介绍中,我们只做最基本的事情:使PHP可以影射到C++的类。这样你可以用PHP中使用你的类,之后的事情将会变得很简单的。在看下面的介绍之前,你可以参考一下Sqlite, SimpleXML 及 cryptopp-php 模块的代码。

这里介绍一下。我们要用一个C++类做为例子,就叫做MyClass吧。在PHP术语中,把它叫做一个resource(资源)。PHP常使用这类的东西,如数据库的连接就是resource,它也可能是一个指向实际resource的struct(结构)。在C++中,class 实际上是struct的一个近义词(struct默认为public,classe默认为private  –仅这个区别而已)。

在结构化的接口中,我们会用类似以下的PHP代码来使用resources:

$m = myclass_new();

myclass_set_string($m, ‘hello world!’);

var_dump(myclass_get_string($m));

?>

在面向对象式的接口中,一样可以使用PHP resources,不过已经被封装在一个PHP对象中了,如:

$m = new MyClass;

$m->setString(‘hello world!’);

var_dump($m->getString());

?>

我们不需要关心被封装的实际的代码做了些什么。当我们有一个叫MyClass的C++类。我们可以把这个C++类当成resources并把C++类里的方法封装成PHP的函数。然后我们也可以把它封装成一个PHP的对象,使得可以像一般的C++那样使用。

在看本文时,记住我们的目的就是:把C++类封装成PHP可以使用的结构化的函数或对象化的类。也许在一开始有些东西会令你迷惑,但读下去后就会慢慢明白的了。中间会有很多的宏定义,但当你看明白后,会觉得所有东西都很清淅很容易了。

1  MyClass

首先我们需要一个类。下边是一个只有一个私有属性和几个公有方法的简单的类。

以下是这个类的声明头文件 myclass.h

#ifndef __MYCLASS_H__

#define __MYCLASS_H__

 

#include

 

using namespace std;

 

class MyClass {

    private:

        string itsString;

 

    public:

        MyClass(string s = “default”);

        ~MyClass();

 

        string getString() const;

        bool setString(const string s);

};

 

#endif

下边是定义代码myclass.cpp

#include “myclass.h”

 

MyClass::MyClass(string s)

{

   itsString = s;

}

 

MyClass::~MyClass()

{

}

 

string MyClass::getString() const

{

    return itsString;

}

 

bool MyClass::setString(const string s)

{

    itsString = s;

    return true;

}

这只是一个做为例子的类。

然后我们要让构建系统知道和可以编译这些文件。把config.m4文件做以下修改:

PHP_NEW_EXTENSION(php5cpp, php5cpp.cpp, $ext_shared)

 

becomes…

 

PHP_NEW_EXTENSION(php5cpp, php5cpp.cpp myclass.cpp, $ext_shared)

php5cpp.cpp 文件中增加#include “myclass.h”

extern “C” {

#include “php.h”

#include “php_ini.h”

#include “ext/standard/info.h”

}

 

#include “php_php5cpp.h”

#include “myclass.h”

不要把include php_php5cpp.hmyclass.h 的语句放在 extern "C" 中,否则会出现错误。

2  宏、函数及其它

为了让这个模块可以同时在PHP 4和PHP 5使用,我们需要使用一些宏去声明是依赖于PHP 4或是PHP 5的。因为PHP 4和PHP 5的执行文件是不兼容的,所以你需要为这两个PHP的版本分别编译不同的版本。

通常我会把这些宏的声明放在一个单独的文件中。在这个例子里,就放在objects.h 中吧。

objects.h 中要写一些PHP 5需要的函数,如:

#ifndef __PHP5CPP_OBJECTS_H__

#define __PHP5CPP_OBJECTS_H__

 

#if PHP_MAJOR_VERSION == 5

 

zend_class_entry *php5cpp_ce_myclass;

static zend_object_handlers php5cpp_object_handlers_myclass;

 

function_entry php5cpp_myclass_methods[] = {

    ZEND_ME_MAPPING(MyClass,    myclass_new,        NULL)

    ZEND_ME_MAPPING(setString,  myclass_set_string, NULL)

    ZEND_ME_MAPPING(getString,  myclass_get_string, NULL)

    {NULL, NULL, NULL}

};

 

typedef enum {

    is_myclass

} php5cpp_obj_type;

 

struct php5cpp_obj {

    zend_object std;

    int rsrc_id;

    php5cpp_obj_type type;

    union {

        MyClass *myclass;

    } u;

};

你可以发现,为了保持一致,每个声明都加上了php5cpp_ 前缀。这是习惯上的一种约定。

另外,#if PHP_MAJOR_VERSION == 5表明在下边的那些宏、函数等都只是在PHP 5下才生效,当我们在PHP 4下编译时它们会被预处理忽略掉。

php5cpp_ce_myclass 是类 MyClass 的入口。 php5cpp_object_handlers_myclass 是类的内部处理handler(句柄)。

php5cpp_myclass_methods[] 把MyClass中的函数影射成可在PHP使用的标准函数。这样我们在PHP中就可以使用 myclass_new, myclass_get_string 等来执行这些函数。你会发现这里并没有定义myclass_destroy函数,因为在你对一个类实例使用unset() 时,系统会自动调用它的释构函数的了。

在结构php5cpp_obj中的枚举变量 php5cpp_obj_type 声明了对象的类型。如果你想在扩展中再加入一个类,如: AnotherClass,你需要再增加一项,如: is_anotherclass

php5cpp_obj 结构中声明了在PHP中使用的一些基本信息,包括:

一个resource ID:rsrc_id ,它指向PHP内部的一个记录着C++对象的track的resource(资源)。实际上,我们的类在PHP中的操作是更像是一个PHP resource,它有自己的垃圾回收和引用计数等机制。

一个zend_object 来声明我们的PHP类,使得它可以像PHP的类那样。当使用这个PHP类时,实际上就会调用我们的C++类去处理了。(需要记住的是:使用时需要像使用其它PHP类那样动态的创建它。(译:使用new))

type 声明现在处理的对象。在这里,它只有一个:is_myclass。但正如我前边说过,你可以再增加其它的类。

联合类型变量u 保存了指向C++对象实例的指针。如果你在这个扩展中还有其它class,那还需要在这再增加其它的指针,如:AnotherClass *anotherclass

static void php5cpp_object_free_storage(zend_object *object TSRMLS_DC)

{

    php5cpp_obj *obj = (php5cpp_obj *) object;

 

    zend_hash_destroy(obj->std.properties);

    FREE_HASHTABLE(obj->std.properties);

 

    if (obj->u.myclass) {

        zend_list_delete(obj->rsrc_id);

    }

 

    efree(obj);

}

 

static void php5cpp_object_new(zend_class_entry *class_type, zend_object_handlers *handlers, zend_object_value *retval TSRMLS_DC)

{

    zval *tmp;

    php5cpp_obj *obj = (php5cpp_obj *) emalloc(sizeof(php5cpp_obj));

    memset(obj, 0, sizeof(php5cpp_obj));

    obj->std.ce = class_type;

 

    ALLOC_HASHTABLE(obj->std.properties);

    zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);

    zend_hash_copy(obj->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));

    retval->handle = zend_objects_store_put(obj, NULL, php5cpp_object_free_storage, NULL TSRMLS_CC);

    retval->handlers = handlers;

}

php5cpp_object_free_storage() 可以看成了对象是 php5cpp_obj 的释构函数,因为它要做的就是把MyClass的对象释放掉

php5cpp_object_new() 基本上算是一个构造函数,它负责分配内存空间,分配zend_object结构,及初始化handler(句柄)等等。它负责为扩展中所有的类的构造,不管是对象MyClass 还是 AnotherClass

php5cpp_object_new_myclass() 通过调用 php5cpp_object_new() 来创建一个PHP MyClsss的实例。如果在扩展中有几个类的话,你要为每个类写一个类似php5cpp_object_new_*()函数。

// Register the class entry..

 

#define PHP5CPP_REGISTER_CLASS(name, obj_name) \

    { \

        zend_class_entry ce; \

        INIT_CLASS_ENTRY(ce, obj_name, php5cpp_ ## name ##_methods); \

        ce.create_object = php5cpp_object_new_ ## name; \

        php5cpp_ce_ ## name = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);  \

        memcpy(&php5cpp_object_handlers_ ## name, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \

        php5cpp_object_handlers_ ## name.clone_obj = NULL; \

        php5cpp_ce_ ## name->ce_flags |= ZEND_ACC_FINAL_CLASS; \

    }

 

 

// Register resources. If we’re using an object, put it into the object store.

 

#define PHP5CPP_REGISTER_RESOURCE(obj_type, return_value, res, le) \

    { \

        int rsrc_id = ZEND_REGISTER_RESOURCE(object ? NULL : return_value, res, le); \

        if (object) { \

            php5cpp_obj *obj = (php5cpp_obj *) zend_object_store_get_object(object TSRMLS_CC); \

            obj->u.obj_type= res; \

            obj->rsrc_id = rsrc_id; \

            obj->type = is_ ## obj_type; \

        } \

    }

 

#define PHP5CPP_REGISTER_MYCLASS_RESOURCE(return_value, res, le) \

    PHP5CPP_REGISTER_RESOURCE(myclass, return_value, res, le)

接着我们要在php5cpp.cpp中加上以上的宏。虽然看起来很相似,但不要把它们弄乱,实际上PHP5CPP_REGISTER_CLASS()PHP5CPP_REGISTER_RESOURCE()的处理是不一样的。

PHP5CPP_REGISTER_CLASS() 登记一个类的实际处理的程序。在后边介绍模块初始化函数(PHP_MINIT_FUNCTION())时还会遇到它。

PHP5CPP_REGISTER_RESOURCE() 负责在登记一个resource时取得resource得ID。其实resource就是我们的C++对象的一个实例。当我们处理一个对象时,它会创建一个PHP对象,把实际的对象保存在PHP的对象贮存器中,然后把resource ID记录在PHP对象中。

PHP5CPP_REGISTER_MYCLASS_RESOURCE() 只是为了使用 PHP5CPP_REGISTER_RESOURCE()时可以方便一些。

// These are for parsing parameters and getting the actual object from the store.

 

#define PHP5CPP_GET_THIS() \

    zval* object = getThis();

 

#define PHP5CPP_SET_OBJ(type) \

    php5cpp_obj *obj = (php5cpp_obj *) zend_object_store_get_object(object TSRMLS_CC); \

    type ## _instance = obj->u.type;

 

#define PHP5CPP_OBJ_PARAMS(type, params) \

    PHP5CPP_GET_THIS(); \

    if (object) { \

        if (params == FAILURE) { \

            RETURN_FALSE; \

        } \

        PHP5CPP_SET_OBJ(type) \

    } \

    else

 

#define PHP5CPP_OBJ_NO_PARAMS(type) \

    PHP5CPP_GET_THIS(); \

    if (object) { \

        if (ZEND_NUM_ARGS() != 0) { \

            php_error(E_WARNING, “didn’t expect any arguments in %s()”, get_active_function_name(TSRMLS_C)); \

        } \

        PHP5CPP_SET_OBJ(type) \

    } \

    else

 

#define PHP5CPP_MYCLASS_OBJ_PARAMS(params)  PHP5CPP_OBJ_PARAMS(myclass, params)

#define PHP5CPP_MYCLASS_OBJ_NO_PARAMS()     PHP5CPP_OBJ_NO_PARAMS(myclass)

 

PHP5CPP_GET_THIS() 会返回当前使用对象的指针。如果当前使用的是一个结构,那么getThis() 会返回NULL;如果使用的是一个对象,getThis() 返回一个指向当前PHP对象的指针。

PHP5CPP_SET_OBJ() 会从对象贮存器中取得PHP对象实际使用的C++对象实例,然后可以用来做其它处理。在使用PHP函数/方法时,对象会贮存在类似”type ## _instance” 的类型里,如:在我们的例子中是myclass_instance,即MyClass*类型。

PHP5CPP_*_OBJ_PARAMS()PHP5CPP_*_NO_OBJ_PARAMS() 会在调用我们的函数/方法时被使用,它们会处理从PHP方传进来的参数。在封装函数和声明中,可以通过zend_parse_parameters() 去分析这些参数。

你可以注意到,宏PHP5CPP_*_PARAMS() 是以 else结尾的。这样的话,当所处理的不是一个对象时,它会试着用结构化方式去处理。这些宏都可以在下边找到。

PHP 5写的类处理的部分已经写完了,下边的部分是为PHP 4写的很简单易懂的处理。如果我们用的是PHP 4,上边的那部分代码在预处理时会被乎略掉,而只处理下边的这些代码。如果我们用的是PHP 5则反之。

#else // End of PHP5-specific macros

 

 

// This stuff is for PHP 4. They’re empty on purpose, obviously.

 

#define PHP5CPP_GET_THIS()

#define PHP5CPP_MYCLASS_OBJ_PARAMS(params)

#define PHP5CPP_MYCLASS_OBJ_NO_PARAMS()

#define PHP5CPP_REGISTER_CLASS(name, obj_name)

 

#define PHP5CPP_REGISTER_MYCLASS_RESOURCE(return_value, res, le) \

    ZEND_REGISTER_RESOURCE(return_value, res, le);

 

#endif // End of PHP4-specific macros

非常的简单,除了PHP5CPP_REGISTER_MYCLASS_RESOURCE()外其它的宏都是空的。PHP5CPP_REGISTER_MYCLASS_RESOURCE()仍会注册一个resource,但不会为对象做任何的检查。好了,完成这些后,就使得代码在PHP 4和PHP 5中都可以编译通过了。

下边的一些宏在PHP 4和PHP 5中都是一样的,它们是处理结构和非面向对象代码的。

// These are for both PHP 4 and 5

 

#define PHP5CPP_MYCLASS_RSRC_PARAMS(params) \

    if (params == FAILURE) { \

        RETURN_FALSE; \

    } \

    else { \

        ZEND_FETCH_RESOURCE(myclass_instance, MyClass*, &resource, -1, “myclass”, le_myclass); \

    }

 

#define PHP5CPP_MYCLASS_RSRC_NO_PARAMS() \

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “r”, &resource) == FAILURE) { \

        RETURN_FALSE; \

    } \

    else { \

        ZEND_FETCH_RESOURCE(myclass_instance, MyClass*, &resource, -1, “myclass”, le_myclass); \

    }

 

static ZEND_RSRC_DTOR_FUNC(destroy_myclass)

{

    if (rsrc->ptr) {

        delete (MyClass*) rsrc->ptr;

        rsrc->ptr = NULL;

    }

}

 

#endif

它们同样是很简单的:先试着分析PHP界面传进来的参数,和取得要处理的resource。

destroy_myclass 负责资源回收。当触发垃圾回收机制,或destroy 对象/ resource 时,它会把在创建C++对象实例时分配的内存清理/释放掉。

php5cpp.cpp里我们会这样使用这些宏:

PHP5CPP_MYCLASS_OBJ_PARAMS(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, &s, &len))

PHP5CPP_MYCLASS_RSRC_PARAMS(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “rs”, &resource, &s, &len))

PHP 5中,经过预处理后会变成这样:

zval* object = getThis();

if (object) {

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, &s, &len) == FAILURE) {

        RETURN_FALSE;

    }

    php5cpp_obj *obj = (php5cpp_obj *) zend_object_store_get_object(object TSRMLS_CC);

    myclass_instance = obj->u.myclass;

}

else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “rs”, &resource, &s, &len) == FAILURE) {

    RETURN_FALSE;

}

else {

    ZEND_FETCH_RESOURCE(myclass_instance, MyClass*, &resource, -1, “myclass”, le_myclass); \

}

可以看到,在面向对象模式下,它会试着去取得对象,分析参数和取得实际的MyClass对象。如果在非面向对象的模式下,这段代码会试着用结构的方式去分析参数,取得resource等等。

PHP 4中,经过预处理后会变成了下边这样:

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “rs”, &resource, &s, &len) == FAILURE) {

    RETURN_FALSE;

}

else {

    ZEND_FETCH_RESOURCE(myclass_instance, MyClass*, &resource, -1, “myclass”, le_myclass); \

}

在这里(PHP 4中),面向对象的代码会完全给忽略掉,只留下结构化程序的代码。

为了上边我们做的有意义,现在我们要做一个最简单的部分,写我们的封装代码,和使得在PHP中可以使用我们的class。

3  封装代码

我们要在php_php5cpp.h中加入一些封装函数和方法的定义:

PHP_FUNCTION(myclass_new);

PHP_FUNCTION(myclass_destroy);

PHP_FUNCTION(myclass_set_string);

PHP_FUNCTION(myclass_get_string);

简单吧。好,那我们在php5cpp.cpp中写入封装函数的执行代码:

PHP_FUNCTION(myclass_new)

{

    MyClass *myclass_instance;

    char* s;

    int len = 0;

    PHP5CPP_GET_THIS();

 

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “|s”, &s, &len) == FAILURE) {

        RETURN_FALSE;

    }

 

    if (len) {

        myclass_instance = new MyClass(string(s, len));

    }

    else {

        myclass_instance = new MyClass;

    }

 

    if (myclass_instance != NULL) {

        PHP5CPP_REGISTER_MYCLASS_RESOURCE(return_value, myclass_instance, le_myclass);

    }

}

 

PHP_FUNCTION(myclass_destroy)

{

    zval *resource;

 

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “r”, &resource) == FAILURE) {

        RETURN_FALSE;

    }

 

    zend_list_delete(Z_RESVAL_P(resource));

}

 

PHP_FUNCTION(myclass_set_string)

{

    zval *resource;

    MyClass *myclass_instance;

    int len = -1;

    char *s;

    PHP5CPP_MYCLASS_OBJ_PARAMS(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, &s, &len))

    PHP5CPP_MYCLASS_RSRC_PARAMS(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “rs”, &resource, &s, &len))

 

    if (myclass_instance == NULL) {

        php_error(E_WARNING, “can’t set string on null resource in %s()”, get_active_function_name(TSRMLS_C));

        RETURN_FALSE;

    }

    else {

        myclass_instance->setString(string(s, len));

        RETURN_TRUE;

    }

}

 

PHP_FUNCTION(myclass_get_string)

{

    zval *resource;

    MyClass *myclass_instance = NULL;

    string retval;

    PHP5CPP_MYCLASS_OBJ_NO_PARAMS()

    PHP5CPP_MYCLASS_RSRC_NO_PARAMS()

 

    if (myclass_instance == NULL) {

        php_error(E_WARNING, “can’t get string from null resource in %s()”, get_active_function_name(TSRMLS_C));

        RETURN_FALSE;

    }

    else {

        retval = myclass_instance->getString();

        RETVAL_STRINGL((char*) retval.data(), retval.length(), 1);

    }

}

这里没什么太难的地方,所以我不想每个函数都解释一次。现在就只简单看一下myclass_new()myclass_set_string()做为例子。

myclass_new()中,首先尝试取得一个对象。再次说明,只在面向对象方式下才会生效,在PHP 4下会被预处理所忽略掉。

因为C++类的构造函数有默认的参数,所以我们要看看PHP是否有传一个string类型的参数过来。分析完传进来的参数后,就为分配myclass_instance内存空间,然后用PHP5CPP_REGISTER_MYCLASS_RESOURCE把得到的resource贮存起来。在PHP 5的OO模式下,PHP5CPP_REGISTER_MYCLASS_RESOURC会把resource当成对象贮存,在PHP 4的结构化模块下只会简单的创建一个resource。

接着说myclass_set_string()

首先,先定义一个zval结构去处理在PHP用结构化接口传进来的参数。myclass_instance会被用来指向实际的C++对象。len*s用来保存从PHP方传来的字串和字串的长度。

PHP 4下,PHP5CPP_MYCLASS_OBJ_PARAMS()会被预处理忽略。在PHP 5它会尝试取得对象,如果获取对象失败,否会返回使用结构化接口的步骤。

在取得myclass_instance后,可以像平时使用类那样使用C++ MyClass类。先调用MyClass的setString()方法。然后把得到的C++标准string类对象转成类似C方式的char指针,并传回给PHP方。

最后,要让PHP知道这个新class,很简单,在PHP_MINIT_FUNCTION中加入:

PHP5CPP_REGISTER_CLASS(myclass, “MyClass”);

 

le_myclass = zend_register_list_destructors_ex(destroy_myclass, NULL, “myclass”, module_number);

好了,到这里代码已经写完了。 :P

 7节.然后…

然后,你可以按自己需要的方式编译扩展模块。你也许需要增加一些另外的代码,像using namespace std 等。这个介绍的下边几页会包含一个完整的可运行例子。这些代码我已经在gentoo系统上测试过了,包括目前(2004-03-19)在CVS上是新的PHP 5和PHP_4_3的环境。(译:本译文只提供了可查看代码的链接,没有包括这些代码)

 8节.例子源代码

译文中没有包括完整在例子源代码,你通过下边的链接去查看:

http://bugs.tutorbuddy.com/php5cpp/php5cpp/example_code.html

 

 9节.授权许可

本文在GNU Free Documentation License下授权传播。以下是许可License的内容链接…

http://bugs.tutorbuddy.com/php5cpp/php5cpp/license.html

 

 10节.修改历史

10 Jun 2004  fishchen translated.

6  May 2004  version 0.1.0.

29 Mar 2004  First version.

 

 

 

2004年05月21日

这篇文章是我的csdn上跟其它人讨论时所写的, 没有怎么整理就转过来了. 希望可以对想了解mysql_pconnect()的工作方式的人有所帮助(虽然使用mysqli_xx函数时已经没有pconnect了:P)

———————-


php中mysql_pconnect()的实现方式:
其实mysql_pconnect()本身并没有做太多的处理, 它唯一做的只是在php运行结束后不主动close掉mysql的连接.

mysql_pconnect()与此同时mysql_connect()的区别:

cgi方式下:
在php经cgi方式运行时pconnect和connect是基本没有区别的, 因为cgi方式是每一个php访问起一个进程, 访问结束后进程也就结束了, 资源也全释放了.

apache模块方式下:
区别在于当php以apache模块方式运行时, 由于apache有使用进程池, 一个httpd进程结束后会被放回进程池, 这也就使得用pconnect打开的的那个mysql连接资源不被释放, 于是有下一个连接请求时就可以被复用.

这就使得在apache并发访问量不大的时候, 由于使用了pconnect, php节省了反复连接db的时间, 使得访问速度加快. 这应该是比较好理解的.

但是在apache并发访问量大的时候, 如果使用pconnect, 会由于之前的一些httpd进程占用的mysql连接没有close, 则可能会因为mysql已经达到最大连接着, 使得之后的一些请求永远得不到满足.

例如:
若mysql最大连接数设为500, 而apache的最大同时访问数设为2000
假设所有访问都会要求访问db, 而且操作时间会比较长

当前500个请求的httpd都没有结束的时候…之后的httd进程都是无法连接到mysql的(因已经达到mysql最大连接数). 只有当前500个httpd进程结束或被复用才可以连接得到了mysql.

其实这个也很好解释了xgy_p的测试中若操作比较简单, pconnect比connect效率高很多, 而且跟使用jsp的连接池的速度比较接近. 因为这个时候httpd进程可以不断的给复用.

而当DB操作复杂, 耗时较长时, 因httpd会fork很多并发进程处理, 而先产生的httpd进程不释放db连接, 使得后产生的httpd进程无法连上db. 因为这样没有复用其它httpd进程的mysql连接. 于是会就产生很多连接超时, 像一开始的1000个并发连接测试说几乎都是连接超时就是这个原因.


(反进来看jsp用的如果是纯粹的db连接池, 则不会有因为达到mysql连接上限而连不上的问题, 因为jsp的连接池会使得可以等待其它连接使用完毕并复用. )


因此在并发访问量不高时,使用pconnect可以简单提高访问速度, 但在并发量增大后, 是否再使用pconnect就要看程序员的选择了..

就我个人认为, php现在对mysql的连接并没有真正用到连接池, pconnect也只是相当于借了apache的进程池来用, 所以在并发访问量大的时候pconnect并不能很好的提高访问DB效率. 在这一点上. php的确比不上jsp.

就目前的这种情况, 如果并发量大的话, 我个人建议最好还用mysql_connect.


2004/04/07 22:03

2004年05月14日

The document briefly explains how to use C++ within a PHP extension.
It’s link is:
http://bugs.tutorbuddy.com/php5cpp/php5cpp/

(2004.06.10)
I had translated it into chinese:
http://www.donews.net/fishchen/archive/2004/06/10/27258.aspx

http://dev.mysql.com/doc/mysql/en/Roadmap.html

1.5 MySQL Development Roadmap


This section provides a snapshot of the MySQL development roadmap, including major features implemented or planned for MySQL 4.0, 4.1, 5.0, and 5.1. The following sections provide information for each release series.


The production release series is MySQL 4.0, which was declared stable for production use as of Version 4.0.12, released in March 2003. This means that future 4.0 development will be limited only to making bugfixes. For the older MySQL 3.23 series, only critical bugfixes will be made.


Active MySQL development currently is taking place in the MySQL 4.1 and 5.0 release series. This means that new features are being added to MySQL 4.1 and MySQL 5.0. Both 4.1 and 5.0 are available now in alpha status.


Before upgrading from one release series to the next, please see the notes at section 2.5 Upgrading/Downgrading MySQL.


Plans for some of the most requested features are summarized in the following table.




































Feature MySQL Version
Unions 4.0
Subqueries 4.1
R-trees 4.1 (for MyISAM tables)
Stored procedures 5.0
Views 5.0 or 5.1
Cursors 5.0
Foreign keys 5.1 (already implemented in 3.23 for InnoDB)
Triggers 5.1
Full outer join 5.1
Constraints 5.1

 

2004年04月22日

    今天在使用iconv做GBK转UCS-2时,顺手研究了一下使用iconv做GB与BIG5码之间的转换,发现基本是无法正常转换的。于是查找了一些资料后认为:


    在目前的情况下,是基本无法使用iconv进行GB与BIG5码之间的转换的。


    原因主要是目前iconv所使用的码表中并没有直接的GB<->BIG5码间的码表,转换时需用通过UCS2(Unicode)做转码中转。但由于GB码和BIG5码的中的同一个汉字有可能被转成了不同的UCS2。因此使得GB码和BIG5码之间没法通过iconv正常转换。


    引用謝東翰/小虫的话:“如果要做兩個交集呈度不高的編碼系統互轉 (如 Big5 與 GB2312 之間) 時,也許還是稍微保守一些,由我們自己在程式裏做轉換表格會較為適當。”


    具体可参考以下文章:
http://xcin.linux.org.tw/i18n/pc2000/p4/node2.html更廣義的字元集轉換:iconv
http://xcin.linux.org.tw/i18n/pc2000/p4/node3.htmlglibc 中 iconv 的心臟 – gconv-module
http://xcin.linux.org.tw/i18n/pc2000/p4/chinese-4.html(GLIBC 提供的字元處理及轉換函式)

2004年03月21日

  之前一直不是很留意SQLite,在知道php5要捆绑SQLite后去了一下SQLite的主页看了一下。发现在只能用文本保存数据的情况下,SQLite是一个比较好的选择方式,而且支持SQL语言。现在看来国内那些搞文本储存的论坛可能会变得意义不大。
  怎么以前就没留意SQLite呢 :P