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


Posted in PHP onApril 02, 2020

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

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

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";
 
?>

总结

到此这篇关于通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法的文章就介绍到这了,更多相关php wrapper 迁移新服务内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

PHP 相关文章推荐
PHP 裁剪图片成固定大小代码方法
Sep 09 PHP
php面向对象全攻略 (十一)__toString()用法 克隆对象 __call处理调用错误
Sep 30 PHP
php设计模式之单例、多例设计模式的应用分析
Jun 30 PHP
php使用fgetcsv读取csv文件出现乱码的解决方法
Nov 08 PHP
php获取QQ头像并显示的方法
Dec 23 PHP
yii2实现根据时间搜索的方法
May 25 PHP
php基于PDO连接MSSQL示例DEMO
Jul 13 PHP
php基于dom实现的图书xml格式数据示例
Feb 03 PHP
php获取微信基础接口凭证Access_token
Aug 23 PHP
php判断目录存在的简单方法
Sep 26 PHP
php和js实现根据子网掩码和ip计算子网功能示例
Nov 09 PHP
php实现的简单多进程服务器类完整示例
Feb 01 PHP
php计数排序算法的实现代码(附四个实例代码)
Mar 31 #PHP
php设计模式之观察者模式实例详解【星际争霸游戏案例】
Mar 30 #PHP
TP5框架实现上传多张图片的方法分析
Mar 29 #PHP
thinkphp框架无限级栏目的排序功能实现方法示例
Mar 29 #PHP
php查看一个变量的占用内存的实例代码
Mar 29 #PHP
tp5框架前台无限极导航菜单类实现方法分析
Mar 29 #PHP
PHP中类与对象功能、用法实例解读
Mar 27 #PHP
You might like
一些使用频率比较高的php函数
2008/10/03 PHP
PHP执行SQL文件并将SQL文件导入到数据库
2015/09/17 PHP
ThinkPHP5 验证器的具体使用
2018/05/31 PHP
PHP终止脚本运行三种实现方法详解
2020/09/01 PHP
JavaScript国旗变换效果代码
2008/08/13 Javascript
js post方式传递提交的实现代码
2010/05/31 Javascript
javascript学习笔记--数字格式类型
2014/05/22 Javascript
火狐下input焦点无法重复获取问题的解决方法
2014/06/16 Javascript
js+csss实现的一个带复选框的下拉框
2014/09/29 Javascript
jquery验证手机号是否正确实例讲解
2015/11/17 Javascript
通用javascript代码判断版本号是否在版本范围之间
2015/11/29 Javascript
jQuery实现的分子运动小球碰撞效果
2016/01/27 Javascript
nodejs爬虫初试superagent和cheerio
2018/03/05 NodeJs
node.js到底要不要加分号浅析
2018/07/11 Javascript
node.js使用fs读取文件出错的解决方案
2019/10/23 Javascript
JS+CSS实现随机点名(实例代码)
2019/11/04 Javascript
详解vue beforeEach 死循环问题解决方法
2020/02/25 Javascript
Vue实现菜单切换功能
2020/11/08 Javascript
手写Vue源码之数据劫持示例详解
2021/01/04 Vue.js
Python 基础知识之字符串处理
2017/01/06 Python
Python设计模式之代理模式简单示例
2018/01/09 Python
python随机生成大小写字母数字混合密码(仅20行代码)
2020/02/01 Python
pandas中的ExcelWriter和ExcelFile的实现方法
2020/04/24 Python
Qoo10台湾站:亚洲领先的在线市场
2018/05/15 全球购物
澳大利亚最受欢迎的女士度假服装:Kabana Shop
2020/10/10 全球购物
internal修饰符起什么作用
2013/12/16 面试题
大学生毕业自我评价范文分享
2013/11/11 职场文书
《自然之道》教学反思
2014/02/11 职场文书
致跳高运动员加油稿
2014/02/12 职场文书
活动总结怎么写啊
2014/05/07 职场文书
英语专业毕业生求职信
2014/05/24 职场文书
优秀乡村医生事迹材料
2014/05/28 职场文书
2014年银行员工年终自我评价
2014/09/19 职场文书
2015年新学期寄语
2015/02/26 职场文书
MySQL 数据丢失排查案例
2021/05/08 MySQL
Golang表示枚举类型的详细讲解
2021/09/04 Golang