通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法
发布:smiling 来源: PHP粉丝网 添加日期:2022-02-23 12:31:17 浏览: 评论:0
出于性能和安全方面的考虑,公司的平台上禁用了本地文件读写和对外的数据抓取.相应的,我们提供了对应的服务来做同样的事情.新服务的接口和原来不太一样.
专门为我们平台开发的程序当然不会存在问题,但是有大量的已有的程序和开源项目,就面临着繁杂的迁移工作.
Wrapper
其实从PHP4.3开始,PHP就支持Wrapper了,这意味着用户可以自定义和重载协议.
只需要使用 stream_wrapper_register 函数就可以注册一个协议,对这个协议的相关操作,PHP都会回调相关的函数.
手册上给了一个例子. 它注册了一个叫var的协议,然后对这个协议操作都会回调VariableStream class里边定义的方法.
- varname = $url["host"];
- $this->position = 0;
- return true;
- }
- function stream_read($count)
- {
- $ret = substr($GLOBALS[$this->varname], $this->position, $count);
- $this->position += strlen($ret);
- return $ret;
- }
- function stream_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);
- return strlen($data);
- }
- function stream_tell()
- {
- return $this->position;
- }
- function stream_eof()
- {
- return $this->position >= strlen($GLOBALS[$this->varname]);
- }
- function stream_seek($offset, $whence)
- {
- switch ($whence) {
- case SEEK_SET:
- if ($offset < strlen($GLOBALS[$this->varname]) && $offset >= 0) {
- $this->position = $offset;
- return true;
- } else {
- return false;
- }
- break;
- case SEEK_CUR:
- if ($offset >= 0) {
- $this->position += $offset;
- return true;
- } else {
- return false;
- }
- break;
- case SEEK_END:
- if (strlen($GLOBALS[$this->varname]) + $offset >= 0) {
- $this->position = strlen($GLOBALS[$this->varname]) + $offset;
- return true;
- } else {
- return false;
- }
- break;
- default:
- return false;
- }
- }
- }
- stream_wrapper_register("var", "VariableStream")
- or die("Failed to register protocol");
- $myvar = "";
- $fp = fopen("var://myvar", "r+");
- fwrite($fp, "line1\n");
- fwrite($fp, "line2\n");
- fwrite($fp, "line3\n");
- rewind($fp);
- while (!feof($fp)) {
- echo fgets($fp);
- }
- fclose($fp);
- var_dump($myvar);
- ?>
回调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):
- #!/usr/bin/env php
- <?php
- namespace wrapper;
- error_reporting(E_ALL);
- require_once '/usr/local/Cellar/thrift/0.9.1/Thrift/ClassLoader/ThriftClassLoader.php';
- use Thrift\ClassLoader\ThriftClassLoader;
- $GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php';
- $loader = new ThriftClassLoader();
- $loader->registerNamespace('Thrift', '/usr/local/Cellar/thrift/0.9.1/');
- $loader->registerDefinition('xiaoju', $GEN_DIR);
- $loader->register();
- use Thrift\Protocol\TBinaryProtocol;
- use Thrift\Transport\TSocket;
- use Thrift\Transport\THttpClient;
- use Thrift\Transport\TBufferedTransport;
- use Thrift\Exception\TException;
- class RetryWrapper {
- public function __construct($classname, $hosts) {
- $this->clazz = new \ReflectionClass($classname);
- $this->hosts = $hosts;
- }
- public function __call($method, $args) {
- shuffle($this->hosts);
- foreach ($this->hosts as $key => $host) {
- try {
- return $this->inner_call($host, $method, $args);
- } catch (TException $ex) {
- $msg = $ex->getMessage();
- if (!strstr($msg, 'TSocket')) {
- throw $ex;
- }
- }
- }
- throw new TException("all server down!");
- }
- public function inner_call($host, $method, $args) {
- $tmp = explode(":", $host);
- $socket = new TSocket($tmp[0], (int)$tmp[1]);
- $transport = new TBufferedTransport($socket, 1024, 1024);
- $protocol = new TBinaryProtocol($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 = new RetryWrapper("\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";
- ?>
Tags: Wrapper PHP迁移原有项目
- 上一篇:PHP开发api接口安全验证操作实例详解
- 下一篇:最后一页
相关文章
- ·PHP Wrapper在SAE上的应用方法(2021-08-01)
推荐文章
热门文章
最新评论文章
- 写给考虑创业的年轻程序员(10)
- PHP新手上路(一)(7)
- 惹恼程序员的十件事(5)
- PHP邮件发送例子,已测试成功(5)
- 致初学者:PHP比ASP优秀的七个理由(4)
- PHP会被淘汰吗?(4)
- PHP新手上路(四)(4)
- 如何去学习PHP?(2)
- 简单入门级php分页代码(2)
- php中邮箱email 电话等格式的验证(2)