1
0
mirror of https://github.com/beestat/app.git synced 2025-05-24 02:14:03 -04:00
beestat/api/cora/api_call.php

328 lines
7.5 KiB
PHP

<?php
namespace cora;
/**
* Process a single API call
*
* @author Jon Ziebell
*/
final class api_call {
/**
* The API call resource.
*
* @var string
*/
private $resource;
/**
* The API call method.
*
* @var string
*/
private $method;
/**
* The API call arguments.
*
* @var array
*/
private $arguments;
/**
* The API call alias.
*
* @var string
*/
private $alias;
/**
* Bypass the cache read.
*
* @var boolean
*/
private $bypass_cache_read;
/**
* Bypass the cache write.
*
* @var boolean
*/
private $bypass_cache_write;
/**
* Clear the cache for this API call, don't actually run the call.
*
* @var boolean
*/
private $clear_cache;
/**
* The current auto-alias. If an alias is not provided, an auto-alias is
* assigned.
*
* @var integer
*/
private static $auto_alias = 0;
/**
* The response of this API call.
*
* @var mixed
*/
private $response;
/**
* When this API call is cached until.
*
* @var string
*/
private $cached_until;
/**
* Construct a new API call.
*
* @throws exception if the resource is not provided.
* @throws exception if the method is not provided.
*
* @param array $api_call
*/
public function __construct($api_call) {
if(isset($api_call['resource']) === false) {
throw new \exception('Resource is required.', 1501);
}
if(isset($api_call['method']) === false) {
throw new \exception('Method is required.', 1502);
}
if(isset($api_call['arguments']) === false) {
$api_call['arguments'] = '{}';
}
$this->resource = $api_call['resource'];
$this->method = $api_call['method'];
$this->arguments = $this->parse_arguments($api_call['arguments']);
if(isset($api_call['alias']) === true) {
$this->alias = $api_call['alias'];
} else {
$this->alias = $this->get_auto_alias();
}
/**
* Note the following three parameters will come in as strings when not in
* a batch and boolean values when in a batch because of the JSON. Cast to
* boolean to support various representations.
*/
if(isset($api_call['bypass_cache_read']) === true) {
$this->bypass_cache_read = ((bool) $api_call['bypass_cache_read'] === true);
} else {
$this->bypass_cache_read = false;
}
if(isset($api_call['bypass_cache_write']) === true) {
$this->bypass_cache_write = ((bool) $api_call['bypass_cache_write'] === true);
} else {
$this->bypass_cache_write = false;
}
if(isset($api_call['clear_cache']) === true) {
$this->clear_cache = ((bool) $api_call['clear_cache'] === true);
} else {
$this->clear_cache = false;
}
}
/**
* Process the API call.
*
* @throws exception If the method does not exist.
*/
public function process() {
$this->restrict_private();
$resource_instance = new $this->resource();
if(method_exists($resource_instance, $this->method) === false) {
throw new \exception('Method does not exist.', 1503);
}
// Caching! If this API call is configured for caching,
if( // Is cacheable
isset($this->resource::$cache) === true &&
isset($this->resource::$cache[$this->method]) === true
) {
$api_cache_instance = new api_cache();
if($this->clear_cache === true) {
$this->response = $api_cache_instance->clear_cache($this);
$this->cached_until = date('Y-m-d H:i:s', strtotime('1970-01-01 00:00:01'));
} else {
$api_cache = $api_cache_instance->retrieve($this);
if($api_cache !== null && $this->bypass_cache_read === false) {
// If there was a cache entry available, use that.
$this->response = $api_cache['response_data'];
$this->cached_until = date('Y-m-d H:i:s', strtotime($api_cache['expires_at']));
} else {
// Else just run the API call, then cache it.
$this->response = call_user_func_array(
[$resource_instance, $this->method],
$this->arguments
);
if($this->bypass_cache_write === false) {
$api_cache = $api_cache_instance->cache(
$this,
$this->response,
$this->resource::$cache[$this->method]
);
$this->cached_until = date('Y-m-d H:i:s', strtotime($api_cache['expires_at']));
}
}
}
}
else { // Not cacheable
$this->response = call_user_func_array(
[$resource_instance, $this->method],
$this->arguments
);
}
}
/**
* Restrict private API calls.
*
* @throws exception If the method does not exist in the resource's
* public/private maps.
* @throws exception If the resource/method is private and the session is
* not valid.
*/
private function restrict_private() {
if(in_array($this->method, $this->resource::$exposed['private'])) {
$type = 'private';
} else if(in_array($this->method, $this->resource::$exposed['public'])) {
$type = 'public';
} else {
throw new exception('Method is not mapped.', 1504);
}
$session = session::get_instance();
if(
$type === 'private' &&
$session->is_valid() === false
) {
throw new exception('Session is expired.', 1505, false);
}
}
/**
* Gets an array of arguments in the correct order for the method being
* called.
*
* @param string $json The arguments JSON.
*
* @throws exception If the arguments in the api_call were not valid JSON.
*
* @return array The requested arguments.
*/
private function parse_arguments($json) {
$arguments = [];
// Arguments are not strictly required. If a method requires them then you
// will still get an error, but they are not required by the API.
if($json !== null) {
// All arguments are sent in the "arguments" key as JSON.
$decoded = json_decode($json, true);
if($decoded === null) {
throw new exception('Arguments are not valid JSON.', 1506);
}
$reflection_method = new \ReflectionMethod(
$this->resource,
$this->method
);
$parameters = $reflection_method->getParameters();
foreach($parameters as $parameter) {
if(isset($decoded[$parameter->getName()]) === true) {
$argument = $decoded[$parameter->getName()];
}
else {
if($parameter->isOptional() === true) {
$argument = $parameter->getDefaultValue();
} else {
$argument = null;
}
}
$arguments[] = $argument;
}
}
return $arguments;
}
/**
* Get the resource.
*
* @return string
*/
public function get_resource() {
return $this->resource;
}
/**
* Get the method.
*
* @return string
*/
public function get_method() {
return $this->method;
}
/**
* Get the arguments.
*
* @return string
*/
public function get_arguments() {
return $this->arguments;
}
/**
* Get the alias.
*
* @return string
*/
public function get_alias() {
return $this->alias;
}
/**
* Get the response.
*
* @return mixed
*/
public function get_response() {
return $this->response;
}
/**
* Get cached_until property.
*
* @return number
*/
public function get_cached_until() {
return $this->cached_until;
}
/**
* Get the next auto-alias.
*
* @return number
*/
private function get_auto_alias() {
return api_call::$auto_alias++;
}
}