通过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代码
Mar 08 PHP
PHP 向右侧拉菜单实现代码,测试使用中
Nov 03 PHP
php数据入库前清理 注意php intval与mysql的int取值范围不同
Dec 12 PHP
php验证手机号码(支持归属地查询及编码为UTF8)
Feb 01 PHP
CI框架中通过hook的方式实现简单的权限控制
Jan 07 PHP
php实现检查文章是否被百度收录
Jan 27 PHP
CI框架的安全性分析
May 18 PHP
PHP7扩展开发之hello word实现方法详解
Jan 15 PHP
php 可变函数使用小结
Jun 12 PHP
thinkphp5.0整合phpsocketio完整攻略(绕坑)
Oct 12 PHP
实例分析10个PHP常见安全问题
Jul 09 PHP
yii2.0框架使用 beforeAction 防非法登陆的方法分析
Sep 11 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
丧钟首部独立剧集《丧钟:骑士与龙》北美正式开播,场面血腥
2020/04/09 欧美动漫
PHP获取http请求的头信息实现步骤
2012/12/16 PHP
三种php连接access数据库方法
2013/11/11 PHP
php采用file_get_contents代替使用curl实例
2014/11/07 PHP
网页的分页下标生成代码(PHP后端方法)
2016/02/03 PHP
PHP共享内存用法实例分析
2016/02/12 PHP
Yii2基于Ajax自动获取表单数据的方法
2016/08/10 PHP
TNC vs IO BO3 第一场2.13
2021/03/10 DOTA
jQuery表格行换色的三种实现方法
2011/06/27 Javascript
Raphael一个用于在网页中绘制矢量图形的Javascript库
2013/01/08 Javascript
使用 Node.js 做 Function Test实现方法
2013/10/25 Javascript
jquery实现下拉菜单的二级联动利用json对象从DB取值显示联动
2014/03/27 Javascript
简单易用的倒计时js代码
2014/08/04 Javascript
jquery插件jquery.beforeafter.js实现左右拖拽分隔条对比图片的方法
2015/08/07 Javascript
全面解析Bootstrap表单使用方法(表单控件状态)
2015/11/24 Javascript
如何高效率去掉js数组中的重复项
2016/04/12 Javascript
深入理解js数组的sort排序
2016/05/28 Javascript
JavaScript实现点击文本自动定位到下拉框选中操作
2016/06/15 Javascript
vue.js 左侧二级菜单显示与隐藏切换的实例代码
2017/05/23 Javascript
浅谈Node.js ORM框架Sequlize之表间关系
2017/07/24 Javascript
详解vue-admin和后端(flask)分离结合的例子
2018/02/12 Javascript
layer实现登录弹框,登录成功后关闭弹框并调用父窗口的例子
2019/09/11 Javascript
WebStorm无法正确识别Vue3组合式API的解决方案
2021/02/18 Vue.js
[04:46]2018年度玩家喜爱的电竞媒体-完美盛典
2018/12/16 DOTA
教你如何在Django 1.6中正确使用 Signal
2014/06/22 Python
Python读取mat文件,并转为csv文件的实例
2018/07/04 Python
python bluetooth蓝牙信息获取蓝牙设备类型的方法
2019/11/29 Python
pd.DataFrame统计各列数值多少的实例
2019/12/05 Python
基于K.image_data_format() == 'channels_first' 的理解
2020/06/29 Python
pycharm Tab键设置成4个空格的操作
2021/02/26 Python
伦敦新晋轻奢耳饰潮牌:Tada & Toy
2020/05/25 全球购物
护理专业毕业生自荐信范文
2014/01/05 职场文书
欢度春节标语
2014/07/01 职场文书
生日寿星公答谢词
2015/09/29 职场文书
创业计划书之农家乐
2019/10/09 职场文书
利用javaScript处理常用事件详解
2021/04/14 Javascript