当前位置:首页 > PHP教程 > php高级应用 > 列表

通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法

发布:smiling 来源: PHP粉丝网  添加日期:2022-02-23 12:31:17 浏览: 评论:0 

出于性能和安全方面的考虑,公司的平台上禁用了本地文件读写和对外的数据抓取.相应的,我们提供了对应的服务来做同样的事情.新服务的接口和原来不太一样.

专门为我们平台开发的程序当然不会存在问题,但是有大量的已有的程序和开源项目,就面临着繁杂的迁移工作.

Wrapper

其实从PHP4.3开始,PHP就支持Wrapper了,这意味着用户可以自定义和重载协议.

只需要使用 stream_wrapper_register 函数就可以注册一个协议,对这个协议的相关操作,PHP都会回调相关的函数.

手册上给了一个例子. 它注册了一个叫var的协议,然后对这个协议操作都会回调VariableStream class里边定义的方法.

  1. varname = $url["host"]; 
  2. $this->position = 0; 
  3. return true; 
  4. } 
  5.  
  6. function stream_read($count) 
  7. { 
  8. $ret = substr($GLOBALS[$this->varname], $this->position, $count); 
  9. $this->position += strlen($ret); 
  10. return $ret; 
  11. } 
  12.  
  13. function stream_write($data) 
  14. { 
  15. $left = substr($GLOBALS[$this->varname], 0, $this->position); 
  16. $right = substr($GLOBALS[$this->varname], $this->position + strlen($data)); 
  17. $GLOBALS[$this->varname] = $left . $data . $right; 
  18. $this->position += strlen($data); 
  19. return strlen($data); 
  20. } 
  21.  
  22. function stream_tell() 
  23. { 
  24. return $this->position; 
  25. } 
  26.  
  27. function stream_eof() 
  28. { 
  29. return $this->position >= strlen($GLOBALS[$this->varname]); 
  30. } 
  31.  
  32. function stream_seek($offset, $whence) 
  33.  
  34. { 
  35.  
  36. switch ($whence) { 
  37.  
  38. case SEEK_SET: 
  39.  
  40. if ($offset < strlen($GLOBALS[$this->varname]) && $offset >= 0) { 
  41.  
  42. $this->position = $offset; 
  43.  
  44. return true; 
  45.  
  46. } else { 
  47.  
  48. return false; 
  49.  
  50. } 
  51.  
  52. break; 
  53.  
  54. case SEEK_CUR: 
  55.  
  56. if ($offset >= 0) { 
  57.  
  58. $this->position += $offset; 
  59.  
  60. return true; 
  61.  
  62. } else { 
  63.  
  64. return false; 
  65.  
  66. } 
  67.  
  68. break; 
  69.  
  70. case SEEK_END: 
  71.  
  72. if (strlen($GLOBALS[$this->varname]) + $offset >= 0) { 
  73.  
  74. $this->position = strlen($GLOBALS[$this->varname]) + $offset; 
  75.  
  76. return true; 
  77.  
  78. } else { 
  79.  
  80. return false; 
  81.  
  82. } 
  83.  
  84. break; 
  85.  
  86. default: 
  87.  
  88. return false; 
  89.  
  90. } 
  91.  
  92. } 
  93.  
  94. } 
  95.  
  96. stream_wrapper_register("var", "VariableStream") 
  97.  
  98. or die("Failed to register protocol"); 
  99.  
  100. $myvar = ""; 
  101.  
  102. $fp = fopen("var://myvar", "r+"); 
  103.  
  104. fwrite($fp, "line1\n"); 
  105.  
  106. fwrite($fp, "line2\n"); 
  107.  
  108. fwrite($fp, "line3\n"); 
  109.  
  110. rewind($fp); 
  111.  
  112. while (!feof($fp)) { 
  113.  
  114. echo fgets($fp); 
  115.  
  116. } 
  117.  
  118. fclose($fp); 
  119.  
  120. var_dump($myvar); 
  121.  
  122. ?> 

回调class里边能实现的接口列表在这里: http://cn2.php.net/manual/en/class.streamwrapper.php

需要注意的一些问题

构造函数

首先是,wrapper class很特别,它的构造函数并不是每次都调用的.只有在你的操作触发了stream_open相关的操作时才会调用,比如你用file_get_contents了.而当你的操作触发和stream无关的函数时,比如file_exists会触发url_stat方法,这个时候构造函数是不会被调用的.

读实现

wrapper里边有position和seek等概念,但是很多服务其实是一次性就读取全部数据的,这个可以在stream_open的时候一次性读回,放到一个属性中,以后seek和tell的时候直接操作属性里边存放的数据就可以了.

url_stat的实现

在wrapper class的实现中,url_stat的实现是个难点.必须正确的实现url_stat才能使is_writable和is_readable等查询文件元信息的函数正常工作.

