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