Merge branch 'master' of github.com:intellivoid/KimchiAPI

This commit is contained in:
Zi Xing 2022-01-02 09:44:39 -05:00
commit eefda29f88
9 changed files with 297 additions and 200 deletions

View File

@ -0,0 +1,12 @@
<?php
namespace Methods\v1;
class Ping extends \KimchiAPI\Abstracts\Command
{
public function execute()
{
// TODO: Implement execute() method.
}
}

View File

@ -0,0 +1,33 @@
{
"name": "Example API service",
"version": "1.0.0.0",
"author": "Zi Xing Narrakas",
"organization": "Intellivoid Technologies",
"configuration": {
"logging_enabled": true,
"root_path": "/test",
"framework_signature": true,
"api_signature": true,
"headers": {}
},
"versions": [
{
"version": "v1",
"enabled": true,
"namespace": "Methods\\v1",
"response_standard": "1.0",
"methods": [
{"method": ["GET", "POST"], "path": "ping", "class": "Ping", "params": []}
]
},
{
"version": "v2",
"enabled": true,
"namespace": "Methods\\v2",
"response_standard": "2.0",
"methods": [
{"method": ["GET", "POST"], "path": "ping", "class": "Ping", "params": []}
]
}
]
}

View File

@ -0,0 +1,54 @@
<?php
namespace KimchiAPI\Abstracts;
use KimchiAPI\KimchiAPI;
use KimchiAPI\Objects\Request;
abstract class Command
{
/**
* Auth level for user commands
*/
public const AUTH_USER = 'User';
/**
* Auth level for system commands
*/
public const AUTH_SYSTEM = 'System';
/**
* Auth level for admin commands
*/
public const AUTH_ADMIN = 'Admin';
/**
* KimchiAPI Object
*
* @var KimchiAPI
*/
protected $KimchiAPI;
/**
* Request object
*
* @var Request
*/
protected $Request;
/**
* The name of the method
*
* @var string
*/
protected $Name;
/**
* A description of the method
*
* @var string
*/
protected $Description;
abstract public function execute();
}

View File

@ -1,24 +0,0 @@
<?php
namespace KimchiAPI\Abstracts;
abstract class Method
{
protected $KimchiAPI;
protected $Request;
protected $Name;
protected $Description;
protected $DocumentationURL;
protected $Version;
protected $Enabled;
abstract public function execute();
}

View File

