如何设计PHP业务模块(函数/方法)返回结果的结构?
如题:如何设计业务模块返回结果的结构?
一个业务函数/方法执行后,对外输出数据的结构通常有以下几种:
1、返回数字,如 成功时返回 0,失败时返回 -1,有的还会用一个全局变量输出错误信息:
<?php
class UserLogic extends \Think\Model {
protected $error; /**
* 更改用户名称
*
* @param int $userId 用户ID
* @param string $userName 用户名
* @return int -1: 操作失败, 0: 操作成功
*/
public function updateUserName($userId = 0, $userName = '') {
if (empty($userId)) {
$this->error = '用户ID不能为空';
return -1;
} if (empty($userName)) {
$this->error = '用户名不能为空';
return -1;
} $where = array(
'userId' => $userId
); $data = array(
'userName' => $userName
); $res = $this->where($where)->save($data);
if ($res !== false) {
return 0;
} return -1;
}
}
2、返回 bool 值,如成功时返回 true,失败时返回 false。
例子跟上面的差不多,这里就不写了。
3、抛出异常
可以使用类似以下代码抛出异常:
throw new \Exception('用户ID不能为空');
更详细的 PHP 抛出异常,可以阅读先前的文章:PHP中的错误处理、异常处理机制详解
不过,我不建议在 业务函数中,使用 throw new \Exception('xxx') 方式抛出异常,在一些基础框架中使用它比较合适,比如:
(1).文件不存在时;
(2).类不存在时;
(3).方法/函数名不存在时;
(4).数据库连接失败时;
等等情况。
4、返回一个数组结构,如
<?php
class UserLogic extends \Think\Model {
/**
* 更改用户名称
*
* @param int $userId 用户ID
* @param string $userName 用户名
* @return array
*/
public function updateUserName($userId = 0, $userName = '') {
$return = array(
'code' => 0, // 状态码, 0: 操作失败, 1: 操作成功
'msg' => '操作失败', // 消息语
'data' => array() // 如果需要, 可以附带数据
); if (empty($userId)) {
$return['msg'] = '用户ID不能为空';
return $return;
} if (empty($userName)) {
$return['msg'] = '用户名不能为空';
return $return;
} $where = array(
'userId' => $userId
); $data = array(
'userName' => $userName
); $res = $this->where($where)->save($data);
if ($res !== false) {
$return['code'] = 1;
$return['msg'] = '操作成功';
} return $return;
}
}
5、返回一个类实例:
上面返回一个统一的数组结构,我还是蛮喜欢的,在项目中,也使用过一段时间,
但是,越来越发现,在每个函数开头,都要事先写个 $return 数组结构,还是挺麻烦的。
$return = array(
'code' => 0, // 状态码, 0: 操作失败, 1: 操作成功
'msg' => '操作失败', // 消息语
'data' => array() // 如果需要, 可以附带数据
);
我们可以改造下,让它面向对象化。
首先,定义一个 业务操作结果类:Result.class.php
<?php
namespace Think; /**
* 业务操作结果类
*
* @package Think
* @autor 52php.cnblogs.com
*/
Class Result {
public $code = ''; // 状态码: success-成功, fail-失败
public $msg = ''; // 消息
public $data = array(); // 数据 /**
* 实例化
*/
public static function instance() {
return new self();
} /**
* 设置 状态码
*
* @param string $code 状态码
* @return $this
*/
public function code($code = null) {
!is_null($code) && ($this->code = $code);
return $this;
} /**
* 设置 消息
*
* @param string $msg 消息
* @return $this
*/
public function msg($msg = null) {
!is_null($msg) && ($this->msg = $msg);
return $this;
} /**
* 设置 数据
*
* @param array $data 数据
* @return $this
*/
public function data($data = null) {
!is_null($data) && ($this->data = $data);
return $this;
} /**
* 成功
*
* @param string $msg 消息
* @param array $data 数据
* @return $this
*/
public function success($msg = null, $data = null) {
$this->code = 'success';
is_null($msg) && ($this->msg === '') && ($this->msg = '操作成功');
!is_null($msg) && ($this->msg = $msg);
!is_null($data) && ($this->data = $data); return $this;
} /**
* 失败
*
* @param string $msg 消息
* @param array $data 数据
* @return $this
*/
public function fail($msg = null, $data = null) {
$this->code = 'fail';
is_null($msg) && ($this->msg === '') && ($this->msg = '操作失败');
!is_null($msg) && ($this->msg = $msg);
!is_null($data) && ($this->data = $data); return $this;
} /**
* 是否为 成功
*/
public function isSuccess() {
return ($this->code == 'success') ? true : false;
} /**
* 是否为 失败
*/
public function isFail() {
return ($this->code != 'success') ? true : false;
} /**
* 输出 (json数据)
*/
public function output() {
header('Content-type:text/json'); $res = array(
'code' => $this->code,
'msg' => $this->msg,
'data' => $this->data,
); if (isset($_REQUEST['jsoncallback'])) {
echo $_REQUEST['jsoncallback'] . '(' . json_encode($res) . ')';
} else {
echo json_encode($res);
} die();
}
}
这样,每个业务函数,统一输出 Result 类实例,一个业务函数调用另外一个或多个业务函数时,处理 业务异常 非常方便!
应用举例:
(1).业务模型中
<?php
namespace Common\Model; use Think\Model;
use Think\Result; class UserLogic extends Model {
/**
* 更改用户名称
*
* @param int $userId 用户ID
* @param string $userName 用户名
* @return Result
*/
public function updateUserName($userId = 0, $userName = '') {
if (empty($userId)) {
return Result::instance()->msg('用户ID不能为空')->fail();
} if (empty($userName)) {
return Result::instance()->msg('用户名不能为空')->fail();
} $where = array(
'userId' => $userId
); $data = array(
'userName' => $userName
); $res = $this->where($where)->save($data);
if ($res !== false) {
return Result::instance()->msg('操作成功')->success();
} return Result::instance()->msg('操作失败')->fail();
}
}
(2).控制器中
<?php
namespace Common\Model; use Think\Controller; class UserController extends Controller {
/**
* 更改用户名称
*/
public function updateUserName() {
$userId = I('REQUEST.userId');
$userName = I('REQUEST.userName'); // "开启" 事务
D('User')->startTrans(); $result = D('User', 'Logic')->updateUserName($userId, $userName);
if ($result->isSuccess()) {
// "提交" 事务
D('User')->commit();
} else {
// "回滚" 事务
D('User')->rollback();
} // 输出 API 的 json 结果 (主要是给 移动端 提供接口)
$result->output();
}
}
API返回的结果结构如下:
{
"code":"success",
"msg":"操作成功",
"data":[]
}
如何定义 API 输出结果的结构?
可以先阅读下文章:App架构设计经验谈:服务端接口的设计
一般也是定义一个统一的结构,如:
{
"code":"0",
"msg": "success",
"data": { "key1": "value1", "key2": "value2", ... }
}
很多人对这个“统一的输出结构”还是挺喜欢的,但是又发现他们对这个 code 值的设计 或 理解 或 使用 又是不对的。业界也没有统一的规则,所以比较乱象。
比如说,定义“code=400”,表示 “验证失败”,开发人员只要遇到“验证失败”的情况,统统返回 code=400 的错误状态码,
如:“用户不存在时”返回 code=400,“开始时间大于结束时间”也返回 code=400,“评论的内容不能为空”也返回 code=400,这样的设计和使用都是不对的。
如果非要把 code 的值设计为编码(一串数字,或一串数字和字母的组合)的话,它具有两层含义,一方面代表“状态码”,是成功还是失败,另一方面代表“错误码”,哪里出错了,出什么错。
同时,它还应该具有以下特性:
(1).唯一性
即一个编码代表具体的某个异常,如果 用 “code=400” 表示“评论的内容不能为空”的异常,就不能用“code=400”表示 “开始时间大于结束时间”的异常。
(2).多语言性
官方只需要对这个code错误码用一种语言(如英文)来描述错误详情,不同的开发使用者可以根据自己平台的需要,把这个错误信息(依据code)翻译成其他语言,如 中文,俄文等等。
(3).模块性
比如,用1开头的code值,表示某个模块里抛出的异常,用2开头的code值,表示另外一个模块抛出的异常。
可以参考下各大平台对 code 值的设计:
1、微信公众号
http://mp.weixin.qq.com/wiki/10/6380dc743053a91c544ffd2b7c959166.html
2、开心网
http://wiki.open.kaixin001.com/index.php?id=%E9%94%99%E8%AF%AF%E4%BB%A3%E7%A0%81%E9%87%8A%E4%B9%89
最后的建议:
如果你的项目不是“开放平台”类型(自己内部使用),再加上项目开发时间又比较紧,一时又没想好如何规划code值的时候,建议 code 只定义为 2 个值,成功时返回为“success”,失败时返回为“fail”。不要使用“true”和“false”,因为它们是各个开发语言的关键字,避免不必要的麻烦。
后记:
对旧项目的“返回一个数组结构”的业务模块接口,做了个小调整:
封装了一个函数,方便初始化,如:
<?php
/**
* 创建 业务函数/方法的(统一的)返回结构
*
* @param int $code 状态码 0-操作失败,1-操作成功
* @param string $msg 消息语
* @param array $data 返回数据
* @return array
*/
function core_return($code = 0, $msg = '操作失败', $data = array()) {
return array(
'code' => $code,
'msg' => $msg,
'data' => $data
);
}
业务函数中,可这样初始化
$return = core_return();
如何设计PHP业务模块(函数/方法)返回结果的结构?的更多相关文章
- Flask初学者:视图函数/方法返回值(HTML模板/Response对象)
返回HTML模板:使用“from flask import render_template”,在函数中传入相对于文件夹“templates”HTML模板路径名称字符串即可(默认模板路径),flask会 ...
- python中os模块函数方法详解最全最新
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 import os print(os.getcwd()) os.chdir("dirname") 改 ...
- GDB 修改当前判断函数的返回值(即修改寄存器的值)的方法
工作中遇到的问题: 在GDB调试时要进入下边该判断后边的函数,而m_EtherDecode.Chk_MakeSure_IP_Pkt(pPacket,dwPacketLen)的返回值是false,所以需 ...
- C++11用于计算函数对象返回类型的统一方法
[C++11用于计算函数对象返回类型的统一方法] 模板 std::result_of 被TR1 引进且被 C++11 所采纳,可允许我们决定和使用一个仿函数其回返值的类别.底下,CalculusVer ...
- 关于金蝶k3 wise供应生门户登陆界面屏蔽业务账套多余功能模块设置方法
关于金蝶k3 wise供应生门户登陆界面屏蔽业务账套多余功能模块设置方法 1. 找到以下路径 ...\Kingdee\K3ERP\KDHR\SITEFILE\WEBUI\ 找到“Login.aspx” ...
- Js基础知识5-函数返回值、函数参数、函数属性、函数方法
函数返回值 所有函数都有返回值,没有return语句时,默认返回内容为undefined,和其他面向对象的编程语言一样,return语句不会阻止finally子句的执行. function testF ...
- C函数实现返回多个值的方法
C语言中,一个函数最多只能实现一个返回值. int func (int b) { int a=5; if (a>b) return a; else return b; return 0; } ...
- [开源]OSharpNS 步步为营系列 - 1. 业务模块设计
什么是OSharp OSharpNS全称OSharp Framework with .NetStandard2.0,是一个基于.NetStandard2.0开发的一个.NetCore快速开发框架.这个 ...
- 将函数作为返回值的方法 - Python
有的时候,我们需要将函数作为返回值,以下为代码: def superfunc(): i = 0 def wrapper(): nonlocal i i +=1 return i return wrap ...
随机推荐
- django-redis和redis-py
项目之前使用memcache做缓存,现在转到redis,改写几个语句的事情,然后就这种我把django-redis和py-redis搞混了,记录一下. django默认使用memcache做缓存,这里 ...
- 【Beta】团队协作模式探讨试行
概述 鉴于Alpha阶段松散的结构和低下的效率,以及Scrum会议时间过长.文档不到位.无标准化验收等问题,尝试对协作模式作一点变化. 依照课程压力等实际情况,以及按照贡献分分配原则,以一周为贡献分计 ...
- BZOJ1031: [JSOI2007]字符加密Cipher
传送门 后缀数组模板题 //BZOJ 1031 //by Cydiater //2016.9.21 #include <iostream> #include <cstring> ...
- juqery 实现商城循环倒计时
<html> <hand> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jqu ...
- 利用mybatis的分页插件实现商品列表的显示
分析思路: 当我们点击查询商品的时候,会出现商品的列表,并按上下页可以实现分页的查询的功能. 首先首先我们先找到商品查询商品的按钮在jsp的那个页面,即首页index.jsp 这里有个url即显示商品 ...
- stamp-po的作用
stamp-po是表示po文件是否有更新,有更新,则重新编译一次
- 命名实参和可选实参 Named and Optional Arguments
1. 利用“命名实参”,您将能够为特定形参指定实参,方法是将实参与该形参的名称关联,而不是与形参在形参列表中的位置关联. static void Main(string[] args) { Conso ...
- python学习笔记-(二)python入门
1.第一个python程序 1.1 直接打印输出 打开cmd,输入python进入到python交互式环境:(看到>>>是在Python交互式环境下:) 在python交互环境下输入 ...
- MVC过滤器之 OnActionExcuted
Controller里 [SendMessage] public Action SendSmsMessage() { var resultExtendInfo=new ResultExtendInfo ...
- C# LIST列表的使用
1. List的基础.常用方法: 声明: 1.List<T> mList = new List<T>(); T为列表中元素类型,现在以string类型作为例子 E.g.: L ...