5 Commits

Author SHA1 Message Date
2d3f8bfdd4 added list of method for HTML view 2025-06-12 07:54:37 +02:00
8502508a11 Merge branch 'main' of https://gitea.tpsoft.org/TPsoft.org/APIlite 2025-06-01 19:20:11 +02:00
a59044102c added format and endpoint into constructor,
fixed processing of parameters in methods,
added return for HTML manual,
chaged TypeScript code to class object export as default
2025-06-01 19:19:48 +02:00
b8ef58e132 added code types into README 2025-05-28 18:12:54 +02:00
2f7f63b620 removed require ext-pdo 2025-05-28 18:07:35 +02:00
5 changed files with 215 additions and 24 deletions

View File

@ -6,7 +6,7 @@ A set of tools to simplify the work of creating backend APIs for your frontend p
For example, we create an API for calculator. So we create class `APIcalculator` and store in file `test/APIcalculator.php` where we defined each actions for API as public method. For example, we create an API for calculator. So we create class `APIcalculator` and store in file `test/APIcalculator.php` where we defined each actions for API as public method.
``` ```php
<?php <?php
/** /**
@ -90,7 +90,7 @@ It is important to extend the `APIcalculator` class with the `\TPsoft\APIlite\AP
When you run this subfile through the webserver, you will see the JSON documentation in the browser When you run this subfile through the webserver, you will see the JSON documentation in the browser
``` ```js
{ {
"name": "APIcalculator", "name": "APIcalculator",
"html_version": "http://localhost/APIlite/test/APIcalculator.php?format=html", "html_version": "http://localhost/APIlite/test/APIcalculator.php?format=html",
@ -194,7 +194,7 @@ and there is also an HTML version available
To connect to the API from TypeScript (e.g. Vue application) it is possible to download the backend script To connect to the API from TypeScript (e.g. Vue application) it is possible to download the backend script
``` ```ts
/** /**
* Generated by APIlite * Generated by APIlite
* https://gitea.tpsoft.org/TPsoft.org/APIlite * https://gitea.tpsoft.org/TPsoft.org/APIlite

View File

@ -26,8 +26,7 @@
} }
], ],
"require": { "require": {
"php": ">=8.2", "php": ">=8.2"
"ext-pdo": "*"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View File

@ -18,23 +18,40 @@ class APIlite
{ {
private string $apiName = ''; private string $apiName = '';
private string $endpoint = '';
private $methods = array(); private $methods = array();
public function __construct() public function __construct(?string $format = null, ?string $endpoint = null)
{ {
register_shutdown_function(array($this, '_shutdownHandler')); register_shutdown_function(array($this, '_shutdownHandler'));
$this->endpoint = $endpoint ?? $this->getCurrentUrl();
$this->analyzeClass(); $this->analyzeClass();
if (isset($_REQUEST['action'])) { if (isset($_REQUEST['action'])) {
$this->doAction($_REQUEST['action']); $this->doAction($_REQUEST['action']);
} else { } else {
global $argv; global $argv;
$switches = array_map('strtolower', is_array($argv) ? $argv : []); if (isset($_REQUEST['format'])) {
if ((isset($_REQUEST['format']) && $_REQUEST['format'] == 'html') || in_array('--html', $switches)) { $format = $_REQUEST['format'];
$this->printHelpHTML(); }
} elseif ((isset($_REQUEST['format']) && $_REQUEST['format'] == 'typescript') || in_array('--typescript', $switches)) { if (isset($argv)) {
$this->printHelpTypescript(); $switches = array_map('strtolower', is_array($argv) ? $argv : []);
} else { if (in_array('--html', $switches)) {
$this->printHelpJSON(); $format = 'html';
}
if (in_array('--typescript', $switches)) {
$format = 'typescript';
}
}
switch ($format) {
case 'html':
$this->printHelpHTML();
break;
case 'typescript':
$this->printHelpTypescript();
break;
default:
$this->printHelpJSON();
break;
} }
} }
} }
@ -50,7 +67,7 @@ class APIlite
private function analyzeClass(): bool private function analyzeClass(): bool
{ {
$refClass = new \ReflectionClass($this); $refClass = new \ReflectionClass($this);
$this->apiName = $refClass->getName(); $this->apiName = $refClass->getShortName();
$this->methods = array(); $this->methods = array();
foreach ($refClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $ref_method) { foreach ($refClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $ref_method) {
$method_name = $ref_method->getName(); $method_name = $ref_method->getName();
@ -81,11 +98,28 @@ class APIlite
if ($ref_param->isOptional()) { if ($ref_param->isOptional()) {
$param['default'] = $ref_param->getDefaultValue(); $param['default'] = $ref_param->getDefaultValue();
} }
$param['doc'] = $this->parseParamDoc($method['doc'], $param['name']); if (!is_null($method['doc'])) {
$param['doc'] = $this->parseParamDoc($method['doc'], $param['name']);
}
$method['params'][] = $param; $method['params'][] = $param;
} }
if ($ref_method->hasReturnType()) { if ($ref_method->hasReturnType()) {
$method['return'] = $ref_method->getReturnType()->getName(); $ref_type = $ref_method->getReturnType();
if ($ref_type instanceof \ReflectionNamedType) {
$method['return'] = $ref_type->getName();
}
if ($ref_type instanceof \ReflectionUnionType
|| $ref_type instanceof \ReflectionIntersectionType )
{
$types = $ref_type->getTypes();
$method['return'] = [];
foreach ($types as $type) {
if ($type instanceof \ReflectionNamedType) {
$method['return'][] = $type->getName();
}
}
}
} }
$this->methods[] = $method; $this->methods[] = $method;
} }

View File

@ -236,6 +236,140 @@
} }
} }
/* Methods grid */
.methods-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.method-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
padding: 1.5rem;
text-align: center;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
}
.method-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.method-card:hover::before {
left: 100%;
}
.method-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
}
.method-card a {
color: white;
text-decoration: none;
font-weight: 600;
font-size: 1.1rem;
display: block;
position: relative;
z-index: 1;
}
/*
.method-card:nth-child(odd) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.method-card:nth-child(even) {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.method-card:nth-child(3n) {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.method-card:nth-child(4n) {
background: linear-gradient(135deg,rgb(86, 209, 127) 0%,rgb(52, 199, 172) 100%);
}
.method-card:nth-child(5n) {
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
}
*/
.method-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.methods-section-title {
font-size: 2rem;
font-weight: 700;
color: #2c3e50;
margin-bottom: 1.5rem;
text-align: center;
position: relative;
}
.methods-section-title::after {
content: '';
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 4px;
background: linear-gradient(90deg, #667eea, #764ba2);
border-radius: 2px;
}
/* Responsive adjustments for methods grid */
@media (max-width: 768px) {
.methods-grid {
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 0.8rem;
}
.method-card {
padding: 1.2rem;
}
.method-card a {
font-size: 1rem;
}
.methods-section-title {
font-size: 1.7rem;
}
}
@media (max-width: 480px) {
.methods-grid {
grid-template-columns: 1fr;
gap: 0.6rem;
}
.method-card {
padding: 1rem;
}
.method-card a {
font-size: 0.95rem;
}
.methods-section-title {
font-size: 1.5rem;
}
}
/* Utility classes */ /* Utility classes */
.text-center { .text-center {
text-align: center; text-align: center;
@ -266,6 +400,18 @@
</div> </div>
</nav> </nav>
<!-- List of Methods -->
<section class="method-section">
<h2 class="methods-section-title">List of Methods</h2>
<div class="methods-grid">
<?php if (is_array($this->methods)) foreach ($this->methods as $index => $method) { ?>
<div class="method-card">
<a href="#<?php echo $method['name']; ?>"><?php echo $method['name']; ?></a>
</div>
<?php } ?>
</div>
</section>
<!-- Main Content --> <!-- Main Content -->
<main class="main-content"> <main class="main-content">
<div class="endpoint-url mb-3"> <div class="endpoint-url mb-3">
@ -274,7 +420,7 @@
</div> </div>
<?php if (is_array($this->methods)) foreach ($this->methods as $index => $method) { ?> <?php if (is_array($this->methods)) foreach ($this->methods as $index => $method) { ?>
<section class="method-section"> <section class="method-section" id="<?php echo $method['name']; ?>">
<h2 class="method-title"><?php echo $method['name']; ?></h2> <h2 class="method-title"><?php echo $method['name']; ?></h2>
<?php if (!empty($method['description'])) { ?> <?php if (!empty($method['description'])) { ?>
@ -306,6 +452,16 @@
</div> </div>
</div> </div>
<?php } ?> <?php } ?>
<h3 style="margin-bottom: 1rem; color: #2c3e50; display: inline-block;">Return:</h3>
<?php if (is_string($method['return'])) { ?>
<code class="parameter-type"><?php echo $method['return']; ?></code>
<?php } ?>
<?php if (is_array($method['return'])) foreach ($method['return'] as $return) { ?>
<code class="parameter-type"><?php echo $return; ?></code>
<?php } ?>
</div> </div>
<?php } ?> <?php } ?>
</section> </section>

View File

@ -5,8 +5,8 @@
* <?php echo date('Y-m-d H:i:s'); ?> * <?php echo date('Y-m-d H:i:s'); ?>
*/ */
export const backend = { class <?php echo $this->apiName; ?> {
endpont: window.location.origin + "<?php echo $this->apiName; ?>.php", endpont = "<?php echo $this->endpoint; ?>";
/* ---------------------------------------------------- /* ----------------------------------------------------
* General API call * General API call
@ -31,7 +31,7 @@ export const backend = {
}); });
xhttp.open('POST', this.endpont + '?action=' + method); xhttp.open('POST', this.endpont + '?action=' + method);
xhttp.send(form_data); xhttp.send(form_data);
}, }
callPromise(method, data) { callPromise(method, data) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -43,20 +43,22 @@ export const backend = {
} }
}); });
}) })
}, }
/* ---------------------------------------------------- /* ----------------------------------------------------
* API actions * API actions
*/ */
help() { help() {
return this.callPromise('__HELP__', {}); return this.callPromise('__HELP__', {});
}, }
<?php if (is_array($this->methods)) foreach ($this->methods as $method) { <?php if (is_array($this->methods)) foreach ($this->methods as $method) {
echo "\t".$method['name'].'('.implode(', ', array_map(function($param) { return $param['name']; }, $method['params'])).') {'; echo "\t".$method['name'].'('.implode(', ', array_map(function($param) { return $param['name']; }, $method['params'])).') {';
echo "\n\t\treturn this.callPromise('".$method['name']."', {".implode(', ', array_map(function($param) { return $param['name'].': '.$param['name']; }, $method['params']))."});"; echo "\n\t\treturn this.callPromise('".$method['name']."', {".implode(', ', array_map(function($param) { return $param['name'].': '.$param['name']; }, $method['params']))."});";
echo "\n\t},\n\n"; echo "\n\t}\n\n";
} }
?> ?>
}; };
export default new BackendAPI();