mc-cms-namelessmc/core/classes/Endpoints/Endpoints.php
2025-05-10 17:43:44 -07:00

125 lines
4.3 KiB
PHP

<?php
use Symfony\Component\HttpFoundation\Response;
/**
* Endpoint management class.
*
* @package NamelessMC\Endpoints
* @author Aberdeener
* @version 2.0.0-pr13
* @license MIT
*/
class Endpoints
{
use MatchesRoutes;
use ManagesTransformers;
/** @var EndpointBase[] */
private iterable $_endpoints = [];
/**
* Get all registered Endpoints.
*
* @return EndpointBase[] All endpoints.
*/
public function getAll(): iterable
{
return $this->_endpoints;
}
/**
* Find an endpoint which matches this request and `execute()` it.
*
* @param string $route Route to find endpoint for.
* @param string $method HTTP method to find endpoint for.
* @param Nameless2API $api Instance of api instance to provide the endpoint.
*/
public function handle(string $route, string $method, Nameless2API $api): void
{
$available_methods = [];
$matched_endpoint = null;
foreach ($this->getAll() as $endpoint) {
if (($vars = $this->matchRoute($endpoint, $route)) !== false) {
// Save that we actually found an endpoint
$matched_endpoint = $endpoint;
// Save the methods that are available for this endpoint
$available_methods[] = $endpoint->getMethod();
if ($endpoint->getMethod() === $method) {
if (!$endpoint->isAuthorised($api)) {
$api->throwError(
$endpoint->getAuthType() === EndpointBase::AUTH_TYPE_API_KEY
? Nameless2API::ERROR_INVALID_API_KEY
: Nameless2API::ERROR_NOT_AUTHORIZED,
null,
Response::HTTP_UNAUTHORIZED
);
}
if (!method_exists($endpoint, 'execute')) {
throw new InvalidArgumentException("Endpoint class must contain an 'execute()' method.");
}
$reflection = new ReflectionMethod($endpoint, 'execute');
if ($reflection->getNumberOfParameters() !== (count($endpoint->customParams()) + count($vars) + 1)) {
throw new InvalidArgumentException("Endpoint's 'execute()' method must take " . (count($endpoint->customParams()) + count($vars) + 1) . ' arguments. Endpoint: ' . $endpoint->getRoute());
}
$endpoint->execute(
$api,
...array_merge($endpoint->customParams(), array_map(function ($type, $value) use ($api) {
return $this::transform($api, $type, $value);
}, array_keys($vars), $vars))
);
return;
}
}
}
if ($matched_endpoint !== null) {
$api->throwError(Nameless2API::ERROR_INVALID_API_METHOD, "The $route endpoint only accepts " . implode(', ', $available_methods) . ", $method was used.", Response::HTTP_METHOD_NOT_ALLOWED);
}
$api->throwError(Nameless2API::ERROR_INVALID_API_METHOD, 'If you are seeing this while in a browser, this means your API is functioning!', Response::HTTP_NOT_FOUND);
}
/**
* Recursively scan, preload and register EndpointBase classes in a folder.
*
* @see EndpointBase
*
* @param string $path Path to scan from.
*/
public function loadEndpoints(string $path): void
{
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS));
foreach ($rii as $file) {
if ($file->isDir()) {
$this->loadEndpoints($file);
return;
}
if ($file->getFilename() === '.DS_Store') {
continue;
}
require_once($file->getPathName());
$endpoint_class_name = str_replace('.php', '', $file->getFilename());
/** @var EndpointBase $endpoint */
$endpoint = new $endpoint_class_name();
$key = $endpoint->getRoute() . '-' . $endpoint->getMethod();
if (!isset($this->_endpoints[$key])) {
$this->_endpoints[$key] = $endpoint;
}
}
}
}