分类目录归档:PHP

PHP源码,PHP扩展,PHP程序

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

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

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

【活动记录的适用场景】
适用于不太复杂的领域逻辑,如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–

Ajax与REST架构简单示例

话说某日有见54chen真人秀,在六体膜拜之后,鉴于之前也有看过REST的一些东东,于是便有了本文,正文如下:
REST(Representational State Transfer表述性状态转移)是一种体系架构,它为客户端和服务器之间的数据交互提供了指导。它将客户/服务器通信这种计算模型抽象到了Web层面。
REST最早是在Roy Thomas Fielding博士的博士论文中提出的,REST是一种针对网络应用的设计和开发方式,是一种风格,可以降低开发的复杂性,提高系统的可伸缩性。
REST强调如下的体系架构概念。
  1、网络上的所有事物都被抽象为资源(resource);
  2、每个资源对应一个唯一的资源标识(resource identifier),
  3、通过通用的连接器接口(generic connector interface)对资源进行操作;
  4、对资源的各种操作不会改变资源标识;
5、所有的操作都是无状态的(stateless)。

REST对于信息的核心抽象是资源,一个资源是一组实体的概念上的映射,而REST使用一个资源标识符来标识组件之间交互所涉及到的特定资源。REST连接器提供了访问和操作资源的值集合的一个通用接口。在这里所有的操作中,它们都是无状态的,这样就不必在多个请求之间保存状态,不必考虑上下文的约束,从而允许服务器组件迅速释放资源,并进一步简化其实现,从而提高系统的可伸缩性。

现在我们就一个简单的示例演示下REST。
在我们的示例中以Javascript为客户端,与HTTP服务器体系架构配合工作,使用URL作为资源标识,并将HTTP作为连接器接口。
客户端的代码:

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
	function rest() {
                var XMLHttpFactories = [
                    function () {return new XMLHttpRequest()},
                    function () {return new ActiveXObject("Msxml2.XMLHTTP")},
                    function () {return new ActiveXObject("Msxml3.XMLHTTP")},
                    function () {return new ActiveXObject("Microsoft.XMLHTTP")}
                ];
 
                var xmlhttp = false;
                for (var i = 0; i < XMLHttpFactories.length; i++) {
                    try {
                        xmlhttp = XMLHttpFactories[i]();
                    } catch (e) {
                        continue;
                    }
                    break;
                }
                // 建立XMLHttpRequest对象
                this.xmlhttp = xmlhttp;
            }
 
            rest.prototype._get = function(url, data) {
                var xmlhttp = this.xmlhttp;
                xmlhttp.open ('GET', url + "&" + data, false);
                xmlhttp.send (null);
                return xmlhttp.responseText;
            };
 
            rest.prototype._post = function(url,  data) {
                var xmlhttp = this.xmlhttp;
                xmlhttp.open ('POST', url, false);
                xmlhttp.setRequestHeader ("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
                xmlhttp.setRequestHeader ("Content-Length", data.length);
                xmlhttp.send (data);
                return xmlhttp.responseText;
            };
 
            rest.prototype.get = function(url, data) {
                url = url += "?op=GET";
                return this._get(url, data);
            }
 
            rest.prototype.post = function(url,  data) {
                url = url += "?op=POST";
                return this._post(url, data);
            }
 
            rest.prototype.put = function(url, data) {
                url = url += "?op=PUT";
                return this._post(url, data);
            };
 
            rest.prototype.del = function(url, data) {
                url = url += "?op=DELETE";
                return this._get(url, data);
            };
 
            function t() {
                var restobj = new rest();
 
                document.write (restobj.get("http://localhost/test/service.php", "content=GET Content"));
                document.write (restobj.post("http://localhost/test/service.php", "content=POST Content"));
                document.write (restobj.put("http://localhost/test/service.php", "content=PUT Content"));
                document.write (restobj.del("http://localhost/test/service.php",  "content=DELETE Content"));
            }
 
            t();

如上所示,我们在客户端创建XMLHttpRequest对象,并且通过这个对象实现通过HTTP对服务器的操作GET、PUT、POST和DELETE以检索和修改资源。HTTP则把对每一个资源的操作都限制在了4个之内:GET、POST、PUT和DELETE。HTTP的这四个方法分别对应我们常见的CRUD操作,具体对应关系如下 :
C(Create) <==> POST
R(Read/Retrieve) <==> GET
U(Update) <==> PUT
D(Delete/Destroy) <==> DELETE
虽然HTTP提供了这4个方法,但是在某些情况下,PUT和DELETE方法是不可用的,于是我们在这里使用GET方法和POST方法进行替代。另外,在JS操作时,需要注意同源策略(Same Origin Policy,SOP),考虑跨域的问题。
服务端代码:

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
<?php
/**
 * REST后台程序简单示例
 */
class Resource {
    public function get($request) {
        echo 'content=', $request['content'], "; get resource Successful<br />";
    }
 
    public function post($request) {
        echo 'content=', $request['content'], "; post resource Successful<br />";
    }
 
    public function put($request) {
        echo 'content=', $request['content'], "; update resource Successful<br />";
    }
 
    public function delete($request) {
        echo 'content=', $request['content'], "; delete resource Successful<br />";
    }
}
 
$request = $_REQUEST;
$op = $request['op'];
$op = strtolower($op);
 
$ops = array(
    'get' => 1,
    'post' => 1,
    'put' => 1,
    'delete' => 1,
        );
 
if (!isset($ops[$op])) {
    die('input error!');
}
 
$resource = new Resource();
$resource->$op($request);
 
 
?>

如上所示,是我们提供REST数据的服务器端的代码。这里只是简单的应答请求,输出标识内容。
以上就是我们这个示例的全部了,在这个示例中,我们的整体架构是基于最简单的客户端/服务器模式,客户端使用Javascript,以Ajax实现。我们不需要使用PHP之类的语言生成客户端的页面,所有的客户端的工作都交给Javascript去做,包括从服务器端读取数据,生成页面内容,页面布局等等。在服务器端,我们需要做的是提供资源,针对客户端的请求返回对应的资源,此时的服务器是无状态的。客户与服务器之间的数据交换不依赖于服务器,由客户端来维护状态。

另外:REST只是一种体系架构风格,它并没有改变服务器,它改变的是我们的编码风格。