babystudio 2003年06月24日
Php 3.x与4.x中关于对象编程的不兼容问题之三 魔法函数说
Php 3.x与4.x中关于对象编程的不兼容问题

“面向对象”听起来是个很流行的词汇,似乎到了如果你还没有OOP,那不如回家种白菜的地步。
Php从版本3.x开始支持对象编程,虽然它的Class从一开始就饱受程序员们的指责,但它的确给我们带来了意外的惊喜。一路跌跌撞撞走来,到了4.x,Php已经相当OOP了。当然,它对于类变量的处理依然不能让人满意,没有私有、公有、保护、静态的声明方法。Php面向对象的可用性不在本文讨论范围内。
伴随着4.x中关于对象编程的完善,Php team给我们带来了“麻烦”:3.x和4.x中关于对象编程的一些游戏规则改变了,不兼容。笔者就实际开发过程遇到的问题稍作讨论,相信有些问题可能笔者尚未遇到,欢迎诸位补充、共赏。

三、魔法函数说

4.x中开始出现的魔法函数,用以达到被Php小组宣称的“不可思议”(magical)的效果。
魔法函数一词直译于“magic function”。
这是一类具有保留名字的类函数,Php小组在推荐中有这样的表述:用户定义自己的类函数不应该以两个下划线“__”开始,因为这可能与现在或后续版本中的魔法函数发生重名冲突并将严重影响用户定义类的正常工作。所以,如果你过去的类代码中如果定义了“__xxx”形式的方法,强烈建议你修改它们。
魔法函数会在类遇到某些事件时自动触发,好像某些数据库软件中的触发器。截至php-4.3.2RC4,共有两个魔法函数:__sleep和__wakeup。从字面上看就是说这两个函数分别在对象发生睡着动作或苏醒动作时触发,事实上是__sleep在对象被序列化(serialize)时触发,__wakeup在对象被反序列化(unserialize)时触发。

说到这里,可能已经让人觉得枯燥了,因为还没有出现什么意外的惊喜激励我们。看看下面的例子。

1、类的任务是打开一个文件读取指定位置和字节长度数据,并可以返回最近一次读取的数据。我们实例化一个对象后,首先读取第7个字节开始共77字节的内容,然后把它序列化保存进一个字串,再从这个字串反序列化出对象后输出最近一次读取的数据。这次不用__wakeup。
<?
class ABaby
{
var $fileName,$fileOpen,$offset,$length;
function ABaby(){
$this->fileName='ABaby.class.php';
$this->fileOpen=fopen($this->fileName,'rb');
$this->offset=0;
$this->length=0;
}
function getData($offset,$length){
$this->offset=$offset;
$this->length=$length;
fseek($this->fileOpen,$offset);
return(fread($this->fileOpen,$length));
}
function getLastData(){
return($this->getData($this->offset,$this->length));
}
}
$ABaby=new ABaby();
echo($ABaby->getData(7,77));
$ABabyBed=serialize($ABaby);
$newABaby=unserialize($ABabyBed);
echo($newABaby->getLastData());
?>
注释:
将上面代码保存成文件ABaby.class.php并运行。出错了:supplied argument is not a valid stream resource!这是因为序列化时解析器把$fileOpen回归了它的本来面目——integer,已经不是原来意义上的文件流操作句柄(resource),实施上,这时候文件还是处于打开状态的,可以把最后一行换作“echo($ABaby->getLastData());”证实。同样的情况还会发生在数据库连接、网络连接等其他句柄上,读者可以自行测试一下。
下面我们通过魔法函数__wakeup修正。当然不是一定要用__wakeup,比如你也可以首先反序列化得到对象$newABaby中,然后将实例变量$offset和$length保存到另外一处,然后调用$newABaby的构造器,然后调用getData()方法,这样就可以达到调用getLastData()符合预期的目的。一连这么多个然后......现在看__wakeup的魔力。

2、通过增加一个魔法函数__wakeup修正上面的问题
<?
class ABaby
{
var $fileName,$fileOpen,$offset,$length;
function ABaby(){
$this->fileName='ABaby.class.php';
$this->fileOpen=fopen($this->fileName,'rb');
$this->offset=0;
$this->length=0;
}
function getData($offset,$length){
$this->offset=$offset;
$this->length=$length;
fseek($this->fileOpen,$offset);
return(fread($this->fileOpen,$length));
}
function getLastData(){
return($this->getData($this->offset,$this->length));
}
function __wakeup(){
$this->fileOpen=fopen($this->fileName,'rb');
}
}
$ABaby=new ABaby();
echo($ABaby->getData(7,77));
$ABabyBed=serialize($ABaby);
$newABaby=unserialize($ABabyBed);
echo($newABaby->getLastData());
?>
注释:
运行上面的脚本应该一切正常。unserialize()操作过程的最后一步会自动检测类是否定义了__wakeup()魔法函数,有则自动调用。这里面我们通过__wakeup()只是简单的重新打开文件流操作句柄。可以在__wakeup()中加入“echo('ABaby');”证实它确实被自动调用了。

上面的代码存在一个“隐患”。
我们序列化一个对象后,通常意味着近期不会马上使用它,但是上面的代码我们却没有显式的关闭打开的文件流操作句柄。虽然,几乎每门语言都宣称自己的垃圾回收机制可以自动释放掉不用的资源,但是实际并不总是这样。所以及时释放掉不用的资源是个好的习惯。下面我们通过__sleep()魔法函数告诉解析器一旦这个对象被序列化,它应该释放掉占用的句柄。
...全文
4 点赞 收藏 8
写回复
8 条回复

还没有回复,快来抢沙发~

发动态
发帖子
基础编程
创建于2007-09-28

9733

社区成员

14.0w+

社区内容

从PHP安装配置,PHP入门,PHP基础到PHP应用
社区公告
暂无公告