函式型介面
函式型介面
如果不是典型的物件增刪改查操作,可以設計函式型介面,比如登入、修改密碼、上傳檔案這些。
函式型介面一般實現在檔案 php/api_functions.php 中,它被主檔案api.php包含。 假設有以下介面定義:
獲取登入資訊(who am i?)
whoami() -> {id}
應用邏輯
- 許可權:AUTH_USER (必須使用者登入後才可用)
我們使用模擬資料實現介面,函式名規範為api_{介面名}
:
function api_whoami()
{
checkAuth(AUTH_USER);
return ["id" => 100];
}
在api_functions.php中,作為示例,其中已經定義了登入、退出等介面,實際開發時在其基礎上修改即可。 由於登入與許可權定義密切相關,為了瞭解原理,我們清空這個檔案,重新來寫登入、退出介面。 同時學習獲取引數、資料庫操作等常用函式。
[任務]
本節要求實現登入、退出、取登入資訊三個介面,設計如下:
登入介面
login(uname, pwd, _app?=user) -> {id, _isNew?}
使用者或員工登入(通過_app引數區分),如果是使用者登入且使用者不存在,可自動建立使用者。
引數
- _app: 前端應用名稱,用於區分登入型別,"user"-使用者端, "emp"-員工端。
返回
- _isNew: 如果是新註冊使用者,該欄位為1,否則不返回此欄位。
應用邏輯
- 許可權: AUTH_GUEST
- 對於使用者登入(_app是"user"),如果使用者不存在,則自動建立使用者。
- 密碼採用md5加密儲存
取登入資訊
whoami() -> {id}
如果已登入,則返回與登入介面相同的資訊,否則返回未登入錯誤。
使用者端或員工端均可用。
客戶端可呼叫本介面測試是否可以通過重用會話,實現免登入進入。
應用邏輯
- 許可權:AUTH_USER | AUTH_EMP
退出介面
logout()
退出登入。使用者端或員工端均可用。
應用邏輯
- 許可權:AUTH_USER | AUTH_EMP
在介面定義中,一般包括介面原型,引數及返回資料說明,應用邏輯等。 對於含義清晰的引數和返回資料,也不必一一說明。 應用邏輯中應先規定該介面的許可權。
許可權定義
在實現介面前,我們先了解如何定義許可權。
許可權定義在介面應用的主檔案api.php中,開啟它我們能看到登入型別和許可權型別的定義:
const AUTH_GUEST = 0;
// 登陸型別
const AUTH_USER = 0x01;
const AUTH_EMP = 0x02;
const AUTH_ADMIN = 0x04;
// AUTH_LOGIN是一個特殊的許可權,表示任一身份已登入。
define("AUTH_LOGIN", AUTH_USER | AUTH_EMP | AUTH_ADMIN);
// 許可權型別
const PERM_MGR = 0x08;
const PERM_TEST_MODE = 0x1000;
const PERM_MOCK_MODE = 0x2000;
$PERMS = [
AUTH_GUEST => "guest",
AUTH_USER => "user",
AUTH_EMP => "employee",
AUTH_ADMIN => "admin",
PERM_MGR => "manager",
PERM_TEST_MODE => "testmode",
PERM_MOCK_MODE => "mockmode",
];
上面按二進位制位數不同,定義登入型別和各類許可權,測試模式與模擬模式也可當作特殊的許可權來對待。 在全域性變數$PERMS中,為每個許可權指定了一個可讀的名字。
然後定義有一個重要的回撥函式onGetPerms
,它將根據登入情況、session中的資料或全域性變數來取出所有當前可能有的許可權,
後面常用的檢查許可權的函式hasPerm/checkAuth都將呼叫它:
function onGetPerms()
{
$perms = 0;
if (isset($_SESSION["uid"])) {
$perms |= AUTH_USER;
}
else if (isset($_SESSION["empId"])) {
$perms |= AUTH_EMP;
}
...
if (@$GLOBALS["TEST_MODE"]) {
$perms |= PERM_TEST_MODE;
}
...
return $perms;
}
在登入成功時,我們應設定相應的session變數,如使用者登入成功設定$_SESSION["uid"]
,員工登入成功設定$_SESSION["empId"]
,等等。
後面講物件型介面時,還會有另一個重要的回撥函式onCreateAC
,用於將許可權與類名進行繫結。
登入與退出
上節我們已經瞭解到,登入與許可權檢查密切相關,需要將使用者資訊存入session中,登入介面的大致實現如下:
function api_login()
{
$type = getAppType();
if ($type == "user") {
... 驗證成功 ...
$_SESSION["uid"] = ...
}
else if ($type == "emp") {
... 驗證成功 ...
$_SESSION["empId"] = ...
}
...
}
定義一個函式型介面,函式名稱一定要符合 api_{介面名}
的規範。介面名以小寫字母開頭。
在介面實現時,一般應根據介面中的許可權說明,使用checkAuth函式進行許可權檢查。
// 按設計要求,用md5加密後儲存密碼。
function hashPwd($pwd)
{
return md5($pwd);
}
function api_login()
{
$type = getAppType();
$uname = mparam("uname");
$pwd = mparam("pwd");
// 使用者登入,如不存在則自動建立新使用者
if ($type == "user") {
$sql = sprintf("SELECT id,pwd FROM User WHERE uname=%s", Q($uname));
$row = queryOne($sql, PDO::FETCH_ASSOC);
if ($row === false) {
// 自動註冊新使用者
$sql = sprintf("INSERT INTO User (uname, pwd) VALUES (%s, '%s')", Q($uname), hashPwd($pwd));
$id = execOne($sql, true);
$ret = [
"id" => $id,
"_isNew" => 1
];
}
else if (hashPwd($pwd) != $row["pwd"]) {
throw new MyException(E_AUTHFAIL, "bad password", "密碼錯誤");
}
else {
$ret = ["id" => $row["id"] ];
}
$_SESSION["uid"] = $ret["id"];
}
// 員工登入
else if ($type == "emp") {
$sql = sprintf("SELECT id,pwd FROM Employee WHERE uname=%s", Q($uname));
$row = queryOne($sql, PDO::FETCH_ASSOC);
if ($row === false || hashPwd($pwd) != $row["pwd"])
throw new MyException(E_AUTHFAIL, "bad uname or password", "使用者名稱或密碼錯誤");
$ret = ["id" => $row["id"] ];
$_SESSION["empId"] = $row["id"];
}
else {
throw new MyException(E_PARAM, "Unknown type `$type`");
}
return $ret;
}
在api_login函式中,先使用框架函式getAppType獲取到登入型別(也稱應用型別),再按登入型別分別查驗身份,並最終設定$_SESSION
相關變數,
這裡設定的變數與之前的許可權回撥函式onGetPerms
中相對應。
這裡使用了很多常用函式,比如獲取必需引數使用mparam函式,資料庫查詢使用了queryOne, execOne函式,出錯返回使用MyException等,之後章節將詳細介紹。
在實現whoami介面時,返回儲存在會話(session)中的變數即可,logout介面則更加簡單,直接銷燬會話:
function api_whoami()
{
checkAuth(AUTH_USER | AUTH_EMP);
// 也可以用AUTH_LOGIN這個特殊的許可權,表示任一身份已登入。
// checkAuth(AUTH_LOGIN);
if (hasPerm(AUTH_USER))
return ["id"=> $_SESSION["uid"]];
if (hasPerm(AUTH_EMP))
return ["id"=> $_SESSION["empId"]];
throw new MyException(E_SERVER);
}
function api_logout()
{
checkAuth(AUTH_LOGIN);
session_destroy();
}
[應用標識與應用型別]
在筋斗雲中,URL引數_app
稱為前端應用標識(app),預設為"user",表示使用者端應用。
不同應用要求使用不同的應用標識,在與後端的會話中使用的cookie也會有所不同,因而不同的應用即使同時在瀏覽器中開啟也不會相互干擾。
應用標識中的主幹部分稱為應用型別(app type),例如有三個應用分別標識為"emp"(員工端), "emp2"(經理端)和"emp-store"(商戶管理端), 它們的主幹部分(去除尾部數字,去除"-"及後面部分)是相同的,都是"emp",即它們具有相同的應用型別"emp"。
函式getAppType就是用來根據URL引數_app
取應用型別,不同的應用如果是相同的應用型別,則登入方式相同,比如上例中都是用員工登入。
相關文章
- 函式式介面函式
- Java函式式介面Java函式
- TypeScript 函式介面TypeScript函式
- 泛型函式泛型函式
- 詳解Java函式式介面Java函式
- TypeScript 函式型別TypeScript函式型別
- java8特性-函式式介面Java函式
- Java 8 Function 函式介面JavaFunction函式
- 介面、虛擬函式、純虛擬函式、抽象類函式抽象
- oracle 10g函式大全–日期型函式Oracle 10g函式
- oracle 10g函式大全--字元型函式Oracle 10g函式字元
- oracle 10g函式大全--日期型函式Oracle 10g函式
- Java 函式式介面 lamada 應用Java函式
- 簡析JAVA8函式式介面Java函式
- Java學習隨筆(函式式介面)Java函式
- Java @FunctionInterface函式式介面使用說明JavaFunction函式
- 第 8 節:函式-函式型別與作用域函式型別
- 函式引數 引數定義函式型別函式型別
- oracle 10g函式大全--數值型函式Oracle 10g函式
- PostgreSQL的 SPI_介面函式SQL函式
- 標準IO常用函式介面函式
- Java8之Stream-函式式介面Java函式
- 「Java8系列」神奇的函式式介面Java函式
- Java8的新特性--函式式介面Java函式
- java8 新特性之函式式介面Java函式
- JDK8新特性之函式式介面JDK函式
- 胡說-JavaScript函式型別JavaScript函式型別
- Java泛型建構函式Java泛型函式
- PostgreSQL:字元——型別及函式SQL字元型別函式
- 虛擬函式與多型函式多型
- C++ 介面(純虛擬函式)C++函式
- 學生成績判斷函式介面函式
- Lambda表示式入門--函數語言程式設計與函式式介面函數程式設計函式
- java8新特性之函式式介面、lambda表示式、介面的預設方法、方法和建構函式的引用Java函式
- 【SQL基礎】T-SQL函式型別——系統函式SQL函式型別
- Java8新特性探索之函式式介面Java函式
- Sql Server函式全解(3):資料型別轉換函式和文字影象函式SQLServer函式資料型別
- Sql Server函式全解(三)資料型別轉換函式和文字影像函式SQLServer函式資料型別