Skip to content

Commit

Permalink
Container and Router refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
thalysmarciobn committed May 19, 2024
1 parent dd963b7 commit fabc245
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 64 deletions.
96 changes: 70 additions & 26 deletions src/Aurora/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,21 @@

namespace AuroraLumina;

use AuroraLumina\Interface\ServiceInterface;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use RuntimeException;
use Psr\Log\LoggerInterface;
use Psr\Container\ContainerInterface;
use AuroraLumina\Interface\ServiceInterface;

class Container implements ContainerInterface
{
/**
* The container records.
*
* @var array<ServiceInterface>
* @var array<string|ServiceInterface>
*/
protected $instances = [];

/**
* Logger instance.
*
* @var LoggerInterface
*/
protected LoggerInterface $logger;

/**
* Constructor that accepts multiple instances of ServiceInterface.
*
Expand All @@ -42,49 +37,98 @@ public function __construct(ServiceInterface ...$services)
/**
* Get an instance from an id
*
* @param string $id
* @return ServiceInterface
* @param string $service
* @return string|ServiceInterface
*
* @throws \Exception
*/
public function get(string $id): ServiceInterface
public function get(string $service): string|ServiceInterface
{
if (!$this->has($id))
if (!$this->has($service))
{
throw new \Exception("Container has not found.");
}

return $this->instances[$id];
return $this->instances[$service];
}

/**
* Check if you have an instance.
*
* @param string $id
* @param string $service
* @return void
*/
public function has(string $id): bool
public function has(string $service): bool
{
return array_key_exists($id, $this->instances);
return array_key_exists($service, $this->instances);
}

/**
* Binds a service to the container in a scoped manner.
*
* @param string $service The name of the service to bind.
*
* @return void
*
* @throws \Exception If the service is already bound in the container.
*/
public function bindScoped(string $service): void
{
if ($this->has($service))
{
throw new \Exception("Service already bound in the container.");
}

// For a scoped binding, simply store the service name itself.
$this->instances[$service] = $service;
}

/**
* Bind an instance from an id
* Bind an instance from an service
*
* @param ServiceInterface $id
* @param ServiceInterface $service
* @return void
*
* @throws \Exception
* @throws RuntimeException
*/
public function bind(ServiceInterface $service): void
public function bind(string|ServiceInterface $service): void
{
$class = get_class($service);
if (is_string($service))
{
$reflectionClass = new ReflectionClass($service);
$instance = $reflectionClass->newInstance();

if ($this->has($class))
$this->validateService($instance);

$this->instances[$service] = $instance;
}

if ($service instanceof ServiceInterface)
{
throw new \Exception("Instance has not found.");
$class = get_class($service);

if ($this->has($class))
{
throw new RuntimeException("Service already bound in the container.");
}
$this->instances[$class] = $service;
}
}

$this->instances[$class] = $service;
/**
* Validates a service instance to ensure it implements the ServiceInterface.
*
* @param mixed $instance The service instance to validate.
*
* @return void
*
* @throws \RuntimeException If the service instance does not implement ServiceInterface.
*/
public function validateService(mixed $instance): void
{
if (!$instance instanceof ServiceInterface)
{
throw new RuntimeException("Invalid service. Expected instance of ServiceInterface.");
}
}
}
2 changes: 0 additions & 2 deletions src/Aurora/Factory/ApplicationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use AuroraLumina\Container;
use AuroraLumina\Application;
use AuroraLumina\Logger\Logger;
use AuroraLumina\Request\RouterRequest;
use AuroraLumina\Middleware\MiddlewareDispatcher;
use AuroraLumina\Interface\RouterRequestInterface;
Expand Down Expand Up @@ -43,7 +42,6 @@ class ApplicationFactory
public static function createApplication(?Container $container = null): Application
{
static::$container = $container ?? static::$container ?? new Container();
static::$container->bind(new Logger());
static::$router = new RouterRequest(static::$container);
static::$middlewareDispatcher = new MiddlewareDispatcher(static::$router);
return new Application(
Expand Down
2 changes: 1 addition & 1 deletion src/Aurora/Http/Response/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public function withStatus(int $code, string $reasonPhrase = ''): Response
*
* @param int $code The HTTP status code to set.
* @param string $reasonPhrase The reason phrase associated with the status code.
* @throws Exception\InvalidArgumentException If the provided status code is invalid.
* @throws \InvalidArgumentException If the provided status code is invalid.
*/
private function setStatusCode(int $code, string $reasonPhrase = ''): void
{
Expand Down
3 changes: 2 additions & 1 deletion src/Aurora/Interface/ControllerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

namespace AuroraLumina\Interface;

use AuroraLumina\Http\Response\Response;

interface ControllerInterface
{

}
105 changes: 71 additions & 34 deletions src/Aurora/Request/RouterRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
use AuroraLumina\Container;
use AuroraLumina\Routing\Route;
use Psr\Http\Message\ResponseInterface;
use AuroraLumina\Http\Response\Response;
use AuroraLumina\Controller\BaseController;
use AuroraLumina\Interface\ServiceInterface;
use Psr\Http\Message\ServerRequestInterface;
use AuroraLumina\Http\Response\EmptyResponse;
use AuroraLumina\Http\Response\Response;
use AuroraLumina\Interface\ControllerInterface;
use AuroraLumina\Interface\RouterRequestInterface;
use Psr\Http\Message\ServerRequestInterface as Request;

Expand Down Expand Up @@ -86,17 +88,29 @@ protected function resolveDependency(\ReflectionParameter $param): ServiceInterf
throw new \RuntimeException("Dependency not found in the container.");
}

return $this->container->get($name);
$service = $this->container->get($name);

if (is_string($service))
{
$reflectionClass = new ReflectionClass($service);
$instance = $reflectionClass->newInstance();

$this->container->validateService($instance);

return $instance;
}

return $service;
}

/**
* Instantiate a controller class with its dependencies injected.
*
* @param string $class The controller class name.
* @return object|null The instantiated controller.
* @return mixed The instantiated controller.
* @throws \RuntimeException If the controller cannot be instantiated.
*/
protected function instantiateController(string $class): ?object
protected function instantiateController(string $class): mixed
{
$reflectionClass = new ReflectionClass($class);

Expand All @@ -114,30 +128,61 @@ protected function instantiateController(string $class): ?object
}

/**
* Validate if the method exists and is public in the given controller class.
* Validates a controller instance.
*
* @param object $controller The controller object.
* @param string $method The method name.
* @param string $class The class name.
* @return void
* @throws \RuntimeException If the method doesn't exist or is not public.
* @param mixed $controller The controller instance to validate.
*
* @throws \RuntimeException If the controller could not be instantiated or if it does not implement ControllerInterface.
*/
private function validateMethod($controller, string $method, string $class): void
protected function validateController(mixed $controller): void
{
// Method existence check
if (!method_exists($controller, $method))
if (!$controller)
{
throw new \RuntimeException("Method not found in controller class.");
throw new \RuntimeException("Controller could not be instantiated.");
}

// Method visibility check
$reflectionMethod = new \ReflectionMethod($controller, $method);
if (!$reflectionMethod->isPublic())
if (!$controller instanceof ControllerInterface)
{
throw new \RuntimeException("Invalid controller. Expected instance of ControllerInterface.");
}
}

/**
* Validates the visibility of a method in a controller class.
*
* @param ControllerInterface $controller The controller instance.
* @param string $method The name of the method to validate.
*
* @throws \RuntimeException If the method is not found in the controller class or if it is not public.
*/
protected function validateMethod(ControllerInterface $controller, string $method): void
{
$reflectionMethod = $this->getReflectionMethod($controller, $method);

if (!$reflectionMethod->isPublic()) {
throw new \RuntimeException("Method in controller class is not public.");
}
}

/**
* Retrieves a reflection of the specified method in the controller class.
*
* @param ControllerInterface $controller The controller instance.
* @param string $method The name of the method to retrieve.
*
* @return \ReflectionMethod The reflection of the specified method.
*
* @throws \RuntimeException If the method is not found in the controller class.
*/
private function getReflectionMethod(ControllerInterface $controller, string $method): \ReflectionMethod
{
if (!method_exists($controller, $method)) {
throw new \RuntimeException("Method not found in controller class.");
}

return new \ReflectionMethod($controller, $method);
}

/**
* Call the specified method on the controller object.
*
Expand All @@ -159,24 +204,18 @@ private function callControllerMethod($controller, string $method, Request $requ
*/
protected function buildCallback(mixed $action): callable
{
return function (Request $request) use ($action)
{
if ($action instanceof \Closure)
{
return function (Request $request) use ($action) {
if ($action instanceof \Closure) {
return $action($request);
}

if (is_array($action))
{
if (is_array($action)) {
[$class, $method] = $action;

$controller = $this->instantiateController($class);
if (!$controller)
{
throw new \RuntimeException("Controller could not be instantiated.");
}

$this->validateMethod($controller, $method, $class);
$this->validateController($controller);
$this->validateMethod($controller, $method);

return $this->callControllerMethod($controller, $method, $request);
}
Expand Down Expand Up @@ -213,21 +252,19 @@ protected function findRoute(string $method, string $path): ?Route
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$method = $request->getMethod();
$path = $request->getUri()->getPath();
$route = $this->findRoute($request->getMethod(), $request->getUri()->getPath());

if ($route = $this->findRoute($method, $path))
if ($route)
{
$buildCallback = $this->buildCallback($route->getAction());

$callback = $buildCallback($request);
$callback = $this->buildCallback($route->getAction())($request);

if (is_string($callback))
{
$response = new Response();
$response->getBody()->write($callback);
return $response;
}

if ($callback instanceof ResponseInterface)
{
return $callback;
Expand Down

0 comments on commit fabc245

Please sign in to comment.