@ -3,19 +3,10 @@
namespace KimchiAPI;
// Define server information for response headers
use Exception;
use KimchiAPI\Abstracts\Method;
use KimchiAPI\Abstracts\Command;
use KimchiAPI\Exceptions\IOException;
use KimchiAPI\Exceptions\MethodAlreadyRegisteredException;
use KimchiAPI\Exceptions\MethodNotFoundException;
use KimchiAPI\Exceptions\MissingComponentsException;
use KimchiAPI\Interfaces\MethodInterface;
use KimchiAPI\Objects\Request;
use KimchiAPI\Objects\Response;
use KimchiAPI\Utilities\Converter;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RegexIterator;
use RuntimeException;
if(defined("KIMCHI_API_SERVER") == false)
@ -37,117 +28,84 @@
class KimchiAPI
{
/**
* An array of declared methods
*
* @var string[]
* @var array
*/
private $methods_path;
private $commands_paths;
/**
* @var array
*/
private $command_classes;
/**
* Server constructor.
*/
public function __construct()
{
$this->methods_path = [];
$this->commands_paths = [];
$this->command_classes = [];
}
/**
* Return the list of commands paths
*
* @return array
*/
public function getCommandsPaths(): array
{
return $this->commands_paths;
}
/**
* Return the list of command classes
*
* @return array
*/
public function getCommandClasses(): array
{
return $this->command_classes;
}
/**
* Add a single custom commands path
*
* @param string $path Custom methods' path to add
* @param string $version The version of the methods to add
* @param bool $before If the path should be prepended or appended to the list
* @param string $path Custom commands path to add
* @param bool $before If the path should be prepended or appended to the list
* @throws IOException
*/
public function addMethodsPath(string $path, string $version, bool $before=true)
public function addCommandsPath(string $path, bool $before=true)
{
if (!is_dir($path))
{
new IOException('Methods path "' . $path . '" does not exist.');
throw new IOException('Commands path "' . $path . '" does not exist.');
}
$this->methods_path[$version] = $path;
}
/**
* Sanitize Method
*
* @param string $command
* @return string
*/
protected function sanitizeMethod(string $command): string
{
return str_replace(' ', '', $this->ucWordsUnicode(str_replace('_', ' ', $command)));
}
/**
* Replace function `ucwords` for UTF-8 characters in the class definition and commands
*
* @param string $str
* @param string $encoding (default = 'UTF-8')
*
* @return string
*/
protected function ucWordsUnicode(string $str, string $encoding = 'UTF-8'): string
{
return mb_convert_case($str, MB_CASE_TITLE, $encoding);
}
/**
* Replace function `ucfirst` for UTF-8 characters in the class definition and commands
*
* @param string $str
* @param string $encoding (default = 'UTF-8')
*
* @return string
*/
protected function ucFirstUnicode(string $str, string $encoding = 'UTF-8'): string
{
return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
. mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
}
public function getCommandsList(): array
{
$commands = [];
foreach ($this->methods_path as $version)
elseif (!in_array($path, $this->commands_paths, true))
{
foreach($this->methods_path[$version] as $path)
if ($before)
{
try {
//Get all "*Command.php" files
$files = new RegexIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path)
),
'/^.+Method.php$/'
);
foreach ($files as $file) {
//Remove "Method.php" from filename
$command = $this->sanitizeMethod(substr($file->getFilename(), 0, -11));
$command_name = mb_strtolower($command);
if (array_key_exists($command_name, $commands)) {
continue;
}
require_once $file->getPathname();
$command_obj = $this->getMethoddObject($command, $file->getPathname());
if ($command_obj instanceof Method)
{
$commands[$command_name] = $command_obj;
}
}
} catch (Exception $e) {
throw new IOException('Error getting commands from path: ' . $path, $e);
}
array_unshift($this->commands_paths, $path);
}
else
{
$this->commands_paths[] = $path;
}
}
}
return $commands;
/**
* Add multiple custom commands paths
*
* @param array $paths Custom commands paths to add
* @param bool $before If the paths should be prepended or appended to the list
* @throws IOException
*/
public function addCommandsPaths(array $paths, bool $before=true)
{
foreach ($paths as $path)
{
$this->addCommandsPath($path, $before);
}
}
@ -157,23 +115,25 @@
* @param string $command
* @param string $filepath
*
* @return Method|null
* @return Command|null
*/
public function getMethoddObject(string $command, string $filepath = ''): ?Method
public function getCommandObject(string $command, string $filepath = ''): ?Command
{
if (isset($this->commands_objects[$command])) {
if (isset($this->commands_objects[$command]))
{
return $this->commands_objects[$command];
}
foreach ($which as $auth) {
$which = [Command::AUTH_SYSTEM];
$which[] = Command::AUTH_USER;
foreach ($which as $auth)
{
$command_class = $this->getCommandClassName($auth, $command, $filepath);
if ($command_class) {
$command_obj = new $command_class($this, $this->update);
if ($auth === Command::AUTH_SYSTEM && $command_obj instanceof SystemCommand) {
return $command_obj;
}
if ($command_class)
{
return new $command_class();
}
}
@ -194,59 +154,7 @@
public function getCommandClassName(string $auth, string $command, string $filepath = ''): ?string
{
$command = mb_strtolower($command);
$auth = $this->ucFirstUnicode($auth);
// First, check for directly assigned command class.
if ($command_class = $this->command_classes[$auth][$command] ?? null) {
return $command_class;
}
// Start with default namespace.
$command_namespace = __NAMESPACE__ . '\\Commands\\' . $auth . 'Commands';
// Check if we can get the namespace from the file (if passed).
if ($filepath && !($command_namespace = $this->getFileNamespace($filepath))) {
return null;
}
$command_class = $command_namespace . '\\' . $this->ucFirstUnicode($command) . 'Command';
if (class_exists($command_class)) {
return $command_class;
}
return null;
}
/**
* Get namespace from php file by src path
*
* @param string $src (absolute path to file)
* @return string|null
*/
protected function getFileNamespace(string $src): ?string
{
$content = file_get_contents($src);
if (preg_match('#^\s*namespace\s+(.+?);#m', $content, $m)) {
return $m[1];
}
return null;
}
/**
* Get classname of predefined commands
*
* @see command_classes
* @param string $auth Auth of command
* @param string $command Command name
* @param string $filepath Path to the command file
* @return string|null
*/
public function getMethodClassName(string $auth, string $command, string $filepath = ''): ?string
{
$command = mb_strtolower($command);
$auth = $this->ucFirstUnicode($auth);
$auth = Converter::ucFirstUnicode($auth);
// First, check for directly assigned command class.
if ($command_class = $this->command_classes[$auth][$command] ?? null)
@ -258,12 +166,12 @@
$command_namespace = __NAMESPACE__ . '\\Commands\\' . $auth . 'Commands';
// Check if we can get the namespace from the file (if passed).
if ($filepath && !($command_namespace = $this->getFileNamespace($filepath)))
if ($filepath && !($command_namespace = Converter::getFileNamespace($filepath)))
{
return null;
}
$command_class = $command_namespace . '\\' . $this->ucFirstUnicode($command) . 'Command';
$command_class = $command_namespace . '\\' . Converter::ucFirstUnicode($command) . 'Command';
if (class_exists($command_class))
{
@ -272,6 +180,4 @@
return null;
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace KimchiAPI\Objects;
class Configuration
{
public $Name;
public $Version;
public $Author;
public $Organization;
}

View File

@ -0,0 +1,31 @@
<?php
namespace KimchiAPI\Objects\Configuration;
class ServerConfiguration
{
/**
* Indicates if logging is enabled or not
*
* @var bool
*/
public $LoggingEnabled;
/**
* The root path of the API for routing purposes.
*
* @var string
*/
public $RootPath;
/**
*
*
* @var bool
*/
public $FrameworkSignature;
public $ApiSignature;
public $Headers;
}

View File

@ -37,4 +37,59 @@
{
return (strlen($input) > $length) ? substr($input,0, $length).'...' : $input;
}
/**
* Sanitize Method
*
* @param string $command
* @return string
*/
public static function sanitizeMethod(string $command): string
{
return str_replace(' ', '', self::ucWordsUnicode(str_replace('_', ' ', $command)));
}
/**
* Replace function `ucwords` for UTF-8 characters in the class definition and commands
*
* @param string $str
* @param string $encoding (default = 'UTF-8')
*
* @return string
*/
public static function ucWordsUnicode(string $str, string $encoding = 'UTF-8'): string
{
return mb_convert_case($str, MB_CASE_TITLE, $encoding);
}
/**
* Replace function `ucfirst` for UTF-8 characters in the class definition and commands
*
* @param string $str
* @param string $encoding (default = 'UTF-8')
*
* @return string
*/
public static function ucFirstUnicode(string $str, string $encoding = 'UTF-8'): string
{
return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
. mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
}
/**
* Get namespace from php file by src path
*
* @param string $src (absolute path to file)
* @return string|null
*/
public static function getFileNamespace(string $src): ?string
{
$content = file_get_contents($src);
if (preg_match('#^\s*namespace\s+(.+?);#m', $content, $m))
{
return $m[1];
}
return null;
}
}

View File

@ -16,22 +16,22 @@
}
},
"components": [
{
"required": true,
"file": "Abstracts/Command.php"
},
{
"required": true,
"file": "Abstracts/RequestMethod.php"
},
{
"required": true,
"file": "KimchiAPI.php"
},
{
"required": true,
"file": "Exceptions/MissingComponentsException.php"
},
{
"required": true,
"file": "Exceptions/InternalServerException.php"
},
{
"required": true,
"file": "Exceptions/IOException.php"
},
{
"required": true,
"file": "Exceptions/MethodAlreadyRegisteredException.php"
@ -42,7 +42,7 @@
},
{
"required": true,
"file": "Utilities/Converter.php"
"file": "Exceptions/MissingComponentsException.php"
},
{
"required": true,
@ -50,11 +50,27 @@
},
{
"required": true,
"file": "Objects/Response.php"
"file": "KimchiAPI.php"
},
{
"required": true,
"file": "Objects/Configuration/ServerConfiguration.php"
},
{
"required": true,
"file": "Objects/Configuration.php"
},
{
"required": true,
"file": "Objects/Request.php"
},
{
"required": true,
"file": "Objects/Response.php"
},
{
"required": true,
"file": "Utilities/Converter.php"
}
],
"files": [