diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..55940e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +composer.lock \ No newline at end of file diff --git a/src/Auth/ApiAuthenticator.php b/src/Auth/ApiAuthenticator.php index c4749ee..7cd25e1 100644 --- a/src/Auth/ApiAuthenticator.php +++ b/src/Auth/ApiAuthenticator.php @@ -12,12 +12,18 @@ class ApiAuthenticator implements ContextAware, WithContext use WithContextTrait; use ContextAwareTrait; + /** @return bool */ public function isAuthenticated(): bool { return false; } - public function hasPermission(ApiPermission ...$permission) + /** + * @param ApiPermission ...$permission + * + * @return bool + */ + public function hasPermission(ApiPermission ...$permission): bool { return false; } diff --git a/src/Controller/ApiController.php b/src/Controller/ApiController.php index 9c872f6..0cd16f7 100644 --- a/src/Controller/ApiController.php +++ b/src/Controller/ApiController.php @@ -4,23 +4,39 @@ use Cubex\ApiFoundation\Auth\ApiAuthenticator; use Cubex\ApiFoundation\Module\Module; use Cubex\ApiFoundation\Routing\ModuleRoute; +use Generator; use Packaged\Context\Context; use Packaged\Http\Responses\JsonResponse; +use Packaged\Routing\Handler\FuncHandler; +use Packaged\Routing\Handler\Handler; +use Packaged\Routing\Route; abstract class ApiController extends ApiFoundationController { const VERSION = '1.0.0'; - public function getVersion() + /** @return JsonResponse */ + public function getVersion(): JsonResponse { return JsonResponse::create(static::VERSION); } - public function getAuthenticator(Context $context) + /** + * @param Context $context + * + * @return ApiAuthenticator + */ + public function getAuthenticator(Context $context): ApiAuthenticator { return ApiAuthenticator::withContext($context); } + /** + * @param Context $c + * @param $handler + * + * @return array|callable|\Closure|mixed|FuncHandler|Handler|string + */ protected function _prepareHandler(Context $c, $handler) { if($handler instanceof ApiModuleController) @@ -30,16 +46,15 @@ protected function _prepareHandler(Context $c, $handler) return parent::_prepareHandler($c, $handler); } - /** - * @return \Generator|Module[] - */ + /** @return Generator|Array */ abstract protected function _yieldModules(); + /** @return Generator */ protected function _generateRoutes() { foreach($this->_yieldModules() as $module) { - yield ModuleRoute::withModule($module); + yield new ModuleRoute($module); } yield self::_route('version', 'version'); } diff --git a/src/Controller/ApiFoundationController.php b/src/Controller/ApiFoundationController.php index 4becfe0..53ad51a 100644 --- a/src/Controller/ApiFoundationController.php +++ b/src/Controller/ApiFoundationController.php @@ -3,10 +3,18 @@ use Cubex\Controller\Controller; use Packaged\Context\Context; +use Packaged\Http\Response; use Packaged\Http\Responses\JsonResponse; abstract class ApiFoundationController extends Controller { + /** + * @param Context $c + * @param $result + * @param $buffer + * + * @return mixed|Response + */ protected function _prepareResponse(Context $c, $result, $buffer = null) { if(is_array($result) || is_scalar($result)) diff --git a/src/Controller/ApiModuleController.php b/src/Controller/ApiModuleController.php index fca4302..4ef7854 100644 --- a/src/Controller/ApiModuleController.php +++ b/src/Controller/ApiModuleController.php @@ -2,22 +2,19 @@ namespace Cubex\ApiFoundation\Controller; use Cubex\ApiFoundation\Auth\ApiAuthenticator; -use Cubex\ApiFoundation\Module\Module; +use Cubex\ApiFoundation\Module\Procedures\ProcedureRoute; +use Generator; class ApiModuleController extends ApiFoundationController { - /** - * @var Module - */ - protected $_module; + /** @var ApiAuthenticator */ + protected ApiAuthenticator $_authenticator; - public function __construct(Module $module) + /** @param Array|Generator $_routes */ + public function __construct(protected array|Generator $_routes) { - $this->_module = $module; } - protected $_authenticator; - /** * @param ApiAuthenticator $authenticator * @@ -29,12 +26,14 @@ public function setAuthenticator(ApiAuthenticator $authenticator) return $this; } + /** + * @return Generator + */ protected function _generateRoutes() { - foreach($this->_module->getRoutes() as $route) + foreach($this->_routes as $route) { yield $route->setAuthenticator($this->_authenticator); } } - } diff --git a/src/Exceptions/InvalidPayloadException.php b/src/Exceptions/InvalidPayloadException.php index 9877c24..67d1a19 100644 --- a/src/Exceptions/InvalidPayloadException.php +++ b/src/Exceptions/InvalidPayloadException.php @@ -2,15 +2,36 @@ namespace Cubex\ApiFoundation\Exceptions; +use Throwable; + class InvalidPayloadException extends \Exception { + /** @var string */ protected $message = 'Invalid payload'; + + /** @var int */ protected $code = 400; - public static function withType($type): static + /** + * @param string $type + * @param string $message + * @param int $code + * @param Throwable|null $previous + */ + public function __construct(string $type, string $message = "", int $code = 0, ?Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + $this->message .= ' ' . $type; + } + + /** + * @deprecated Use the constructor instead + * @param string $type + * + * @return static + */ + public static function withType(string $type) { - $ex = new static(); - $ex->message .= ' ' . $type; - return $ex; + return new static($type); } } diff --git a/src/Module/Procedures/ProcedureRoute.php b/src/Module/Procedures/ProcedureRoute.php index 0ee815e..7608aac 100644 --- a/src/Module/Procedures/ProcedureRoute.php +++ b/src/Module/Procedures/ProcedureRoute.php @@ -5,27 +5,29 @@ use Cubex\ApiFoundation\Exceptions\InvalidPayloadException; use Cubex\ApiTransport\Endpoints\ApiEndpoint; use Cubex\ApiTransport\Payloads\AbstractPayload; +use Exception; use Packaged\Context\Context; use Packaged\Context\ContextAware; use Packaged\Http\Responses\JsonResponse; use Packaged\Routing\Handler\Handler; use Packaged\Routing\RequestCondition; use Packaged\Routing\Route; +use ReflectionClass; +use ReflectionException; use Symfony\Component\HttpFoundation\Response; class ProcedureRoute extends Route implements Handler { - /** @var */ - protected $_endpoint; - /** @var string */ - protected $_procedureClass; - - public function __construct(ApiEndpoint $endpoint, string $procedureClass) + /** + * @param ApiEndpoint $_endpoint + * @param class-string $_procedureClass + */ + public function __construct(protected ApiEndpoint $_endpoint, protected string $_procedureClass) { - $this->_endpoint = $endpoint; - $this->_procedureClass = $procedureClass; $this->add( - RequestCondition::i()->path($endpoint->getPath(), RequestCondition::TYPE_EXACT)->method($endpoint->getVerb()) + RequestCondition::i() + ->path($this->_endpoint->getPath(), RequestCondition::TYPE_EXACT) + ->method($this->_endpoint->getVerb()) ); } @@ -45,6 +47,11 @@ public function setAuthenticator(ApiAuthenticator $authenticator) return $this; } + /** + * @param Context $context + * + * @return bool + */ public function match(Context $context): bool { if(parent::match($context)) @@ -68,20 +75,21 @@ public function match(Context $context): bool return false; } + /** @return $this */ public function getHandler() { return $this; } /** - * @throws InvalidPayloadException + * @throws InvalidPayloadException|ReflectionException|Exception */ public function handle(Context $c): Response { $procedure = new $this->_procedureClass(); if(!($procedure instanceof Procedure)) { - throw new \Exception("Invalid procedure class given"); + throw new Exception("Invalid procedure class given"); } if($procedure instanceof ContextAware) @@ -89,32 +97,30 @@ public function handle(Context $c): Response $procedure->setContext($c); } - if(method_exists($procedure, 'execute')) + if(!method_exists($procedure, 'execute')) { - $plClass = $this->_endpoint->getPayloadClass(); - if($plClass !== null) - { - $payload = new $plClass(); - if($payload instanceof AbstractPayload) - { - $payload->fromContext($c); - } + return \Packaged\Http\Response::create("Unable to handle procedure: execute missing", 404); + } - if(!$payload->isValid()) - { - throw InvalidPayloadException::withType((new \ReflectionClass($payload))->getShortName()); - } + $plClass = $this->_endpoint->getPayloadClass(); - //Execute with payload - $response = $procedure->execute($payload); - } - else - { - //Execute without payload - $response = $procedure->execute(); - } - return JsonResponse::create($response); + if (!$plClass) + { + return JsonResponse::create($procedure->execute()); } - return \Packaged\Http\Response::create("Unable to handle procedure: execute missing", 404); + + $payload = new $plClass(); + if($payload instanceof AbstractPayload) + { + $payload->fromContext($c); + } + + if(!$payload->isValid()) + { + throw new InvalidPayloadException((new ReflectionClass($payload))->getShortName()); + } + + //Execute with payload + return JsonResponse::create($procedure->execute($payload)); } } diff --git a/src/Routing/ModuleCondition.php b/src/Routing/ModuleCondition.php index d2a5038..ed41232 100644 --- a/src/Routing/ModuleCondition.php +++ b/src/Routing/ModuleCondition.php @@ -7,19 +7,33 @@ class ModuleCondition extends RequestCondition { - private $_module; + /** + * @param class-string $_moduleClassName + * @param string $moduleBasePath + */ + public function __construct(private string $_moduleClassName, string $moduleBasePath) + { + $this->path($moduleBasePath); + } + /** + * @deprecated Use the constructor instead + * @param Module $module + * + * @return static + */ public static function withModule(Module $module) { - $condition = new static(); - $condition->_module = $module; - $condition->path($module::getBasePath()); - return $condition; + return new static(get_class($module), $module::getBasePath()); } + /** + * @param Context $context + * + * @return void + */ public function complete(Context $context) { - $context->meta()->set('api.module', get_class($this->_module)); + $context->meta()->set('api.module', $this->_moduleClassName); } - } diff --git a/src/Routing/ModuleRoute.php b/src/Routing/ModuleRoute.php index b303415..a19dbea 100644 --- a/src/Routing/ModuleRoute.php +++ b/src/Routing/ModuleRoute.php @@ -7,18 +7,26 @@ class ModuleRoute extends Route { - protected $_module; + /** @param Module $_module */ + public function __construct(protected Module $_module) + { + $this->add(new ModuleCondition(get_class($this->_module), $this->_module::getBasePath())); + } + /** + * @deprecated Use the constructor instead + * @param Module $module + * + * @return static + */ public static function withModule(Module $module) { - $route = new static(); - $route->_module = $module; - $route->add(ModuleCondition::withModule($module)); - return $route; + return new static($module); } + /** @return ApiModuleController */ public function getHandler() { - return new ApiModuleController($this->_module); + return new ApiModuleController($this->_module->getRoutes()); } }