月度归档:2010年10月

PHP5.3版本编译扩展时出现:LNK2001: unresolved external symbol _ZVAL_ADDREF

PHP5.3版本编译扩展时出现的问题
近,因要编译PHP扩展,本着最新最好的出发点,将PHP5.3的源码包下载下来,并将扩展源码创建VS2008项目,在经历百般磨难后,终于不再出现语法错误,然而当满怀希望按下F7键后,发现出现了N多的LNK2001和LNK2019错误,其中error LNK2001巨多,在纠结了一个周末后,可能是感动上天,终于在GOOGLE中找到了答案。
报错如下:
error LNK2001: unresolved external symbol _ZVAL_ADDREF
GOOGLE给的答案是php5.3以上版本更改了一些Zend API,而ZVAL_ADDREF刚好是其中的一个。
如下为解决方案:
假设我们的扩展为martin,则在php_martin.h或martin文件中,在include了相关php本身的头文件后添加如下 代码:

1
2
3
4
5
6
7
8
9
 
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3) || (PHP_MAJOR_VERSION >= 6)
#undef ZVAL_REFCOUNT
#undef ZVAL_ADDREF
#undef ZVAL_DELREF
#define ZVAL_REFCOUNT Z_REFCOUNT_P
#define ZVAL_ADDREF Z_ADDREF_P
#define ZVAL_DELREF Z_DELREF_P
#endif

以上代码的作用是将新的宏以旧名重定义,以保持其可用性。

【参考资料】

http://www.hightman.cn/bbs/showthread.php?tid=656

http://d.hatena.ne.jp/rsky/20071016/1192524940

数据源架构模式之活动记录

数据源架构模式之活动记录

【活动记录的意图】
一个对象,它包装数据表或视图中某一行,封装数据库访问,并在这些数据上增加了领域逻辑。

【活动记录的适用场景】
适用于不太复杂的领域逻辑,如CRUD操作等。

【活动记录的运行机制】
对象既有数据又有行为。其使用最直接的方法,将数据访问逻辑置于领域对象中。
活动记录的本质是一个领域模型,这个领域模型中的类和基数据库中的记录结构应该完全匹配,类的每个域对应表的每一列。
一般来说,活动记录包括如下一些方法:
1、由数据行构造一个活动记录实例;
2、为将来对表的插入构造一个新的实例;
3、用静态查找方法来包装常用的SQL查询和返回活动记录;
4、更新数据库并将活动记录中的数据插入数据库;
5、获取或设置域;
6、实现部分业务逻辑。

【活动记录的优点和缺点】
优点:
1、简单,容易创建并且容易理解。
2、在使用事务脚本时,减少代码复制。
3、可以在改变数据库结构时不改变领域逻辑。
4、基于单个活动记录的派生和测试验证会很有效。

缺点:
1、没有隐藏关系数据库的存在。
2、仅当活动记录对象和数据库中表直接对应时,活动记录才会有效。
3、要求对象的设计和数据库的设计紧耦合,这使得项目中的进一步重构很困难

【活动记录与其它模式】
数据源架构模式之行数据入口:活动记录与行数据入口十分类似。二者的主要差别是行数据入口 仅有数据库访问而活动记录既有数据源逻辑又有领域逻辑。

【活动记录的PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<?php
 
/**
 * 企业应用架构 数据源架构模式之活动记录 2010-10-17 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * 哥学社成员(http://www.blog-brother.com/)
 * @package architecture
 */
 
/**
 * 定单类
 */
class Order {
 
    /**
     *  定单ID
     * @var <type>
     */
    private $_order_id;
 
    /**
     * 客户ID
     * @var <type>
     */
    private $_customer_id;
 
    /**
     * 定单金额
     * @var <type>
     */
    private $_amount;
 
    public function __construct($order_id, $customer_id, $amount) {
        $this->_order_id = $order_id;
        $this->_customer_id = $customer_id;
        $this->_amount = $amount;
    }
 
    /**
     * 实例的删除操作
     */
    public function delete() {
        $sql = "DELETE FROM Order SET WHERE order_id = " . $this->_order_id . " AND customer_id = "  . $this->_customer_id;
        return DB::query($sql);
    }
 
