在Yaf中使用Yar

在Yaf中使用Yar本文提到的两个东西,都是 PHP 大神 惠新宸 的作品。

  Yaf: PHP MVC框架 http://php.net/manual/zh/book.yaf.php

  Yar: PHP RPC框架 http://php.net/manual/zh/book.yar.php

  看着篇文章之前你需要 Yaf,Yar 有所了解。

  随着网站的发展,原本的系统会逐渐的实现细分,而细分的系统之间并不是全无关联,这就涉及到各系统之前数据传输问题,Yar 就是帮助我们解决这些问题的。

  虽然 Yar已经把底层数据传输给我们解决了,并且调用十分简单,但要部署上线还是需要自行处理一些问题的,所谓“师傅领进门,修行靠个人”。

  大致来说我们要解决下面这三个问题:

1.数据传输安全校验

2.接口定义

3.数据格式

  先贴出目录结构方便后面对应文字描述:

.

├── application

│ ├── Bootstrap.php

│ ├── controllers

│ │ ├── Api.php

│ │ ├── Error.php

│ │ └── Index.php

│ ├── library

│ │ └── Api

│ │ ├── Order.php

│ │ └── User.php

│ ├── models

│ ├── modules

│ │ └── Api

│ │ └── controllers

│ │ └── User.php

│ ├── plugins

│ │ └── Runtime.php

│ └── views

│ ├── error

│ │ └── error.phtml

│ └── index

│ └── index.phtml

├── conf

│ └── application.ini

├── libs

│ └── IZY

│ └── Sys

│ ├── ApiClient.php

│ └── ApiServer.php

└── public

└── index.php

  首先来定义服务端,定义之前考虑下,一个系统可能会为不同的模块单独定义不同的接口类

  比如一个电商系统,有会员模块,和订单模块,所以在定义接口时,把它们区分开来分开定义,所以把每一类的接口抽象为一个Server,定义如下。

  会员接口: http://servername/Api/user/ 订单接口: http://servername/Api/Order/

  所以在 Yaf 中需要这样写。

  Controller File: /application/controllers/Api.php

handle();

return false;

}

/**

* 订单接口

* @return boolean

*/

public function orderAction()

{

$service = new Yar_Server(new ApiOrder());

$service->handle();

return false;

}

}

以上我们的接口类文件指向了命令空间 Api 下的 User 类 与 Order 类, 在Yaf 下需要如此定义。

  User File: /application/library/Api/User.php

* @desc 会员对外接口

*/

namespace Api;

class User extends IZYSysApiServer

{

/**

* 获取会员信息

* @param int $userId 用户ID

* return Array [会员信息]

*/

public function getUserInfo($userId)

{

// ... 业务处理

$data = array('userName'=>'zhangsan','nickName'=>'张三','regTime'=>'2014-12-01 10:10:10');

return $this->response(1,$data);

}

}

Order File: /application/library/Api/Order.php

* @desc 订单对外接口

*/

namespace Api;

class Order extends IZYSysApiServer

{

/**

* 获取会员信息

* @param int $orderId 订单ID

* @param str $sign 签名

* return Array [订单信息]

*/

public function getOrderInfo($orderId,$sign)

{

//验证签名

if(!$this->checkSign(func_get_args(),$sign)){

return $this->response(0,array(

'code'=>'SIGN_ERROR',

'errMsg'=>'签名错误'

));

}

// ... 业务处理

$data = array('orderId'=>$orderId,'orderStatus'=>'success');

return $this->response(1, $data);

}

}

这两个类文件都继承了命名空间 IZYSys ApiServer 类,这个类实际上是一些通用方法的封装

  这个文件属于系统的核心文件,所以把它放在了 Yaf.ini 指定的 lib 下,这个根据具体指定而定,比如我的是:

  yaf.library = /data/webroot/izhengyin/libs

  内容如下:

 $val)

{

if(empty($val) || $val == $sign) continue;

$signStr .= $key.'='.$val.'&';

}

$signStr = rtrim($signStr,'&');

foreach (self:signs as $v){

if(md5($signStr.$v) === $sign){

return true;

}

}

return false;

}