而我们需要为我们的虚设备伪造这些值.以mc为例,我给大家一些参考数据.

url_stat应该返回一个数组,分13个项,内容如下:

dev 设备号- 写0即可

ino inode号 - 写0即可

mode 文件mode - 这个是文件的权限控制符号,稍后详细说明

nlink link - 写0即可.

uid uid - Linux上用posix_get_uid可以取到,windows上为0

gid gid - Linux上用posix_get_gid可以取到,windows上为0

rdev 设备类型 - 当为inode设备时有值

size 文件大小

atime 最后读时间 格式为unix时间戳

mtime 最后写时间

ctime 创建时间

blksize  blocksize of filesystem IO 写零即可

blocks  number of 512-byte blocks allocated 写零即可

其中mode的值必须写对

如果是文件,其值为

0100000 + 文件权限 ; 如 0100000 + 0777;

如果是目录,其值为

040000 + 目录权限 ; 如 0400000 + 0777;

可以重载标准协议

根据实际测试来看,用stream_wrapper_unregister可以卸载掉http等内置协议.这就方便我们完全无缝的替换用户的一些操作,比如file_get_contents(‘http://sae.sina.com.cn')到我们自己实现的服务上.

知识点补充:

php wrapper实现

【背景】

做一个thrift client的wrapper,用以实现对于服务器的重试逻辑。

【关键点】

1. wrapper要求跟用client一样方便。

2. 当某个服务器挂掉之后可以随机选另一台重试。

3. 用到的php几个关键特性: __call()(magic function,当访问的对象函数不存在时会调用这个), ReflectionClass 反射类及其其成员函数newInstanceArgs ,   call_user_func_array回调函数。

直接看代码吧(某位牛人写的,not me):

  1. #!/usr/bin/env php 
  2. <?php 
  3.    
  4. namespace wrapper; 
  5.    
  6. error_reporting(E_ALL); 
  7.    
  8. require_once '/usr/local/Cellar/thrift/0.9.1/Thrift/ClassLoader/ThriftClassLoader.php'; 
  9.    
  10. use Thrift\ClassLoader\ThriftClassLoader; 
  11.    
  12. $GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; 
  13.    
  14. $loader = new ThriftClassLoader(); 
  15. $loader->registerNamespace('Thrift', '/usr/local/Cellar/thrift/0.9.1/'); 
  16. $loader->registerDefinition('xiaoju', $GEN_DIR); 
  17. $loader->register(); 
  18.    
  19. use Thrift\Protocol\TBinaryProtocol; 
  20. use Thrift\Transport\TSocket; 
  21. use Thrift\Transport\THttpClient; 
  22. use Thrift\Transport\TBufferedTransport; 
  23. use Thrift\Exception\TException; 
  24.    
  25.    
  26. class RetryWrapper { 
  27.   public function __construct($classname, $hosts) { 
  28.     $this->clazz = new \ReflectionClass($classname); 
  29.     $this->hosts = $hosts; 
  30.   } 
  31.    
  32.   public function __call($method, $args) { 
  33.     shuffle($this->hosts); 
  34.     foreach ($this->hosts as $key => $host) { 
  35.       try { 
  36.         return $this->inner_call($host, $method, $args); 
  37.       } catch (TException $ex) { 
  38.         $msg = $ex->getMessage(); 
  39.         if (!strstr($msg, 'TSocket')) { 
  40.           throw $ex; 
  41.         } 
  42.       } 
  43.     } 
  44.     throw new TException("all server down!"); 
  45.   } 
  46.    
  47.   public function inner_call($host, $method, $args) { 
  48.     $tmp = explode(":", $host); 
  49.     $socket = new TSocket($tmp[0], (int)$tmp[1]); 
  50.     $transport = new TBufferedTransport($socket, 1024, 1024); 
  51.     $protocol = new TBinaryProtocol($transport); 
  52.     $client = $this->clazz->newInstanceArgs(array($protocol)); 
  53.    
  54.     $transport->open(); 
  55.     $result = call_user_func_array(array($client, $method), $args); 
  56.     $transport->close(); 
  57.     return $result; 
  58.   } 
  59. } 
  60.    
  61. $hosts = array('localhost:9090', 'localhost:9091'); 
  62. $wrapper = new RetryWrapper("\xxx\xx\MessageServiceClient", $hosts, 3); 
  63.    
  64. $data = array('businessId' => 300100001, 'phones' => array('2','2','3'), 'message' => 'asdfqer') ; 
  65. $message = new \xxx\xx\Message($data); 
  66.    
  67. print $wrapper->sendMessage($message); 
  68. print "\n"; 
  69.    
  70. ?>

Tags: Wrapper PHP迁移原有项目

分享到:

相关文章