    /**
     * 实例的更新操作
     */
    public function update() {
    }
 
    /**
     * 插入操作
     */
    public function insert() {
    }
 
    public static function load($rs) {
        return new Order($rs['order_id'] ? $rs['order_id'] : NULL, $rs['customer_id'], $rs['amount'] ? $rs['amount'] : 0);
    }
 
}
 
class Customer {
 
    private $_name;
    private $_customer_id;
 
    public function __construct($customer_id, $name) {
        $this->_customer_id = $customer_id;
        $this->_name = $name;
    }
 
    /**
     * 用户删除定单操作 此实例方法包含了业务逻辑
     * 通过调用定单实例实现
     * 假设此处是对应的删除操作(实际中可能是一种以某字段来标记的假删除操作)
     */
    public function deleteOrder($order_id) {
        $order = Order::load(array('order_id' => $order_id, 'customer_id' => $this->_customer_id));
        return $order->delete();
    }
 
    /**
     * 实例的更新操作
     */
    public function update() {
    }
 
    /**
     * 入口类自身拥有插入操作
     */
    public function insert() {
    }
 
    public static function load($rs) {
        /* 此处可加上缓存 */
        return new Customer($rs['customer_id'] ? $rs['customer_id'] : NULL, $rs['name']);
    }
 
    /**
     * 根据客户ID 查找
     * @param integer $id   客户ID
     * @return  Customer 客户对象
     */
    public static function find($id) {
        return CustomerFinder::find($id);
    }
 
}
 
/**
 * 人员查找类
 */
class CustomerFinder {
 
    public static function find($id) {
        $sql = "SELECT * FROM person WHERE customer_id = " . $id;
        $rs = DB::query($sql);
 
        return Customer::load($rs);
    }
}
 
class DB {
 
    /**
     * 这只是一个执行SQL的演示方法
     * @param string $sql   需要执行的SQL
     */
    public static function query($sql) {
        echo "执行SQL: ", $sql, " <br />";
 
         if (strpos($sql, 'SELECT') !== FALSE) { //  示例,对于select查询返回查询结果
            return array('customer_id' => 1, 'name' => 'Martin');
        }
    }
 
}
 
/**
 * 客户端调用
 */
class Client {
 
    /**
     * Main program.
     */
    public static function main() {
 
 
        header("Content-type:text/html; charset=utf-8");
 
        /* 加载客户ID为1的客户信息 */
        $customer = Customer::find(1);
 
        /* 假设用户拥有的定单id为 9527*/
        $customer->deleteOrder(9527);
    }
 
}
 
Client::main();
?>

同前面的文章一样,这仅仅是一个活动记录的示例,关于活动记录模式的应用,可以查看Yii框架中的DB类,在其源码中有一个CActiveRecord抽象类,从这里可以看到活动记录模式的应用

另外,如果从事务脚本中创建活动记录,一般是首先将表包装为入口,接着开始行为迁移,使表深化成为活动记录。
对于活动记录中的域的访问和设置可以如yii框架一样,使用魔术方法__set方法和__get方法。

win7下用vs2008开发PHP扩展

win7下用vs2008开发PHP扩展
由于在win7下安装vc6比较麻烦,一直没有安装成功,只能改用vs2008.
由于要编译PHP扩展,于是做了一个简单PHP扩展开发的DEMO

环境:win7 + vs2008 + cygwin + php5.3.1 + xampp1.7.3
生成过程如下:
1、环境配置,安装cygwin,这个在生成PHP扩展的框架时有用到,我的安装目录为c:\cygwin
2、安装xampp1.7.3,下载地址可以google下
3、下载php5.3.1,下载地址:http://cn.php.net/get/php-5.3.1.tar.gz/from/cn2.php.net/mirror,解压到目录PHPHOME(我的是在D:\project\c\php-5.3.1)
4、安装vs2008,这个时间会久一些
5、如果以上的一切都安装好了,那么转第6步,我们开始开发扩展martin(这只是一个名称而已)

