8 Commits

Author SHA1 Message Date
fab8efd780 added bin/apilite-files for generate best-practce files 2026-02-09 07:33:17 +01:00
746b06e317 added response all result for special call __HELP__ 2025-10-16 01:39:59 +02:00
3fa131e1b2 fixed API name for export in template 2025-10-16 01:30:46 +02:00
c0fd7b3fe5 added apiName 2025-10-13 23:36:34 +02:00
dab07e55ec fixed typo 2025-06-15 19:05:30 +02:00
a10e864ba3 transformed spaces to tabs 2025-06-12 08:28:15 +02:00
9d45bb5ceb update example of typescript in README 2025-06-12 08:26:58 +02:00
f1e6e92f46 added instruction of installation into README 2025-06-12 08:22:21 +02:00
6 changed files with 232 additions and 53 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/composer.lock
/vendor

View File

@ -2,6 +2,44 @@
A set of tools to simplify the work of creating backend APIs for your frontend projects. Includes tutorials, patterns and practical examples for creating projects based on REST APIs. A set of tools to simplify the work of creating backend APIs for your frontend projects. Includes tutorials, patterns and practical examples for creating projects based on REST APIs.
## Installation
Download source code and add to your project
```php
<?php
require_once __DIR__ . '/APIlite/src/APIlite.php';
```
or use composer
```bash
composer require tpsoft/apilite
```
and add to your project
```php
<?php
require __DIR__ . '/vendor/autoload.php';
class YourAPI extends \TPsoft\APIlite\APIlite {
/**
* My first method for greetings.
*
* @param string $name Your name
* @param int $age Your age
* @return string Greetings
*/
public function myFirstMethod(string $name, int $age): string
{
return 'Hi, I`m '.$name.' and I`m '.$age.' years old.';
}
}
```
## Basic usage ## Basic usage
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.
@ -199,10 +237,10 @@ To connect to the API from TypeScript (e.g. Vue application) it is possible to d
* Generated by APIlite * Generated by APIlite
* https://gitea.tpsoft.org/TPsoft.org/APIlite * https://gitea.tpsoft.org/TPsoft.org/APIlite
* *
* 2025-05-28 15:44:07 */ * 2025-06-12 06:24:33 */
export const backend = { class APIcalculator {
endpont: window.location.origin + "APIcalculator.php", endpont = "http://";
/* ---------------------------------------------------- /* ----------------------------------------------------
* General API call * General API call
@ -227,7 +265,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) => {
@ -239,34 +277,34 @@ export const backend = {
} }
}); });
}) })
}, }
/* ---------------------------------------------------- /* ----------------------------------------------------
* API actions * API actions
*/ */
help() { help() {
return this.callPromise('__HELP__', {}); return this.callPromise('__HELP__', {});
}, }
add(a, b) { add(a, b) {
return this.callPromise('add', {a: a, b: b}); return this.callPromise('add', {a: a, b: b});
}, }
subtract(a, b) { subtract(a, b) {
return this.callPromise('subtract', {a: a, b: b}); return this.callPromise('subtract', {a: a, b: b});
}, }
multiply(a, b) { multiply(a, b) {
return this.callPromise('multiply', {a: a, b: b}); return this.callPromise('multiply', {a: a, b: b});
}, }
divide(a, b) { divide(a, b) {
return this.callPromise('divide', {a: a, b: b}); return this.callPromise('divide', {a: a, b: b});
}, }
}; };
export default new BackendAPI();
``` ```
These outputs can also be generated in the command line as follows These outputs can also be generated in the command line as follows

129
bin/apilite-files Normal file
View File