/**

* 返回接口处理结果

* @param $status

* @param $data

* @param $other

* return Array [格式化好了的结果]

*/

protected function response($status,$data,$other=array())

{

$response = array ();

$response ['status'] = ( bool ) $status;

$response ['data'] = $data;

if (is_array ( $other ) && ! empty ( $other )) {

foreach ( $other as $k => $v ) {

// 附加信息不能使用的键名

if (! in_array ( $k, array (

'status',

'data'

) )) {

$response [$k] = $v;

}

}

}

return $response;

}

}

简单的封装了两个方法,验证签名,和返回数据格式定义,实际上就是解决 数据安全校验,与数据格式定义问题。

  完了以后写个测试文件,测试下。

  test.php

getUserInfo(10); 

echo '

'; 

var_dump($result);

?>

访问这个文件正常能看到 getUserInfo 的返回。

  上面是 server 的一些封装,接下来看下, client 的封装

  命名空间 IZYSys ApiClient 类

 $val)

{

if(empty($val)) continue;

$signStr .= $key.'='.$val.'&';

}

$signStr = rtrim($signStr,'&');

return md5($signStr.self:signs[mt_rand(0,count(self:signs)-1)]);

}

/**

* 调用服务端接口

* @param $server Api server

* @param $api 接口

* @param $params 参数

* @param $openSign 开启签名

* @param $callBack 回调

*/

public function call($server,$api,$params,$openSign=false,$callBack=null)

{

if($openSign){

$params['sign'] = $this->getSign($params);

}

if($callBack === null){

$client = new Yar_Client("http://izhengyin.com/Api/User");

return call_user_method_array($api, $client, $params);

}

$this->callNum ++;

$this->callBack = $callBack;

return Yar_Concurrent_Client::call($server,$api,$params,array($this, 'ApiClientCallBack'));

}

/**

* 执行并发调用

*/

public function loop()

{

return Yar_Concurrent_Client::loop();

}

/**

* 并发调用回调

* @param $retval

* @param $callinfo

*/

public function ApiClientCallBack($retval,$callinfo)

{

if($callinfo === null){

return $this->callBack($retval,$callinfo);

}

static $data = array();

$data[$callinfo['method']] = $retval;

if(count($data) == $this->callNum){

$fn = $this->callBack;

return $fn($data,$callinfo);

}

}

}

这个类封装了,取得签名的方法,已经并发调用的方法,使用时类似于这样:

  单个接口调用,并且不进行加密:

call("http://izhengyin.com/Api/User", 'getUserInfo', array(10));

var_dump($userInfo );

return false;

}

}

输出:

array(2) { ["status"]=> bool(true) ["data"]=> array(3) { ["userName"]=> string(8) "zhangsan" ["nickName"]=> string(6) "张三" ["regTime"]=> string(19) "2014-12-01 10:10:10" }

多个接口调用并且却 getOrderInfo 进行加密:

call("http://izhengyin.com/Api/User", 'getUserInfo', array(10),false,'callback'); 

$apiClinet->call("http://izhengyin.com/Api/Order", 'getOrderInfo', array(10),true,'callback');

$apiClinet->loop();

return false;

}

}

输出:

array(2) { ["getOrderInfo"]=> array(2) { ["status"]=> bool(true) ["data"]=> array(2) { ["ord erId"]=> int(10) ["orderStatus"]=> string(7) "success" } } ["getUserInfo"]=> array(2) { ["status"]=> bool(true) ["data"]=> array(3) { ["userName"]=> string(8) "zhangsan" ["nickName"]=> string(6) "张三" ["regTime"]=> string(19) "2014-12-01 10:10:10" } } }

评论

Popular Posts

如何提高情商(EQ)

地方门户网站项目可行性分析

快乐来自于做出「舍弃」的能力

2009年即将流行的牛逼语录:经典的我都无语了(转载)

小清新必须死

SolveigMM Video Splitter - 无损视频编辑工具(视频剪辑,合并等)

《挪威的森林》精彩篇章:永远记得我

不要再Best Regards了!英文Email四大问题

聪明处事72招

没有灵魂,只有交易——为何“苹果”会杀人