6、在命令行中,cd进入PHPHOME/ext/目录,输入php.exe ext_skel_win32.php –extname=martin,此时在ext目录下会生成martin文件夹及在此文件夹下与扩展相关的文件,包括php_martin.h,martin.c文件等。
如果php.exe所在目录没有加到PATH中,请在php.exe前面加程序的完整路径

7、打开vs2008,新建基于已有文件的项目,选择VC++,选择文件所在目录,输入项目名称php_martin, 下一步,在Project type:中选择 DLL project,next直到完成。
8、修改源码,
打开php_martin.h文件,找到PHP_FUNCTION(confirm_martin_compiled);在其下面增加一个扩展函数声明:PHP_FUNCTION(martin_echo);
打开martin.c文件,找到zend_function_entry martin_functions[],在其元素中添加 PHP_FE(martin_echo, NULL)
在martin.c文件中添加如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
   PHP_FUNCTION(martin_echo)
{
	char *arg = NULL;
	int arg_len, len;
	char *strg;
 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
		return;
	}
 
	len = spprintf(&strg, 0, "This is %s's extension.the input string is %s.", "martin", arg);
	RETURN_STRINGL(strg, len, 0);
}

9、按F7 build此项目,此时会有文件找不到报错,此乃php部分源码没有包含的原因,右键项目属性,选择【Configuration Properties】-> 【C/C++】-> 【General】-> 【Additional Include Directories】,将源码根目录,main目录,TSRM目录,Zend目录添加到此处我的是(”D:\project\c\php-5.3.1″;”D:\project\c\php-5.3.1\main”;”D:\project\c\php-5.3.1\TSRM”;”D:\project\c\php-5.3.1\Zend”)
10、继续build,此时可能会显示zend_config.h文件找不到,此时为部分宏没有定义,解决方案:选择项目属性,选择【Configuration Properties】-> 【C/C++】-> 【Preprocessor】-> 【Preprocessor Definitions】,在此处理添加 ZEND_DEBUG=0;COMPILE_DL_MARTIN;ZTS;ZEND_WIN32;PHP_WIN32;HAVE_MARTIN=1
对于不同的扩展可以将COMPILE_DL_MARTIN和HAVE_MARTIN中的MARTIN替换成你的扩展名

11、继续build,此时可能会显示../main/config.w32.h没有找到,google了半天,所说在php5.2.9的源码中有此文件存在(此文件应该是某个环节生成的,没搞清楚),于是下载5.2.9,将此文件放到main目录下

12、继续build,此时可能会显示fatal error LNK1120,解决方案:打开项目属性,选择【Configuration Properties】-> 【Linker】->【Input】->【Additional Dependencies】,在此处添加php5ts.lib,另外需要在【Tools】->【Options】->【Projects and Solutions】->【VC++ Directories】,在【Show directories for:】下拉中,选择Librafy files,将php5ts.lib所在的路径添加进来,此文件存在于 二进制版本的dev/lib目录下。

13、右键solution属性,将Configuration选择为Release
14、build,在ext\martin\Release下会有生成你一个以你的项目名为名称的dll文件(我的为php_martin.dll)
15、将php_martin.dll文件拷贝到机器中运行的php所在的ext目录,修改php.ini文件,添加一行:extension=php_martin.dll,重启apache,
16,运行一个包含了 echo martin_echo(“phppan.com”);语句的php文件,可以看到有输出This is martin’s extension.the input string is phppan.com.

如果你在启动apache中有报错为:
PHP Warning: PHP Startup: martin: Unable to initialize module\nModule compiled with build ID=API20090626,TS\nPHP compiled with build ID=API20090626,TS,VC6\nThese options need to match\n in Unknown on line 0

Warning: PHP Startup: martin: Unable to initialize module
Module compiled with build ID=API20090626,TS
PHP compiled with build ID=API20090626,TS,VC6
These options need to match
in Unknown on line 0
你需要在main/config.w32.h中添加如下语句
#define PHP_COMPILER_ID “VC6″

此处来源于:PHP5.3系列设置编译器ID(build ID)

–EOF–