@ -0,0 +1,129 @@
#!/usr/bin/env php
<?php
include $_composer_autoload_path ?? __DIR__ . '/../vendor/autoload.php';
echo " APIlite files\n\n";
$project_root = findProjectRoot(__DIR__);
if (!$project_root) {
echo "⛔ Project root not found\n";
exit(1);
}
$composer_arr = json_decode(file_get_contents($project_root . '/composer.json'), true);
echo "Project root: {$project_root}\n";
$source_dirs = array_values($composer_arr['autoload']['psr-4']);
foreach ($source_dirs as $index => $source_dir) {
echo "➡️ [$index]: $project_root/$source_dir\n";
}
$selected = readline("❔ Select source dir [default: 0]: ");
$selected = empty($selected) ? 0 : $selected;
if (!isset($source_dirs[$selected])) {
echo "⛔ Invalid selection\n";
exit(2);
}
$source_dir = $project_root . '/' . $source_dirs[$selected];
$source_dir = rtrim($source_dir, '/') . '/';
$namespaces = array_keys($composer_arr['autoload']['psr-4']);
$namespace = $namespaces[$selected];
echo "✔️ Source dir: {$source_dir} with namespace: {$namespace}\n";
// Copy files
echo " Copy files\n";
copyFile($source_dir . 'API.php', '<' . '?php
namespace ' . rtrim($namespace, '\\') . ';
use TPsoft\APIlite\APIlite;
class API extends APIlite {
}
');
copyFile($project_root . '/scripts/buildTypeScript.php', '<' . '?php
require __DIR__ . \'/../vendor/autoload.php\';
ob_start();
$backend_api = new TPsoft\BugreportBackend\API(\'typescript\', \'import.meta.env.VITE_BACKENDAPI_URL\', \'backend\');
$output = ob_get_contents();
ob_end_clean();
$ts_path = realpath(__DIR__ . \'/../../frontend/src\').\'/backend.js\';
$suc = file_put_contents($ts_path, $output);
if ($suc === false) {
echo "✗ TypeScript store into file failed\n";
exit(2);
}
echo "✓ TypeScript backend script created\n";
');
copyFile($project_root . '/public/API.php', '<' . '?php
require_once __DIR__.\'/../src/API.php\';
new \\' . $namespace . 'API();
');
// Change composer.json
if (!isset($composer_arr['scripts']['build'])) {
$composer_arr['scripts']['build'] = 'php scripts/buildTypeScript.php';
}
$composer_json = json_encode($composer_arr, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$composer_json = preg_replace_callback(
'/^( +)/m',
fn($m) => str_repeat("\t", intdiv(strlen($m[1]), 4)),
$composer_json
);
$suc = file_put_contents($project_root . '/composer.json', $composer_json);
if ($suc) {
echo "✅ File changed: $project_root/composer.json\n";
} else {
echo "🔴 Failed to change file: $project_root/composer.json\n";
}
/**
* Functions
*/
function findProjectRoot(string $startDir): ?string
{
$dir = realpath($startDir);
if (!$dir) return null;
while ($dir && $dir !== dirname($dir)) {
if (is_file($dir . '/vendor/autoload.php')) {
return $dir;
}
$dir = dirname($dir);
}
return null;
}
function copyFile(string $destination, string $content): bool
{
$dir = dirname($destination);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
if (file_exists($destination)) {
echo "⚠️ File already exists: $destination\n";
$confirm = readline("❔ Overwrite? (y - yes, other - no): ");
if ($confirm != 'y') {
return false;
}
}
$suc = file_put_contents($destination, $content) !== false;
if ($suc) {
echo "✅ File created: $destination\n";
} else {
echo "🔴 Failed to create file: $destination\n";
}
return $suc;
}

View File

@ -32,5 +32,8 @@
"psr-4": { "psr-4": {
"TPsoft\\APIlite\\": "src/" "TPsoft\\APIlite\\": "src/"
} }
} },
"bin": [
"bin/apilite-files"
]
} }

View File

@ -21,10 +21,11 @@ class APIlite
private string $endpoint = ''; private string $endpoint = '';
private $methods = array(); private $methods = array();
public function __construct(?string $format = null, ?string $endpoint = null) public function __construct(?string $format = null, ?string $endpoint = null, ?string $apiName = null)
{ {
register_shutdown_function(array($this, '_shutdownHandler')); register_shutdown_function(array($this, '_shutdownHandler'));
$this->endpoint = $endpoint ?? $this->getCurrentUrl(); $this->endpoint = $endpoint ?? $this->getCurrentUrl();
$this->apiName = $apiName ?? '';
$this->analyzeClass(); $this->analyzeClass();
if (isset($_REQUEST['action'])) { if (isset($_REQUEST['action'])) {
$this->doAction($_REQUEST['action']); $this->doAction($_REQUEST['action']);
@ -67,7 +68,9 @@ class APIlite
private function analyzeClass(): bool private function analyzeClass(): bool
{ {
$refClass = new \ReflectionClass($this); $refClass = new \ReflectionClass($this);
if (strlen($this->apiName) <= 0) {
$this->apiName = $refClass->getShortName(); $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();

View File

@ -6,7 +6,7 @@
*/ */
class <?php echo $this->apiName; ?> { class <?php echo $this->apiName; ?> {
endpont = "<?php echo $this->endpoint; ?>"; endpoint = <?php echo sprintf(substr($this->endpoint, 0, 4) == 'http' ? '"%s"' : '%s', $this->endpoint); ?>;
/* ---------------------------------------------------- /* ----------------------------------------------------
* General API call * General API call
@ -29,13 +29,17 @@ class <?php echo $this->apiName; ?> {
if (typeof val == 'object') val = JSON.stringify(val); if (typeof val == 'object') val = JSON.stringify(val);
form_data.append(key, val); form_data.append(key, val);
}); });
xhttp.open('POST', this.endpont + '?action=' + method); xhttp.open('POST', this.endpoint + '?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) => {
this.call(method, data, function(response) { this.call(method, data, function(response) {
if (method == '__HELP__') {
resolve(response);
return;
}
if (response.status == 'OK') { if (response.status == 'OK') {
resolve(response.data); resolve(response.data);
} else { } else {
@ -61,4 +65,4 @@ class <?php echo $this->apiName; ?> {
}; };
export default new BackendAPI(); export default new <?php echo $this->apiName; ?>();