Skip to content

Instantly share code, notes, and snippets.

@naosim
Created January 3, 2017 02:17
Show Gist options
  • Save naosim/848b4c5479dc875540f66c49c5437e8e to your computer and use it in GitHub Desktop.
Save naosim/848b4c5479dc875540f66c49c5437e8e to your computer and use it in GitHub Desktop.
簡易なAPIライブラリ
<?php
namespace com\naosim\apilib;
use \Exception;
/**
* APIライブラリ
* 前提:
* - 1ファイル, 1APIで作成する
* - レスポンスはJSONとする (callbackがあればjsonp)
*/
class ApiUtil {
function send($map) {
$json = json_encode($map);
// JSONの場合
if(!isset($_GET['callback'])){
header("Content-type: application/json");
echo $json;
return;
}
// JSONPの場合
header("Content-type: application/javascript");
$callback=$_GET['callback'];
print <<<END
$callback($json);
END;
}
/**
* Exception Handling
*/
public function onException(Exception $e) {
$this->send($this->failedMap($e->getMessage()));
}
/**
* handle get call
* if $action throw exception, then return error response
* @var function $action
*/
function get(Api $api) {
try {
$result = $api->invoke($this, new Request($_GET));
if($result == null) return;
$this->send($result);
} catch(Exception $e) {
$this->onException($e);
}
}
/**
* handle post call
* if $action throw exception, then return error response
* @var function $action
*/
function post(Api $api) {
try {
$result = $api->invoke($this, new Request($_POST));
if($result == null) return;
$this->send($result);
} catch(Exception $e) {
$this->onException($e);
}
}
function successMap(array $map) {
return [
'status' => 'success' ,
'data' => $map
];
}
function failedMap(string $message) {
return [
'status' => 'failed',
'message' => $message
];
}
function getParamOrThrow(array $ary, string $key, Exception $exception) {
if(isset($ary[$key])) {
return $ary[$key];
} else {
throw $exception;
}
}
/**
* get value or throw
* @throws Exception
*/
function required(array $ary, string $key) {
return $this->getParamOrThrow($ary, $key, new Exception('required ' . $key));
}
/**
* get value or default
*/
function option(array $ary, string $key, $default) {
return $ary[$key] ?? $default;
}
/**
* call getValue() in value object.
*/
function ValuePicker($targetObject) {
/**
* if value is hoge, then called like $targetObject->getHoge()->getValue().
* if value is isFoo, then called like $targetObject->getIsHoge()->getValue().
* @var $targetObject value name
*/
return function ($key) use($targetObject) {
// create getter method
$methodName = 'get' . strtoupper(substr($key, 0, 1)) . substr($key, 1);
// starts with 'is' & upper case after 'is'
if(substr($key, 0, 2) === 'is' && ctype_upper(substr($key, 2, 1))) {
$methodName = $key;
}
return $targetObject->$methodName()->getValue();
};
}
}// end API
function GET(Api $api) {
(new ApiUtil())->get($api);
}
function POST(Api $api) {
(new ApiUtil())->post($api);
}
class Request {
private $params;
public function __construct(array $params) {
$this->params = $params;
}
public function getParams(): array {
return $this->params;
}
public function option($key, $default) {
return $this->params[$key] ?? $default;
}
public function required($key) {
return $this->orThrow($key, new Exception('required ' . $key));
}
public function orThrow(string $key, Exception $exception) {
if(isset($this->params[$key])) {
return $this->params[$key];
} else {
throw $exception;
}
}
}
interface Api {
function invoke(ApiUtil $apiUtil, Request $request): array;
}
@naosim
Copy link
Author

naosim commented Jan 3, 2017

ミニマムなサンプル

required_onceは適当に追加してください。

use function com\naosim\apilib\GET;
use com\naosim\apilib\Api;
use com\naosim\apilib\ApiUtil;
use com\naosim\apilib\Request;

class HogeApi implements Api {
    function invoke(ApiUtil $apiUtil, Request $request): array {
        $name = $request->required('name');
        $age = $request->required('age');

        return $apiUtil->successMap([
            'name' => $name,
            'age' => $age
        ]);
    }
}

GET(new HogeApi());

動作結果

成功

URL: http://localhost:8080/sample.php?name=mike&age=20

{
  "status": "success",
  "data": {
    "name": "mike",
    "age": "20"
  }
}

jsonpの場合
URL: http://localhost:8080/sample.php?name=mike&age=20&callback=foo

foo({"status":"success","data":{"name":"mike","age":"20"}}); 

失敗 (ageが足りない)

URL: http://localhost:8080/sample.php?name=mike

{
  "status": "failed",
  "message": "required age"
}

@naosim
Copy link
Author

naosim commented Jan 3, 2017

Api#invoke()の戻り値がそのままjsonで返る。
例外が発生した場合は、上記の失敗例と同じフォーマットで返る。
個人的にはerrorとdataが排他の関係になるとイイと思う。
例外時の返却をカスタマイズしたい場合は、ApiUtilを継承してonExceptionをオーバライドすればok。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment