通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法
出于性能和安全方面的考虑,公司的平台上禁用了本地文件读写和对外的数据抓取.相应的,我们提供了对应的服务来做同样的事情.新服务的接口和原来不太一样.
专门为我们平台开发的程序当然不会存在问题,但是有大量的已有的程序和开源项目,就面临着繁杂的迁移工作.
Wrapper
其实从PHP4.3开始,PHP就支持Wrapper了,这意味着用户可以自定义和重载协议.
只需要使用stream_wrapper_register函数就可以注册一个协议,对这个协议的相关操作,PHP都会回调相关的函数.
手册上给了一个例子.它注册了一个叫var的协议,然后对这个协议操作都会回调VariableStreamclass里边定义的方法.
varname=$url["host"]; $this->position=0; returntrue; } functionstream_read($count) { $ret=substr($GLOBALS[$this->varname],$this->position,$count); $this->position+=strlen($ret); return$ret; } functionstream_write($data) { $left=substr($GLOBALS[$this->varname],0,$this->position); $right=substr($GLOBALS[$this->varname],$this->position+strlen($data)); $GLOBALS[$this->varname]=$left.$data.$right; $this->position+=strlen($data); returnstrlen($data); } functionstream_tell() { return$this->position; } functionstream_eof() { return$this->position>=strlen($GLOBALS[$this->varname]); } functionstream_seek($offset,$whence) { switch($whence){ caseSEEK_SET: if($offsetvarname])&&$offset>=0){ $this->position=$offset; returntrue; }else{ returnfalse; } break; caseSEEK_CUR: if($offset>=0){ $this->position+=$offset; returntrue; }else{ returnfalse; } break; caseSEEK_END: if(strlen($GLOBALS[$this->varname])+$offset>=0){ $this->position=strlen($GLOBALS[$this->varname])+$offset; returntrue; }else{ returnfalse; } break; default: returnfalse; } } } stream_wrapper_register("var","VariableStream") ordie("Failedtoregisterprotocol"); $myvar=""; $fp=fopen("var://myvar","r+"); fwrite($fp,"line1\n"); fwrite($fp,"line2\n"); fwrite($fp,"line3\n"); rewind($fp); while(!feof($fp)){ echofgets($fp); } fclose($fp); var_dump($myvar); ?>
回调class里边能实现的接口列表在这里:http://cn2.php.net/manual/en/class.streamwrapper.php
需要注意的一些问题
构造函数
首先是,wrapperclass很特别,它的构造函数并不是每次都调用的.只有在你的操作触发了stream_open相关的操作时才会调用,比如你用file_get_contents了.而当你的操作触发和stream无关的函数时,比如file_exists会触发url_stat方法,这个时候构造函数是不会被调用的.
读实现
wrapper里边有position和seek等概念,但是很多服务其实是一次性就读取全部数据的,这个可以在stream_open的时候一次性读回,放到一个属性中,以后seek和tell的时候直接操作属性里边存放的数据就可以了.
url_stat的实现
在wrapperclass的实现中,url_stat的实现是个难点.必须正确的实现url_stat才能使is_writable和is_readable等查询文件元信息的函数正常工作.
而我们需要为我们的虚设备伪造这些值.以mc为例,我给大家一些参考数据.
url_stat应该返回一个数组,分13个项,内容如下:
dev设备号-写0即可
inoinode号-写0即可
mode文件mode-这个是文件的权限控制符号,稍后详细说明
nlinklink-写0即可.
uiduid-Linux上用posix_get_uid可以取到,windows上为0
gidgid-Linux上用posix_get_gid可以取到,windows上为0
rdev设备类型-当为inode设备时有值
size文件大小
atime最后读时间格式为unix时间戳
mtime最后写时间
ctime创建时间
blksize blocksizeoffilesystemIO写零即可
blocks numberof512-byteblocksallocated写零即可
其中mode的值必须写对
如果是文件,其值为
0100000+文件权限;如0100000+0777;
如果是目录,其值为
040000+目录权限;如0400000+0777;
可以重载标准协议
根据实际测试来看,用stream_wrapper_unregister可以卸载掉http等内置协议.这就方便我们完全无缝的替换用户的一些操作,比如file_get_contents(‘http://sae.sina.com.cn')到我们自己实现的服务上.
知识点补充:
phpwrapper实现
【背景】
做一个thriftclient的wrapper,用以实现对于服务器的重试逻辑。
【关键点】
1.wrapper要求跟用client一样方便。
2.当某个服务器挂掉之后可以随机选另一台重试。
3.用到的php几个关键特性:__call()(magicfunction,当访问的对象函数不存在时会调用这个),ReflectionClass反射类及其其成员函数newInstanceArgs , call_user_func_array回调函数。
直接看代码吧(某位牛人写的,notme):
#!/usr/bin/envphp registerNamespace('Thrift','/usr/local/Cellar/thrift/0.9.1/'); $loader->registerDefinition('xiaoju',$GEN_DIR); $loader->register(); useThrift\Protocol\TBinaryProtocol; useThrift\Transport\TSocket; useThrift\Transport\THttpClient; useThrift\Transport\TBufferedTransport; useThrift\Exception\TException; classRetryWrapper{ publicfunction__construct($classname,$hosts){ $this->clazz=new\ReflectionClass($classname); $this->hosts=$hosts; } publicfunction__call($method,$args){ shuffle($this->hosts); foreach($this->hostsas$key=>$host){ try{ return$this->inner_call($host,$method,$args); }catch(TException$ex){ $msg=$ex->getMessage(); if(!strstr($msg,'TSocket')){ throw$ex; } } } thrownewTException("allserverdown!"); } publicfunctioninner_call($host,$method,$args){ $tmp=explode(":",$host); $socket=newTSocket($tmp[0],(int)$tmp[1]); $transport=newTBufferedTransport($socket,1024,1024); $protocol=newTBinaryProtocol($transport); $client=$this->clazz->newInstanceArgs(array($protocol)); $transport->open(); $result=call_user_func_array(array($client,$method),$args); $transport->close(); return$result; } } $hosts=array('localhost:9090','localhost:9091'); $wrapper=newRetryWrapper("\xxx\xx\MessageServiceClient",$hosts,3); $data=array('businessId'=>300100001,'phones'=>array('2','2','3'),'message'=>'asdfqer'); $message=new\xxx\xx\Message($data); print$wrapper->sendMessage($message); print"\n"; ?>
总结
到此这篇关于通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法的文章就介绍到这了,更多相关phpwrapper迁移新服务内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!