added version 1.0.0 of APIlite library,
added basic HTML template for help, added test class APIcalculator for demo
This commit is contained in:
parent
1a6e173da9
commit
59b317cacd
258
src/APIlite.php
Normal file
258
src/APIlite.php
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
Copyright (c) TPsoft.org 2000-2025
|
||||||
|
Author: Ing. Igor Mino <mino@tpsoft.org>
|
||||||
|
License: GNU GPL-3.0 or later
|
||||||
|
|
||||||
|
Description: A set of tools to simplify the work of creating backend APIs for your frontend projects.
|
||||||
|
Version: 1.0.0
|
||||||
|
|
||||||
|
Milestones:
|
||||||
|
2025-05-28 07:42 Igor - Created
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TPsoft\APIlite;
|
||||||
|
|
||||||
|
class APIlite
|
||||||
|
{
|
||||||
|
|
||||||
|
private string $apiName = '';
|
||||||
|
private $methods = array();
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
register_shutdown_function(array($this, '_shutdownHandler'));
|
||||||
|
$this->analyzeClass();
|
||||||
|
if (isset($_REQUEST['action'])) {
|
||||||
|
$this->doAction($_REQUEST['action']);
|
||||||
|
} else {
|
||||||
|
if (isset($_REQUEST['format']) && $_REQUEST['format'] == 'html') {
|
||||||
|
$this->printHelpHTML();
|
||||||
|
} else {
|
||||||
|
$this->printHelpJSON();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _shutdownHandler()
|
||||||
|
{
|
||||||
|
$error = error_get_last();
|
||||||
|
if ($error !== null && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) {
|
||||||
|
$this->responseERROR($error['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function analyzeClass(): bool
|
||||||
|
{
|
||||||
|
$refClass = new \ReflectionClass($this);
|
||||||
|
$this->apiName = $refClass->getName();
|
||||||
|
$this->methods = array();
|
||||||
|
foreach ($refClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $ref_method) {
|
||||||
|
$method_name = $ref_method->getName();
|
||||||
|
if (substr($method_name, 0, 1) == '_') continue;
|
||||||
|
$method = array(
|
||||||
|
'name' => $method_name,
|
||||||
|
'doc' => null,
|
||||||
|
'description' => null,
|
||||||
|
'params' => array(),
|
||||||
|
'return' => null
|
||||||
|
);
|
||||||
|
$docComment = $ref_method->getDocComment();
|
||||||
|
if ($docComment) {
|
||||||
|
$method['doc'] = trim($docComment);
|
||||||
|
$method['description'] = $this->parseDescription($method['doc']);
|
||||||
|
}
|
||||||
|
foreach ($ref_method->getParameters() as $ref_param) {
|
||||||
|
$param = array(
|
||||||
|
'name' => $ref_param->getName(),
|
||||||
|
'type' => null,
|
||||||
|
'optional' => $ref_param->isOptional(),
|
||||||
|
'default' => null,
|
||||||
|
'doc' => null,
|
||||||
|
);
|
||||||
|
if ($ref_param->hasType()) {
|
||||||
|
$param['type'] = $ref_param->getType()->getName();
|
||||||
|
}
|
||||||
|
if ($ref_param->isOptional()) {
|
||||||
|
$param['default'] = $ref_param->getDefaultValue();
|
||||||
|
}
|
||||||
|
$param['doc'] = $this->parseParamDoc($method['doc'], $param['name']);
|
||||||
|
$method['params'][] = $param;
|
||||||
|
}
|
||||||
|
if ($ref_method->hasReturnType()) {
|
||||||
|
$method['return'] = $ref_method->getReturnType()->getName();
|
||||||
|
}
|
||||||
|
$this->methods[] = $method;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseDescription(string $doc): string
|
||||||
|
{
|
||||||
|
$lines = explode("\n", $doc);
|
||||||
|
$desc = array();
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
$line = trim(trim($line, '/*'));
|
||||||
|
if (substr($line, 0, 1) == '@') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strlen($line) <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$desc[] = $line;
|
||||||
|
}
|
||||||
|
return implode("\n", $desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseParamDoc(string $doc, string $param_name): string|null
|
||||||
|
{
|
||||||
|
$lines = explode("\n", $doc);
|
||||||
|
if (substr($param_name, 0, 1) != '$') {
|
||||||
|
$param_name = '$' . $param_name;
|
||||||
|
}
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (strpos($line, $param_name) !== false) {
|
||||||
|
return trim(substr($line, strpos($line, $param_name) + strlen($param_name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function response(array $arr): void
|
||||||
|
{
|
||||||
|
ob_clean();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*';
|
||||||
|
header('Access-Control-Allow-Origin: ' . $origin);
|
||||||
|
header('Access-Control-Allow-Credentials: true');
|
||||||
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept');
|
||||||
|
echo json_encode($arr);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function responseOK(array $data): void
|
||||||
|
{
|
||||||
|
$this->response(array('status' => 'OK', 'data' => $data));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function responseERROR(string $error): void
|
||||||
|
{
|
||||||
|
$this->response(array('status' => 'ERROR', 'msg' => $error));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printHelpJSON(): void
|
||||||
|
{
|
||||||
|
$this->response(array(
|
||||||
|
'name' => $this->apiName,
|
||||||
|
'html_version' => $this->getCurrentUrl().'?format=html',
|
||||||
|
'actions' => $this->methods
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printHelpHTML(): void
|
||||||
|
{
|
||||||
|
include __DIR__ . '/help.tpl.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCurrentUrl(): string {
|
||||||
|
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ||
|
||||||
|
$_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
|
||||||
|
$host = $_SERVER['HTTP_HOST'];
|
||||||
|
$uri = $_SERVER['REQUEST_URI'];
|
||||||
|
$path = parse_url($uri, PHP_URL_PATH);
|
||||||
|
return $protocol . $host . $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMethod(string $action): ?array
|
||||||
|
{
|
||||||
|
foreach ($this->methods as $method) {
|
||||||
|
if ($method['name'] == $action) {
|
||||||
|
return $method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function doAction(string $action): void
|
||||||
|
{
|
||||||
|
$method = $this->getMethod($action);
|
||||||
|
if (is_null($method)) {
|
||||||
|
$this->responseERROR('Action "' . $action . '" not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$params = array();
|
||||||
|
foreach ($method['params'] as $param) {
|
||||||
|
if (isset($_REQUEST[$param['name']])) {
|
||||||
|
$param_value = $_REQUEST[$param['name']];
|
||||||
|
switch ($param['type']) {
|
||||||
|
case 'int':
|
||||||
|
$param_value = (int) $param_value;
|
||||||
|
break;
|
||||||
|
case 'float':
|
||||||
|
$param_value = (float) $this->fixDecimalPoint($param_value);
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
$param_value = (string) $param_value;
|
||||||
|
break;
|
||||||
|
case 'bool':
|
||||||
|
$param_value = (bool) $param_value;
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
$param_value = json_decode($param_value, true);
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
$param_value = json_decode($param_value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$params[$param['name']] = $param_value;
|
||||||
|
} else {
|
||||||
|
if (!$param['optional']) {
|
||||||
|
$this->responseERROR('Parameter "' . $param['name'] . '" is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$result = call_user_func_array(array($this, $method['name']), $params);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->responseERROR($e->getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->responseOK(array('status' => 'OK', 'data' => $result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fixDecimalPoint(mixed $number): float|array
|
||||||
|
{
|
||||||
|
if (is_array($number)) {
|
||||||
|
foreach ($number as $index => $val) {
|
||||||
|
$number[$index] = $this->fixDecimalPoint($val);
|
||||||
|
}
|
||||||
|
return $number;
|
||||||
|
}
|
||||||
|
$position_comma = strpos($number, ',');
|
||||||
|
$position_dot = strpos($number, '.');
|
||||||
|
if (
|
||||||
|
$position_comma !== false
|
||||||
|
&& $position_dot !== false
|
||||||
|
) {
|
||||||
|
if ($position_comma < $position_dot) { // e.g. 2,845,478.55
|
||||||
|
$_number = str_replace(',', '', $number);
|
||||||
|
} else { // e.g. 2.845.478,55
|
||||||
|
$_number = str_replace('.', '', $number);
|
||||||
|
}
|
||||||
|
$number = $_number;
|
||||||
|
}
|
||||||
|
$number = str_replace(' ', '', $number);
|
||||||
|
$number = str_replace(',', '.', $number);
|
||||||
|
$pos = strpos($number, '.');
|
||||||
|
if ($pos !== false) $number = rtrim($number, '0');
|
||||||
|
if (substr($number, -1) == '.') $number .= '0';
|
||||||
|
return $number;
|
||||||
|
}
|
||||||
|
}
|
37
src/help.tpl.php
Normal file
37
src/help.tpl.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo $this->apiName; ?></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1><?php echo $this->apiName; ?></h1>
|
||||||
|
|
||||||
|
<p>API documentation in <a href="<?php echo $this->getCurrentUrl(); ?>?format=json">JSON</a></p>
|
||||||
|
<p>API endpoint URL <a href="<?php echo $this->getCurrentUrl(); ?>"><?php echo $this->getCurrentUrl(); ?></a></p>
|
||||||
|
|
||||||
|
<?php if (is_array($this->methods)) foreach ($this->methods as $index => $method) { ?>
|
||||||
|
|
||||||
|
<h2><?php echo $method['name']; ?></h2>
|
||||||
|
<i><?php echo $method['description']; ?></i>
|
||||||
|
<p>Endpoint URL: <a href="<?php echo $this->getCurrentUrl(); ?>?action=<?php echo $method['name']; ?>"><?php echo $this->getCurrentUrl(); ?>?action=<?php echo $method['name']; ?></a></p>
|
||||||
|
|
||||||
|
<?php if (is_array($method['params'])) foreach ($method['params'] as $param) { ?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php echo $param['name']; ?>: <code><?php echo $param['type']; ?></code>
|
||||||
|
—
|
||||||
|
<?php
|
||||||
|
if ($param['optional']) echo '(optional) ';
|
||||||
|
echo $param['doc'];
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
126
test/APIcalculator.php
Normal file
126
test/APIcalculator.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class APIcalculator
|
||||||
|
*
|
||||||
|
* A calculator class that extends the functionality of TPsoft's APIlite.
|
||||||
|
* This class provides basic arithmetic operations and a few additional
|
||||||
|
* mathematical functions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../src/APIlite.php';
|
||||||
|
|
||||||
|
class APIcalculator extends \TPsoft\APIlite\APIlite
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add two numbers.
|
||||||
|
* Standard plus operation.
|
||||||
|
*
|
||||||
|
* @param float $a The first number.
|
||||||
|
* @param float $b The second number.
|
||||||
|
* @return float The sum of the two numbers.
|
||||||
|
*/
|
||||||
|
public function add(float $a, float $b): float
|
||||||
|
{
|
||||||
|
return $a + $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract two numbers.
|
||||||
|
* Standard minus operation.
|
||||||
|
*
|
||||||
|
* @param float $a The first number.
|
||||||
|
* @param float $b The second number.
|
||||||
|
* @return float The difference of the two numbers.
|
||||||
|
*/
|
||||||
|
public function subtract(float $a, float $b): float
|
||||||
|
{
|
||||||
|
return $a - $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiply two numbers.
|
||||||
|
* Standard multiplication operation.
|
||||||
|
*
|
||||||
|
* @param float $a The first number.
|
||||||
|
* @param float $b The second number.
|
||||||
|
* @return float The product of the two numbers.
|
||||||
|
*/
|
||||||
|
public function multiply(float $a, float $b): float
|
||||||
|
{
|
||||||
|
return $a * $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divide two numbers.
|
||||||
|
* Standard division operation. Throws an exception if dividing by zero.
|
||||||
|
*
|
||||||
|
* @param float $a The first number.
|
||||||
|
* @param float $b The second number.
|
||||||
|
* @return float The quotient of the two numbers. Throws an exception if dividing by zero.
|
||||||
|
* @throws \Exception If dividing by zero.
|
||||||
|
*/
|
||||||
|
public function divide(float $a, float $b): float
|
||||||
|
{
|
||||||
|
if ($b == 0) {
|
||||||
|
throw new \Exception('Division by zero');
|
||||||
|
}
|
||||||
|
return $a / $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raise a number to a power.
|
||||||
|
*
|
||||||
|
* @param float $a The base number.
|
||||||
|
* @param int $exponent The exponent.
|
||||||
|
* @return float The result of raising the base number to the exponent.
|
||||||
|
* @throws \Exception If the exponent is not an integer.
|
||||||
|
*/
|
||||||
|
public function power(float $a, int $exponent): float
|
||||||
|
{
|
||||||
|
$exponent = (int) $exponent;
|
||||||
|
if ($exponent == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return pow($a, $exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the square root of a number.
|
||||||
|
*
|
||||||
|
* @param int $a The number to calculate the square root of.
|
||||||
|
* @return float The square root of the number.
|
||||||
|
*/
|
||||||
|
public function sqrt(int $a): float
|
||||||
|
{
|
||||||
|
return sqrt($a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the absolute value of a number.
|
||||||
|
*
|
||||||
|
* @param float $a The number to calculate the absolute value of.
|
||||||
|
* @return float The absolute value of the number.
|
||||||
|
*/
|
||||||
|
public function abs(float $a): float
|
||||||
|
{
|
||||||
|
return abs($a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Round a number to the nearest integer.
|
||||||
|
*
|
||||||
|
* @param float $a The number to round.
|
||||||
|
* @param int $precision The number of decimal places to round to.
|
||||||
|
* @return int The rounded number.
|
||||||
|
*/
|
||||||
|
public function round(float $a, int $precision = 2): float
|
||||||
|
{
|
||||||
|
return round($a, $precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
new APIcalculator();
|
Loading…
x
Reference in New Issue
Block a user