From 8c527998b59b88c395e375709424e87f797c6517 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 01:13:22 +0800 Subject: [PATCH 01/46] Change "Middleware" to "Server" --- src/Application.php | 159 +-------------- src/Application/Application.php | 6 +- src/Container/Container.php | 139 +------------- src/Debug/ErrorHandlerIntegration.php | 6 +- src/Middleware/MiddlewareIntegration.php | 50 +---- src/Routing/Route.php | 10 +- src/Routing/RouteInterface.php | 2 +- src/Server/CallableWrapper.php | 66 +++++++ src/Server/Dispatch.php | 53 +++++ src/Server/Doublepass.php | 25 +++ src/Server/Handler.php | 72 +++++++ src/Server/HandlerInterface.php | 10 + src/Server/Handlers/Handler030.php | 32 ++++ src/Server/Handlers/Handler041.php | 32 ++++ src/Server/Handlers/Handler050.php | 32 ++++ src/Server/Handlers/Handler100.php | 33 ++++ src/Server/MiddlewareInterface.php | 14 ++ src/Server/Version.php | 41 ++++ src/System.php | 181 ++++++++++++++++++ src/System/Handler.php | 144 ++++++++++++++ src/System/Resolver.php | 176 +++++++++++++++++ tests/Application/ApplicationTest.php | 22 ++- tests/Application/ApplicationTestCases.php | 4 +- tests/Application/AurynContainerTest.php | 3 +- tests/Fixture/Middlewares/EmptyMiddleware.php | 15 +- tests/Fixture/Middlewares/FirstMiddleware.php | 17 +- tests/Fixture/Middlewares/LastMiddleware.php | 21 +- .../Fixture/Middlewares/SecondMiddleware.php | 17 +- tests/Sample/Router.php | 3 +- 29 files changed, 981 insertions(+), 404 deletions(-) create mode 100644 src/Server/CallableWrapper.php create mode 100644 src/Server/Dispatch.php create mode 100644 src/Server/Doublepass.php create mode 100644 src/Server/Handler.php create mode 100644 src/Server/HandlerInterface.php create mode 100644 src/Server/Handlers/Handler030.php create mode 100644 src/Server/Handlers/Handler041.php create mode 100644 src/Server/Handlers/Handler050.php create mode 100644 src/Server/Handlers/Handler100.php create mode 100644 src/Server/MiddlewareInterface.php create mode 100644 src/Server/Version.php create mode 100644 src/System.php create mode 100644 src/System/Handler.php create mode 100644 src/System/Resolver.php diff --git a/src/Application.php b/src/Application.php index 212b1165..ff913ce8 100644 --- a/src/Application.php +++ b/src/Application.php @@ -2,170 +2,15 @@ namespace Rougin\Slytherin; -use Psr\Container\ContainerInterface; -use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Application\CallbackHandler; -use Rougin\Slytherin\Container\Container; -use Rougin\Slytherin\Integration\ConfigurationInterface; -use Rougin\Slytherin\Middleware\Delegate; - /** * Application * * Integrates all specified components into the application. + * NOTE: To be removed in v1.0.0. Use \System instead. * * @package Slytherin * @author Rougin Gutib */ -class Application +class Application extends System { - // NOTE: To be removed in v1.0.0 ------------------------------------ - const ERROR_HANDLER = 'Rougin\Slytherin\Debug\ErrorHandlerInterface'; - // ------------------------------------------------------------------ - - const MIDDLEWARE = 'Interop\Http\ServerMiddleware\MiddlewareInterface'; - - const RENDERER = 'Rougin\Slytherin\Template\RendererInterface'; - - const ROUTER = 'Rougin\Slytherin\Routing\RouterInterface'; - - const SERVER_REQUEST = 'Psr\Http\Message\ServerRequestInterface'; - - /** - * @var \Rougin\Slytherin\Integration\Configuration - */ - protected $config; - - /** - * @var \Rougin\Slytherin\Container\ContainerInterface - */ - protected static $container; - - /** - * Initializes the application instance. - * - * @param \Rougin\Slytherin\Container\ContainerInterface|null $container - * @param \Rougin\Slytherin\Integration\Configuration|null $config - */ - public function __construct(ContainerInterface $container = null, ConfigurationInterface $config = null) - { - if (! $config) $config = new Configuration; - - $this->config = $config; - - if (! $container) $container = new Container; - - static::$container = $container; - } - - /** - * Returns the static instance of the specified container. - * - * @return \Rougin\Slytherin\Container\ContainerInterface - */ - public static function container() - { - return static::$container; - } - - /** - * Handles the ServerRequestInterface to convert it to a ResponseInterface. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function handle(ServerRequestInterface $request) - { - $callback = new CallbackHandler(self::$container); - - $hasMiddleware = static::$container->has(self::MIDDLEWARE); - - if (! $hasMiddleware) return $callback($request); - - /** @var \Interop\Http\ServerMiddleware\MiddlewareInterface */ - $middleware = static::$container->get(self::MIDDLEWARE); - - $delegate = new Delegate($callback); - - return $middleware->process($request, $delegate); - } - - /** - * Adds the specified integrations to the container. - * - * @param \Rougin\Slytherin\Integration\IntegrationInterface[]|string[]|string $integrations - * @param \Rougin\Slytherin\Integration\Configuration|null $config - * @return self - */ - public function integrate($integrations, ConfigurationInterface $config = null) - { - if (! $config) $config = $this->config; - - $container = static::$container; - - foreach ((array) $integrations as $item) - { - /** @var \Rougin\Slytherin\Integration\IntegrationInterface */ - $integration = is_string($item) ? new $item : $item; - - $container = $integration->define($container, $config); - } - - static::$container = $container; - - return $this; - } - - /** - * Emits the headers from response and runs the application. - * - * @return void - */ - public function run() - { - // NOTE: To be removed in v1.0.0. Use "ErrorHandlerIntegration" instead. - if (static::$container->has(self::ERROR_HANDLER)) - { - /** @var \Rougin\Slytherin\Debug\ErrorHandlerInterface */ - $debugger = static::$container->get(self::ERROR_HANDLER); - - $debugger->display(); - } - - /** @var \Psr\Http\Message\ServerRequestInterface */ - $request = static::$container->get(self::SERVER_REQUEST); - - echo (string) $this->emit($request)->getBody(); - } - - /** - * Emits the headers based from the response. - * NOTE: To be removed in v1.0.0. Should be included in run(). - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - protected function emit(ServerRequestInterface $request) - { - $response = $this->handle($request); - - $code = (string) $response->getStatusCode(); - - $code .= ' ' . $response->getReasonPhrase(); - - $headers = (array) $response->getHeaders(); - - $version = $response->getProtocolVersion(); - - header('HTTP/' . $version . ' ' . $code); - - foreach ($headers as $name => $values) - { - $value = (string) implode(',', $values); - - header((string) $name . ': ' . $value); - } - - return $response; - } } diff --git a/src/Application/Application.php b/src/Application/Application.php index f82ccca0..ddae47bc 100644 --- a/src/Application/Application.php +++ b/src/Application/Application.php @@ -2,15 +2,17 @@ namespace Rougin\Slytherin\Application; +use Rougin\Slytherin\Application as System; + /** * Application * * Integrates all specified components into the application. - * NOTE: To be removed in v1.0.0 + * NOTE: To be removed in v1.0.0. Use \Application instead. * * @package Slytherin * @author Rougin Gutib */ -class Application extends \Rougin\Slytherin\Application +class Application extends System { } diff --git a/src/Container/Container.php b/src/Container/Container.php index b93e80c3..7a6644d6 100644 --- a/src/Container/Container.php +++ b/src/Container/Container.php @@ -5,6 +5,7 @@ use Psr\Container\ContainerInterface as PsrContainerInterface; use Psr\Container\NotFoundExceptionInterface; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\System\Resolver; /** * Container @@ -73,36 +74,6 @@ public function alias($id, $original) return $this; } - /** - * Resolves the specified parameters from a container. - * - * @param \ReflectionFunctionAbstract $reflector - * @param array $parameters - * @return array - */ - public function arguments(\ReflectionFunctionAbstract $reflector, $parameters = array()) - { - $items = $reflector->getParameters(); - - $result = array(); - - foreach ($items as $key => $item) - { - $argument = $this->argument($item); - - $name = (string) $item->getName(); - - if (array_key_exists($name, $parameters)) - { - $result[$key] = $parameters[$name]; - } - - if ($argument) $result[$key] = $argument; - } - - return $result; - } - /** * Finds an entry of the container by its identifier and returns it. * @@ -127,7 +98,9 @@ public function get($id) } else { - $entry = $this->resolve($id); + $resolver = new Resolver($this); + + $entry = $resolver->resolve($id); } if (is_object($entry)) return $entry; @@ -148,37 +121,6 @@ public function has($id) return isset($this->instances[$id]) || $this->extra->has($id); } - /** - * Resolves the specified identifier to an instance. - * - * @throws \Psr\Container\NotFoundExceptionInterface - * - * @param string $id - * @param \Psr\Http\Message\ServerRequestInterface|null $request - * @return mixed - */ - public function resolve($id, ServerRequestInterface $request = null) - { - /** @var class-string $id */ - $reflection = new \ReflectionClass($id); - - if (! $constructor = $reflection->getConstructor()) - { - return $this->extra->get($id); - } - - $result = array(); - - foreach ($constructor->getParameters() as $parameter) - { - $argument = $this->argument($parameter); - - $result[] = $this->handle($argument, $request); - } - - return $reflection->newInstanceArgs($result); - } - /** * Sets a new instance to the container. * @@ -192,77 +134,4 @@ public function set($id, $concrete) return $this; } - - /** - * Returns an argument based on the given parameter. - * - * @param \ReflectionParameter $parameter - * @return mixed|null - */ - protected function argument(\ReflectionParameter $parameter) - { - try - { - $argument = $parameter->getDefaultValue(); - } - catch (\ReflectionException $exception) - { - $param = new Parameter($parameter); - - $class = $param->getClass(); - - $name = $parameter->getName(); - - if (! is_null($class)) $name = $class->getName(); - - $argument = $this->value($name); - } - - return $argument; - } - - /** - * Handles the manipulated ServerRequest (from middleware) to an argument. - * - * @param mixed $argument - * @param \Psr\Http\Message\ServerRequestInterface|null $request - * @return mixed - */ - protected function handle($argument, ServerRequestInterface $request = null) - { - return $argument instanceof ServerRequestInterface && $request ? $request : $argument; - } - - /** - * Returns the value of the specified argument. - * - * @param string $name - * @return mixed|null - */ - protected function value($name) - { - $object = null; - - if (isset($this->instances[$name])) - { - $object = $this->get($name); - } - - if ($object || ! $this->extra->has($name)) - { - return $object; - } - - // If the identifier does not exists from extra, --- - // Try to get again from the parent container - try - { - return $this->extra->get($name); - } - catch (NotFoundExceptionInterface $error) - { - return $this->get($name); - } - // ------------------------------------------------- - } } diff --git a/src/Debug/ErrorHandlerIntegration.php b/src/Debug/ErrorHandlerIntegration.php index ff77fe51..812534ce 100644 --- a/src/Debug/ErrorHandlerIntegration.php +++ b/src/Debug/ErrorHandlerIntegration.php @@ -43,11 +43,7 @@ public function define(ContainerInterface $container, Configuration $config) { ini_set('display_errors', '1'); - error_reporting(E_ALL); - - // NOTE: To be removed in v1.0.0. Use $handler->display() instead. --- - $container->set(Application::ERROR_HANDLER, $handler); - // ------------------------------------------------------------------- + error_reporting(E_ALL); $handler->display(); } return $container; diff --git a/src/Middleware/MiddlewareIntegration.php b/src/Middleware/MiddlewareIntegration.php index 3721ffda..dfae997c 100644 --- a/src/Middleware/MiddlewareIntegration.php +++ b/src/Middleware/MiddlewareIntegration.php @@ -2,12 +2,11 @@ namespace Rougin\Slytherin\Middleware; -use Psr\Http\Message\ResponseInterface; -use Rougin\Slytherin\Application; use Rougin\Slytherin\Container\ContainerInterface; use Rougin\Slytherin\Integration\Configuration; use Rougin\Slytherin\Integration\IntegrationInterface; -use Zend\Stratigility\MiddlewarePipe; +use Rougin\Slytherin\Server\Dispatch; +use Rougin\Slytherin\System; /** * Middleware Integration @@ -33,52 +32,15 @@ class MiddlewareIntegration implements IntegrationInterface */ public function define(ContainerInterface $container, Configuration $config) { - $middleware = Application::MIDDLEWARE; + $middleware = System::MIDDLEWARE; if (! interface_exists($middleware)) return $container; - /** @var \Psr\Http\Message\ResponseInterface */ - $response = $container->get('Psr\Http\Message\ResponseInterface'); - - /** @var array */ + /** @var array */ $stack = $config->get('app.middlewares', array()); - $dispatcher = $this->dispatcher($response, $stack); - - // NOTE: To be removed in v1.0.0. Use Middleware\DispatcherInterface instead. --- - $container->set('Rougin\Slytherin\Middleware\MiddlewareInterface', $dispatcher); - // ------------------------------------------------------------------------------ - - $container->set('Rougin\Slytherin\Middleware\DispatcherInterface', $dispatcher); - $container->set('Interop\Http\ServerMiddleware\MiddlewareInterface', $dispatcher); - - return $container; - } - - /** - * Returns the middleware dispatcher to be used. - * - * @param \Psr\Http\Message\ResponseInterface $response - * @param array $stack - * @return \Rougin\Slytherin\Middleware\DispatcherInterface - */ - protected function dispatcher(ResponseInterface $response, $stack) - { - $dispatcher = new Dispatcher($stack, $response); - - $empty = $this->preferred === null; - - $hasZend = class_exists('Zend\Stratigility\MiddlewarePipe'); - - $wantZend = $this->preferred === 'stratigility'; - - if (($empty || $wantZend) && $hasZend) - { - $pipe = new MiddlewarePipe; - - $dispatcher = new StratigilityDispatcher($pipe, $stack, $response); - } + $dispatch = new Dispatch($stack); - return $dispatcher; + return $container->set($middleware, $dispatch); } } diff --git a/src/Routing/Route.php b/src/Routing/Route.php index c7902e30..6dbe24d9 100644 --- a/src/Routing/Route.php +++ b/src/Routing/Route.php @@ -19,7 +19,7 @@ class Route implements RouteInterface protected $method; /** - * @var \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] + * @var mixed[] */ protected $middlewares; @@ -34,10 +34,10 @@ class Route implements RouteInterface protected $uri; /** - * @param string $method - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[]|string $middlewares + * @param string $method + * @param string $uri + * @param callable|string[]|string $handler + * @param mixed[] $middlewares */ public function __construct($method, $uri, $handler, $middlewares = array()) { diff --git a/src/Routing/RouteInterface.php b/src/Routing/RouteInterface.php index 1342428f..bd181900 100644 --- a/src/Routing/RouteInterface.php +++ b/src/Routing/RouteInterface.php @@ -21,7 +21,7 @@ public function getHandler(); public function getMethod(); /** - * @return \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] + * @return mixed[] */ public function getMiddlewares(); diff --git a/src/Server/CallableWrapper.php b/src/Server/CallableWrapper.php new file mode 100644 index 00000000..5c3d5737 --- /dev/null +++ b/src/Server/CallableWrapper.php @@ -0,0 +1,66 @@ + + * @author Rasmus Schultz + */ +class CallableWrapper +{ + /** + * @var callable + */ + protected $middleware; + + /** + * @var \Psr\Http\Message\ResponseInterface|null + */ + protected $response = null; + + /** + * Initializes the middleware instance. + * + * @param callable $middleware + * @param \Psr\Http\Message\ResponseInterface|null $response + */ + public function __construct($middleware, ResponseInterface $response = null) + { + $this->middleware = $middleware; + + $this->response = $response; + } + + /** + * Processes an incoming server request and return a response. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate + * @return \Psr\Http\Message\ResponseInterface + */ + public function process(ServerRequestInterface $request, DelegateInterface $delegate) + { + $middleware = $this->middleware; + + if (! $this->response instanceof ResponseInterface) + { + return $middleware($request, $delegate); + } + + $fn = function ($request) use ($delegate) + { + return $delegate->process($request); + }; + + return $middleware($request, $this->response, $fn); + } +} diff --git a/src/Server/Dispatch.php b/src/Server/Dispatch.php new file mode 100644 index 00000000..8a3a0556 --- /dev/null +++ b/src/Server/Dispatch.php @@ -0,0 +1,53 @@ +stack = $stack; + } + + public function getStack() + { + return $this->stack; + } + + public function process(ServerRequestInterface $request, HandlerInterface $handler) + { + $stack = array(); + + foreach ($this->stack as $item) + { + array_push($stack, $this->transform($item)); + } + + $handler = new Handler($stack, $handler); + + return $handler->handle($request); + } + + public function setStack($stack) + { + $this->stack = $stack; + + return $this; + } + + protected function transform($middleware) + { + $isClosure = is_a($middleware, 'Closure'); + + if ($isClosure) + { + return new CallableWrapper($middleware); + } + + return new $middleware; + } +} diff --git a/src/Server/Doublepass.php b/src/Server/Doublepass.php new file mode 100644 index 00000000..18d18431 --- /dev/null +++ b/src/Server/Doublepass.php @@ -0,0 +1,25 @@ +handler = $handler; + + $this->response = $response; + } + + public function handle(ServerRequestInterface $request) + { + return call_user_func($this->handler, $request, $this->response); + } +} diff --git a/src/Server/Handler.php b/src/Server/Handler.php new file mode 100644 index 00000000..731a3987 --- /dev/null +++ b/src/Server/Handler.php @@ -0,0 +1,72 @@ +default = $default; + + $this->stack = $stack; + } + + /** + * @param mixed $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request) + { + if (! isset($this->stack[$this->index])) + { + return $this->default->handle($request); + } + + $item = $this->stack[$this->index]; + + $next = $this->next(); + + if (Version::is('0.3.0')) + { + $next = new Handler030($next); + } + + if (Version::is('0.4.1')) + { + $next = new Handler041($next); + } + + if (Version::is('0.5.0')) + { + $next = new Handler050($next); + } + + if (Version::is('1.0.0')) + { + $next = new Handler100($next); + } + + return $item->process($request, $next); + } + + protected function next() + { + $next = clone $this; + + $next->index++; + + return $next; + } +} diff --git a/src/Server/HandlerInterface.php b/src/Server/HandlerInterface.php new file mode 100644 index 00000000..0dc340b8 --- /dev/null +++ b/src/Server/HandlerInterface.php @@ -0,0 +1,10 @@ +handler = $handler; + } + + /** + * @param \Psr\Http\Message\RequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function process(RequestInterface $request) + { + return $this->handler->handle($request); + } +} diff --git a/src/Server/Handlers/Handler041.php b/src/Server/Handlers/Handler041.php new file mode 100644 index 00000000..5019cf0e --- /dev/null +++ b/src/Server/Handlers/Handler041.php @@ -0,0 +1,32 @@ +handler = $handler; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function process(ServerRequestInterface $request) + { + return $this->handler->handle($request); + } +} diff --git a/src/Server/Handlers/Handler050.php b/src/Server/Handlers/Handler050.php new file mode 100644 index 00000000..69e63f1d --- /dev/null +++ b/src/Server/Handlers/Handler050.php @@ -0,0 +1,32 @@ +handler = $handler; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request) + { + return $this->handler->handle($request); + } +} diff --git a/src/Server/Handlers/Handler100.php b/src/Server/Handlers/Handler100.php new file mode 100644 index 00000000..dadc5ac9 --- /dev/null +++ b/src/Server/Handlers/Handler100.php @@ -0,0 +1,33 @@ +handler = $handler; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request): ResponseInterface + { + return $this->handler->handle($request); + } +} diff --git a/src/Server/MiddlewareInterface.php b/src/Server/MiddlewareInterface.php new file mode 100644 index 00000000..4b79e8e4 --- /dev/null +++ b/src/Server/MiddlewareInterface.php @@ -0,0 +1,14 @@ +config = $config; + + if (! $container) $container = new Container; + + $this->container = $container; + } + + /** + * Finds an entry of the container by its identifier and returns it. + * + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\Container\ContainerExceptionInterface + * + * @param string $id + * @return object + */ + public function get($id) + { + return $this->container->get($id); + } + + /** + * Handles the ServerRequestInterface to convert it to a ResponseInterface. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request) + { + $uri = $request->getUri()->getPath(); + + $method = $request->getMethod(); + + /** @var \Rougin\Slytherin\Routing\DispatcherInterface */ + $dispatcher = $this->container->get(self::DISPATCHER); + + if ($this->container->has(self::ROUTER)) + { + /** @var \Rougin\Slytherin\Routing\RouterInterface */ + $router = $this->container->get(self::ROUTER); + + $dispatcher = $dispatcher->setRouter($router); + } + + $route = $dispatcher->dispatch($method, $uri); + + $items = $route->getMiddlewares(); + + $handler = new Handler($route, $this->container); + + if (! $this->container->has(self::MIDDLEWARE)) + { + return $handler->handle($request); + } + + /** @var \Rougin\Slytherin\Server\MiddlewareInterface */ + $middleware = $this->container->get(self::MIDDLEWARE); + + $stack = $middleware->getStack(); + + $middleware->setStack(array_merge($items, $stack)); + + return $middleware->process($request, $handler); + } + + /** + * Adds the specified integrations to the container. + * + * @param \Rougin\Slytherin\Integration\IntegrationInterface[]|string[]|string $integrations + * @param \Rougin\Slytherin\Integration\Configuration|null $config + * @return self + */ + public function integrate($integrations, ConfigurationInterface $config = null) + { + if (! $config) $config = $this->config; + + $container = $this->container; + + foreach ((array) $integrations as $item) + { + /** @var \Rougin\Slytherin\Integration\IntegrationInterface */ + $integration = is_string($item) ? new $item : $item; + + $container = $integration->define($container, $config); + } + + $this->container = $container; + + return $this; + } + + /** + * Emits the headers from response and runs the application. + * + * @return void + */ + public function run() + { + /** @var \Psr\Http\Message\ServerRequestInterface */ + $request = $this->container->get(self::SERVER_REQUEST); + + echo (string) $this->emit($request)->getBody(); + } + + /** + * Emits the headers based from the response. + * NOTE: To be removed in v1.0.0. Should be included in run(). + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + protected function emit(ServerRequestInterface $request) + { + $response = $this->handle($request); + + $code = (string) $response->getStatusCode(); + + $code .= ' ' . $response->getReasonPhrase(); + + $headers = (array) $response->getHeaders(); + + $version = $response->getProtocolVersion(); + + header('HTTP/' . $version . ' ' . $code); + + foreach ($headers as $name => $values) + { + $value = (string) implode(',', $values); + + header((string) $name . ': ' . $value); + } + + return $response; + } +} \ No newline at end of file diff --git a/src/System/Handler.php b/src/System/Handler.php new file mode 100644 index 00000000..eec9d900 --- /dev/null +++ b/src/System/Handler.php @@ -0,0 +1,144 @@ + + */ +class Handler implements HandlerInterface +{ + /** + * @var \Rougin\Slytherin\Container\ContainerInterface + */ + protected $container; + + /** + * @var \Rougin\Slytherin\Routing\RouteInterface + */ + protected $route; + + /** + * Initializes the system handler. + * + * @param \Rougin\Slytherin\Routing\RouteInterface $route + * @param \Rougin\Slytherin\Container\ContainerInterface $container + */ + public function __construct(RouteInterface $route, ContainerInterface $container) + { + $this->route = $route; + + $this->container = $container; + } + + /** + * Returns a callback for handling the application. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request) + { + // Attach the request again in the container to reflect from stack --- + $this->container->set(System::SERVER_REQUEST, $request); + // ------------------------------------------------------------------- + + $resolver = new Resolver($this->container); + + $handler = $this->route->getHandler(); + + if (is_array($handler) && is_string($handler[0])) + { + $handler[0] = $resolver->resolve($handler[0], $request); + + /** @var object|string */ + $objectOrMethod = $handler[0]; + + /** @var string */ + $method = $handler[1]; + + $reflector = new \ReflectionMethod($objectOrMethod, $method); + } + else + { + /** @var \Closure|string */ + $closure = $handler; + + $reflector = new \ReflectionFunction($closure); + } + + /** @var callable */ + $callable = $handler; + + $params = $this->setParams($reflector); + + $handler = call_user_func_array($callable, $params); + + return $this->setResponse($handler); + } + + /** + * Parses the reflection parameters against the result parameters. + * + * @param \ReflectionFunctionAbstract $reflector + * @return array + */ + protected function setParams(\ReflectionFunctionAbstract $reflector) + { + $resolver = new Resolver($this->container); + + $params = $this->route->getParams(); + + if (empty($params)) + { + return $resolver->getArguments($reflector, $params); + } + + $items = $reflector->getParameters(); + + $values = $resolver->getArguments($reflector, $params); + + $result = array(); + + foreach (array_keys($items) as $key) + { + $exists = array_key_exists($key, $values); + + $result[] = $exists ? $values[$key] : $params[$key]; + } + + return $result; + } + + /** + * Converts the result into a \Psr\Http\Message\ResponseInterface instance. + * + * @param \Psr\Http\Message\ResponseInterface|mixed $function + * @return \Psr\Http\Message\ResponseInterface + */ + protected function setResponse($function) + { + /** @var \Psr\Http\Message\ResponseInterface */ + $response = $this->container->get(System::RESPONSE); + + if (is_string($function)) + { + $stream = $response->getBody(); + + $stream->write($function); + } + + $instanceof = $function instanceof ResponseInterface; + + return $instanceof ? $function : $response; + } +} diff --git a/src/System/Resolver.php b/src/System/Resolver.php new file mode 100644 index 00000000..2608d2f1 --- /dev/null +++ b/src/System/Resolver.php @@ -0,0 +1,176 @@ + + */ +class Resolver +{ + /** + * @var \Psr\Container\ContainerInterface + */ + protected $container; + + /** + * @var \Psr\Container\ContainerInterface + */ + protected $extra; + + /** + * @param \Psr\Container\ContainerInterface $container + * @param \Psr\Container\ContainerInterface|null $extra + */ + public function __construct(ContainerInterface $container, ContainerInterface $extra = null) + { + $this->container = $container; + + if (! $extra) + { + $extra = new ReflectionContainer; + } + + $this->extra = $extra; + } + + /** + * Resolves the specified parameters from a container. + * + * @param \ReflectionFunctionAbstract $reflector + * @param array $parameters + * @return array + */ + public function getArguments(\ReflectionFunctionAbstract $reflector, $parameters = array()) + { + $items = $reflector->getParameters(); + + $result = array(); + + foreach ($items as $key => $item) + { + $argument = $this->getArgument($item); + + $name = (string) $item->getName(); + + if (array_key_exists($name, $parameters)) + { + $result[$key] = $parameters[$name]; + } + + if ($argument) $result[$key] = $argument; + } + + return $result; + } + + /** + * Resolves the specified identifier to an instance. + * + * @throws \Psr\Container\NotFoundExceptionInterface + * + * @param string $id + * @param \Psr\Http\Message\ServerRequestInterface|null $request + * @return mixed + */ + public function resolve($id, ServerRequestInterface $request = null) + { + /** @var class-string $id */ + $reflection = new \ReflectionClass($id); + + if (! $constructor = $reflection->getConstructor()) + { + return $this->extra->get($id); + } + + $result = array(); + + foreach ($constructor->getParameters() as $parameter) + { + $argument = $this->getArgument($parameter); + + $result[] = $this->handle($argument, $request); + } + + return $reflection->newInstanceArgs($result); + } + + /** + * Returns an argument based on the given parameter. + * + * @param \ReflectionParameter $parameter + * @return mixed|null + */ + protected function getArgument(\ReflectionParameter $parameter) + { + try + { + $argument = $parameter->getDefaultValue(); + } + catch (\ReflectionException $exception) + { + $param = new Parameter($parameter); + + $class = $param->getClass(); + + $name = $parameter->getName(); + + if (! is_null($class)) $name = $class->getName(); + + $argument = $this->value($name); + } + + return $argument; + } + + /** + * Handles the manipulated ServerRequest (from middleware) to an argument. + * + * @param mixed $argument + * @param \Psr\Http\Message\ServerRequestInterface|null $request + * @return mixed + */ + protected function handle($argument, ServerRequestInterface $request = null) + { + return $argument instanceof ServerRequestInterface && $request ? $request : $argument; + } + + /** + * Returns the value of the specified argument. + * + * @param string $name + * @return mixed|null + */ + protected function value($name) + { + $object = null; + + if ($this->container->has($name)) + { + $object = $this->container->get($name); + } + + if ($object || ! $this->extra->has($name)) + { + return $object; + } + + // If the identifier does not exists from extra, --- + // Try to get again from the parent container + try + { + return $this->extra->get($name); + } + catch (NotFoundExceptionInterface $error) + { + return $this->container->get($name); + } + // ------------------------------------------------- + } +} diff --git a/tests/Application/ApplicationTest.php b/tests/Application/ApplicationTest.php index 5ff24c69..cb19f48a 100644 --- a/tests/Application/ApplicationTest.php +++ b/tests/Application/ApplicationTest.php @@ -2,13 +2,15 @@ namespace Rougin\Slytherin\Application; +use Rougin\Slytherin\Testcase; + /** * Application Test * * @package Slytherin * @author Rougin Gutib */ -class ApplicationTest extends \Rougin\Slytherin\Testcase +class ApplicationTest extends Testcase { /** * @var \Rougin\Slytherin\ComponentCollection @@ -22,11 +24,13 @@ class ApplicationTest extends \Rougin\Slytherin\Testcase */ protected function doSetUp() { - if (! interface_exists('Psr\Container\ContainerInterface')) { + if (! interface_exists('Psr\Container\ContainerInterface')) + { $this->markTestSkipped('Container Interop is not installed.'); } - if (! interface_exists('Psr\Http\Message\ResponseInterface')) { + if (! interface_exists('Psr\Http\Message\ResponseInterface')) + { $this->markTestSkipped('PSR-7 HTTP Message is not installed.'); } @@ -38,7 +42,8 @@ protected function doSetUp() 'Rougin\Slytherin\Fixture\Components\SingleComponent', ); - if (class_exists('Zend\Stratigility\MiddlewarePipe')) { + if (class_exists('Zend\Stratigility\MiddlewarePipe')) + { $components[] = 'Rougin\Slytherin\Fixture\Components\MiddlewareComponent'; } @@ -137,7 +142,8 @@ public function testRunMethodWithPutHttpMethod() */ public function testRunMethodWithPhroute() { - if (! class_exists('Phroute\Phroute\RouteCollector')) { + if (! class_exists('Phroute\Phroute\RouteCollector')) + { $this->markTestSkipped('Phroute is not installed.'); } @@ -188,7 +194,8 @@ public function testRunMethodWithIntegrateMethod() $integrations[] = 'Rougin\Slytherin\Template\RendererIntegration'; $integrations[] = 'Rougin\Slytherin\Debug\ErrorHandlerIntegration'; - if (interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) { + if (interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) + { $integrations[] = 'Rougin\Slytherin\Middleware\MiddlewareIntegration'; } @@ -215,7 +222,8 @@ private function runApplication($httpMethod, $uriEndpoint, $data = array()) $request = $request->withMethod($httpMethod)->withUri($uri); - switch ($httpMethod) { + switch ($httpMethod) + { case 'GET': $request = $request->withQueryParams($data); diff --git a/tests/Application/ApplicationTestCases.php b/tests/Application/ApplicationTestCases.php index e2229f7c..ec146e7c 100644 --- a/tests/Application/ApplicationTestCases.php +++ b/tests/Application/ApplicationTestCases.php @@ -206,7 +206,7 @@ public function testHandleMethodWithTypehintedParameter() { $interface = 'Rougin\Slytherin\Routing\DispatcherInterface'; - $dispatcher = Application::container()->get($interface); + $dispatcher = $this->application->get($interface); // TODO: Implement resolving of type hinted parameters from container to PhrouteResolver ---------- if (is_a($dispatcher, 'Rougin\Slytherin\Routing\PhrouteDispatcher')) @@ -271,7 +271,7 @@ protected function request($method, $uri, $data = array(), $server = array()) } // TODO: Remove this one. This was added because of Phroute will resolve it automatically. :( --- - $static = Application::container(); + $static = $this->application; if ($static && method_exists($static, 'set')) { diff --git a/tests/Application/AurynContainerTest.php b/tests/Application/AurynContainerTest.php index 4b2cb5e1..b43de2a1 100644 --- a/tests/Application/AurynContainerTest.php +++ b/tests/Application/AurynContainerTest.php @@ -29,7 +29,8 @@ class_exists('Auryn\Injector') || $this->markTestSkipped('Auryn is not installed $container->alias('Psr\Http\Message\ResponseInterface', 'Rougin\Slytherin\Http\Response'); $container->alias('Rougin\Slytherin\Routing\DispatcherInterface', 'Rougin\Slytherin\Routing\Dispatcher'); - if (interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) { + if (interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) + { $container->share(new \Rougin\Slytherin\Middleware\Dispatcher); $container->alias('Rougin\Slytherin\Middleware\DispatcherInterface', 'Rougin\Slytherin\Middleware\Dispatcher'); diff --git a/tests/Fixture/Middlewares/EmptyMiddleware.php b/tests/Fixture/Middlewares/EmptyMiddleware.php index 4eabfe72..14aa9230 100644 --- a/tests/Fixture/Middlewares/EmptyMiddleware.php +++ b/tests/Fixture/Middlewares/EmptyMiddleware.php @@ -2,7 +2,8 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Psr\Http\Message\ResponseInterface; +use Interop\Http\ServerMiddleware\DelegateInterface; +use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; /** @@ -11,16 +12,10 @@ * @package Slytherin * @author Rougin Gutib */ -class EmptyMiddleware +class EmptyMiddleware implements MiddlewareInterface { - /** - * @param \Psr\Http\Message\ResponseInterface $request - * @param \Psr\Http\Message\ServerRequestInterface $response - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) + public function process(ServerRequestInterface $request, DelegateInterface $delegate) { - return $next($request, $response); + return $delegate->process($request); } } diff --git a/tests/Fixture/Middlewares/FirstMiddleware.php b/tests/Fixture/Middlewares/FirstMiddleware.php index a7038f7d..d7a794d4 100644 --- a/tests/Fixture/Middlewares/FirstMiddleware.php +++ b/tests/Fixture/Middlewares/FirstMiddleware.php @@ -2,7 +2,8 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Psr\Http\Message\ResponseInterface; +use Interop\Http\ServerMiddleware\DelegateInterface; +use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; /** @@ -11,18 +12,14 @@ * @package Slytherin * @author Rougin Gutib */ -class FirstMiddleware +class FirstMiddleware implements MiddlewareInterface { - /** - * @param \Psr\Http\Message\ResponseInterface $request - * @param \Psr\Http\Message\ServerRequestInterface $response - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) + public function process(ServerRequestInterface $request, DelegateInterface $delegate) { + $response = $delegate->process($request); + $response->getBody()->write('First!'); - return $next($request, $response); + return $response; } } diff --git a/tests/Fixture/Middlewares/LastMiddleware.php b/tests/Fixture/Middlewares/LastMiddleware.php index f62264e4..2cffa77e 100644 --- a/tests/Fixture/Middlewares/LastMiddleware.php +++ b/tests/Fixture/Middlewares/LastMiddleware.php @@ -2,7 +2,8 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Psr\Http\Message\ResponseInterface; +use Interop\Http\ServerMiddleware\DelegateInterface; +use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; /** @@ -11,21 +12,13 @@ * @package Slytherin * @author Rougin Gutib */ -class LastMiddleware +class FirstMiddleware implements MiddlewareInterface { - /** - * @param \Psr\Http\Message\ResponseInterface $request - * @param \Psr\Http\Message\ServerRequestInterface $response - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) + public function process(ServerRequestInterface $request, DelegateInterface $delegate) { - if ($response->getBody() == '') { - $response->getBody()->write('Loaded with middleware'); - } else { - $response->getBody()->write(' Last!'); - } + $response = $delegate->process($request); + + $response->getBody()->write('Loaded with middleware'); return $response; } diff --git a/tests/Fixture/Middlewares/SecondMiddleware.php b/tests/Fixture/Middlewares/SecondMiddleware.php index 84bf6264..0025cf9d 100644 --- a/tests/Fixture/Middlewares/SecondMiddleware.php +++ b/tests/Fixture/Middlewares/SecondMiddleware.php @@ -2,7 +2,8 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Psr\Http\Message\ResponseInterface; +use Interop\Http\ServerMiddleware\DelegateInterface; +use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; /** @@ -11,18 +12,14 @@ * @package Slytherin * @author Rougin Gutib */ -class SecondMiddleware +class SecondMiddleware implements MiddlewareInterface { - /** - * @param \Psr\Http\Message\ResponseInterface $request - * @param \Psr\Http\Message\ServerRequestInterface $response - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) + public function process(ServerRequestInterface $request, DelegateInterface $delegate) { + $response = $delegate->process($request); + $response->getBody()->write(' Second!'); - return $next($request, $response); + return $response; } } diff --git a/tests/Sample/Router.php b/tests/Sample/Router.php index 10ffa628..f3074cbf 100644 --- a/tests/Sample/Router.php +++ b/tests/Sample/Router.php @@ -2,6 +2,7 @@ namespace Rougin\Slytherin\Sample; +use Rougin\Slytherin\Fixture\Middlewares\LastMiddleware; use Rougin\Slytherin\Routing\Router as Slytherin; /** @@ -20,7 +21,7 @@ public function routes($parsed = false) $this->get('/hello', 'Hello@index'); - $this->get('/response', 'Hello@response'); + $this->get('/response', 'Hello@response', new LastMiddleware); $this->get('/hi/{name}', 'Hello@name'); From 5c7856f7054ab3ce99e9b68153760475c7b2cd10 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 10:47:31 +0800 Subject: [PATCH 02/46] Fix unit tests for "Server" --- src/Server/Dispatch.php | 7 ++-- .../{CallableWrapper.php => Wrapper.php} | 2 +- src/System/Handler.php | 4 +-- tests/Application/AurynContainerTest.php | 33 +++++++++++-------- tests/Application/ContainerInterfaceTest.php | 29 +++++++++------- .../Application/IntegrationInterfaceTest.php | 14 -------- tests/Fixture/Middlewares/LastMiddleware.php | 5 +-- tests/Middleware/DispatcherTestCases.php | 25 ++++++++------ tests/Sample/Router.php | 3 +- 9 files changed, 60 insertions(+), 62 deletions(-) rename src/Server/{CallableWrapper.php => Wrapper.php} (98%) diff --git a/src/Server/Dispatch.php b/src/Server/Dispatch.php index 8a3a0556..d5bcbc1c 100644 --- a/src/Server/Dispatch.php +++ b/src/Server/Dispatch.php @@ -43,11 +43,8 @@ protected function transform($middleware) { $isClosure = is_a($middleware, 'Closure'); - if ($isClosure) - { - return new CallableWrapper($middleware); - } + if (! $isClosure) return new $middleware; - return new $middleware; + return new Wrapper($middleware); } } diff --git a/src/Server/CallableWrapper.php b/src/Server/Wrapper.php similarity index 98% rename from src/Server/CallableWrapper.php rename to src/Server/Wrapper.php index 5c3d5737..a7b1e797 100644 --- a/src/Server/CallableWrapper.php +++ b/src/Server/Wrapper.php @@ -15,7 +15,7 @@ * @author Rougin Gutib * @author Rasmus Schultz */ -class CallableWrapper +class Wrapper { /** * @var callable diff --git a/src/System/Handler.php b/src/System/Handler.php index eec9d900..bb14276c 100644 --- a/src/System/Handler.php +++ b/src/System/Handler.php @@ -132,9 +132,7 @@ protected function setResponse($function) if (is_string($function)) { - $stream = $response->getBody(); - - $stream->write($function); + $response->getBody()->write($function); } $instanceof = $function instanceof ResponseInterface; diff --git a/tests/Application/AurynContainerTest.php b/tests/Application/AurynContainerTest.php index b43de2a1..d89d2e74 100644 --- a/tests/Application/AurynContainerTest.php +++ b/tests/Application/AurynContainerTest.php @@ -2,6 +2,12 @@ namespace Rougin\Slytherin\Application; +use Rougin\Slytherin\Container\AurynContainer; +use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Routing\Dispatcher; +use Rougin\Slytherin\Server\Dispatch; +use Rougin\Slytherin\System; + /** * Auryn Container Test * @@ -17,25 +23,26 @@ class AurynContainerTest extends ApplicationTestCases */ protected function doSetUp() { - class_exists('Auryn\Injector') || $this->markTestSkipped('Auryn is not installed.'); + if (! class_exists('Auryn\Injector')) $this->markTestSkipped('Auryn is not installed.'); + + $container = new AurynContainer; - $container = new \Rougin\Slytherin\Container\AurynContainer; + $router = $this->router(); $container->share($this->request('GET', '/')); - $container->share(new \Rougin\Slytherin\Http\Response); - $container->share(new \Rougin\Slytherin\Routing\Dispatcher($this->router())); + $container->alias(System::SERVER_REQUEST, 'Rougin\Slytherin\Http\ServerRequest'); - $container->alias('Psr\Http\Message\ServerRequestInterface', 'Rougin\Slytherin\Http\ServerRequest'); - $container->alias('Psr\Http\Message\ResponseInterface', 'Rougin\Slytherin\Http\Response'); - $container->alias('Rougin\Slytherin\Routing\DispatcherInterface', 'Rougin\Slytherin\Routing\Dispatcher'); + $container->share(new Response); + $container->alias(System::RESPONSE, 'Rougin\Slytherin\Http\Response'); - if (interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) - { - $container->share(new \Rougin\Slytherin\Middleware\Dispatcher); + $container->share(new Dispatcher($router)); + $container->alias(System::DISPATCHER, 'Rougin\Slytherin\Routing\Dispatcher'); - $container->alias('Rougin\Slytherin\Middleware\DispatcherInterface', 'Rougin\Slytherin\Middleware\Dispatcher'); - } + // TODO: Reimplement "Server" with "Middleware" -------------------------- + $container->share(new Dispatch); + $container->alias(System::MIDDLEWARE, 'Rougin\Slytherin\Server\Dispatch'); + // ----------------------------------------------------------------------- - $this->application = new \Rougin\Slytherin\Application($container); + $this->application = new Application($container); } } diff --git a/tests/Application/ContainerInterfaceTest.php b/tests/Application/ContainerInterfaceTest.php index 9071352f..bfe23381 100644 --- a/tests/Application/ContainerInterfaceTest.php +++ b/tests/Application/ContainerInterfaceTest.php @@ -2,6 +2,13 @@ namespace Rougin\Slytherin\Application; +use Rougin\Slytherin\Application; +use Rougin\Slytherin\Container\Container; +use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Routing\Dispatcher; +use Rougin\Slytherin\Server\Dispatch; +use Rougin\Slytherin\System; + /** * Container Interface Test * @@ -17,22 +24,20 @@ class ContainerInterfaceTest extends ApplicationTestCases */ protected function doSetUp() { - $container = new \Rougin\Slytherin\Container\Container; - - $dispatcher = new \Rougin\Slytherin\Routing\Dispatcher($this->router()); + $container = new Container; - $response = new \Rougin\Slytherin\Http\Response; + $dispatcher = new Dispatcher($this->router()); + $container->set(System::DISPATCHER, $dispatcher); - $container->set('Psr\Http\Message\ServerRequestInterface', $this->request('GET', '/')); - $container->set('Psr\Http\Message\ResponseInterface', $response); - $container->set('Rougin\Slytherin\Routing\DispatcherInterface', $dispatcher); + $request = $this->request('GET', '/'); + $container->set(System::SERVER_REQUEST, $request); - if (interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) { - $middleware = new \Rougin\Slytherin\Middleware\Dispatcher; + $response = new Response; + $container->set(System::RESPONSE, $response); - $container->set('Rougin\Slytherin\Middleware\DispatcherInterface', $middleware); - } + $dispatch = new Dispatch; + $container->set(System::MIDDLEWARE, $dispatch); - $this->application = new \Rougin\Slytherin\Application($container); + $this->application = new Application($container); } } diff --git a/tests/Application/IntegrationInterfaceTest.php b/tests/Application/IntegrationInterfaceTest.php index 50e5d4ae..02c0c597 100644 --- a/tests/Application/IntegrationInterfaceTest.php +++ b/tests/Application/IntegrationInterfaceTest.php @@ -43,18 +43,4 @@ protected function doSetUp() $this->application = $app->integrate($integrations, $config); } - - /** - * Tests the instances of static::$container. - * - * @return - */ - public function testStaticContainer() - { - $container = \Rougin\Slytherin\Application::container(); - - $interface = 'Rougin\Slytherin\Template\RendererInterface'; - - $this->assertTrue($container->has($interface)); - } } diff --git a/tests/Fixture/Middlewares/LastMiddleware.php b/tests/Fixture/Middlewares/LastMiddleware.php index 2cffa77e..779ea66a 100644 --- a/tests/Fixture/Middlewares/LastMiddleware.php +++ b/tests/Fixture/Middlewares/LastMiddleware.php @@ -5,6 +5,7 @@ use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Http\Response; /** * Last Middleware @@ -12,11 +13,11 @@ * @package Slytherin * @author Rougin Gutib */ -class FirstMiddleware implements MiddlewareInterface +class LastMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, DelegateInterface $delegate) { - $response = $delegate->process($request); + $response = new Response; $response->getBody()->write('Loaded with middleware'); diff --git a/tests/Middleware/DispatcherTestCases.php b/tests/Middleware/DispatcherTestCases.php index 86521606..e23368ec 100644 --- a/tests/Middleware/DispatcherTestCases.php +++ b/tests/Middleware/DispatcherTestCases.php @@ -3,6 +3,7 @@ namespace Rougin\Slytherin\Middleware; use Rougin\Slytherin\Http\ServerRequest; +use Rougin\Slytherin\Testcase; /** * Dispatcher Test Cases @@ -10,7 +11,7 @@ * @package Slytherin * @author Rougin Gutib */ -class DispatcherTestCases extends \Rougin\Slytherin\Testcase +class DispatcherTestCases extends Testcase { /** * @var \Rougin\Slytherin\Middleware\DispatcherInterface @@ -24,7 +25,8 @@ class DispatcherTestCases extends \Rougin\Slytherin\Testcase */ public function testProcessMethodWithDoublePassCallback() { - $this->dispatcher->push(function ($request, $response, $next) { + $this->dispatcher->push(function ($request, $response, $next) + { $response = $next($request, $response)->withStatus(404); return $response->withHeader('X-Slytherin', time()); @@ -48,7 +50,8 @@ public function testProcessMethodWithSinglePassCallback() $wrapper = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapper'; - if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) { + if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) + { $message = 'Stratigility\'s current installed version'; $message .= ' does not accept single pass middlewares'; @@ -58,7 +61,8 @@ public function testProcessMethodWithSinglePassCallback() $time = (integer) time(); - $this->dispatcher->push(function ($request, $next) use ($time) { + $this->dispatcher->push(function ($request, $next) use ($time) + { $response = $next($request); return $response->withHeader('X-Slytherin', $time); @@ -82,7 +86,8 @@ public function testProcessMethodWithDelagateInterfaceCallback() $wrapper = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapper'; - if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) { + if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) + { $message = 'Stratigility\'s current version'; $message .= (string) ' does not accept delegates'; @@ -90,7 +95,8 @@ public function testProcessMethodWithDelagateInterfaceCallback() $this->markTestSkipped((string) $message); } - $this->dispatcher->push(function ($request, $delegate) { + $this->dispatcher->push(function ($request, $delegate) + { $response = $delegate->process($request); return $response->withHeader('Content-Type', 'application/json'); @@ -114,10 +120,9 @@ public function testProcessMethodWithString() $wrapper = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapper'; - if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) { - $message = 'Stratigility\'s current version'; - - $message .= ' does not accept PSR-15 middlewares'; + if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) + { + $message = 'Stratigility\'s current version does not accept PSR-15 middlewares'; $this->markTestSkipped((string) $message); } diff --git a/tests/Sample/Router.php b/tests/Sample/Router.php index f3074cbf..10ffa628 100644 --- a/tests/Sample/Router.php +++ b/tests/Sample/Router.php @@ -2,7 +2,6 @@ namespace Rougin\Slytherin\Sample; -use Rougin\Slytherin\Fixture\Middlewares\LastMiddleware; use Rougin\Slytherin\Routing\Router as Slytherin; /** @@ -21,7 +20,7 @@ public function routes($parsed = false) $this->get('/hello', 'Hello@index'); - $this->get('/response', 'Hello@response', new LastMiddleware); + $this->get('/response', 'Hello@response'); $this->get('/hi/{name}', 'Hello@name'); From e613018a560423d457171a98fdfdb9634eb68701 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 11:27:16 +0800 Subject: [PATCH 03/46] Change details in CHANGELOG.md --- CHANGELOG.md | 3 + src/Application/CallbackHandler.php | 106 ------------- src/Application/FinalCallback.php | 144 ------------------ src/Middleware/Dispatcher.php | 4 +- tests/Application/ApplicationTestCases.php | 13 -- tests/Fixture/Middlewares/EmptyMiddleware.php | 15 +- tests/Fixture/Middlewares/FirstMiddleware.php | 17 ++- tests/Fixture/Middlewares/LastMiddleware.php | 18 +++ .../Fixture/Middlewares/SecondMiddleware.php | 17 ++- tests/Middleware/DispatcherTestCases.php | 14 +- 10 files changed, 63 insertions(+), 288 deletions(-) delete mode 100644 src/Application/CallbackHandler.php delete mode 100644 src/Application/FinalCallback.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 549854a5..5d1fad85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,13 @@ All notable changes to `Slytherin` will be documented in this file. - `preferred` property in integrations - `ContainerException` in `Container` - `RouteInterface` for handling specific routes +- Support for all versions of `http-interop/http-middleware` (`0.3`, `0.4`, `0.5`) ### Changed - Third-party packages in `Routing` extends to Slytherin's `Dispatcher`, `Router` - Conformed all application logic to `RouteInterface` - `UnexpectedValueException` to `BadMethodCallException` in `DispatcherInterface` +- Conformed `Middleware` to the official `PSR-15` package (`psr/http-server-middleware`) ### Fixed - Type hinting of all classes using `PHPStan` (up to `level 9`) @@ -25,6 +27,7 @@ All notable changes to `Slytherin` will be documented in this file. ### Removed - `__call` methods in `Router`, use the defined methods instead (e.g., `get()`, `post()`, etc.) +- Double pass middlewares (to be fully compliant for PSR-15) ## [0.9.6](https://github.com/rougin/slytherin/compare/v0.9.5...v0.9.6) - 2023-11-16 diff --git a/src/Application/CallbackHandler.php b/src/Application/CallbackHandler.php deleted file mode 100644 index 108de546..00000000 --- a/src/Application/CallbackHandler.php +++ /dev/null @@ -1,106 +0,0 @@ - - */ -class CallbackHandler -{ - const DISPATCHER = 'Rougin\Slytherin\Routing\DispatcherInterface'; - - const ROUTER = 'Rougin\Slytherin\Routing\RouterInterface'; - - const RESPONSE = 'Psr\Http\Message\ResponseInterface'; - - /** - * @var \Rougin\Slytherin\Application\FinalCallback - */ - protected $callback; - - /** - * @var \Rougin\Slytherin\Container\Container - */ - protected $container; - - /** - * @var array - */ - protected $middlewares = array(); - - /** - * Initializes the handler instance. - * - * @param \Psr\Container\ContainerInterface $container - */ - public function __construct(ContainerInterface $container) - { - $this->container = new Container(array(), $container); - } - - /** - * Returns a response instance. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request) - { - /** @var \Rougin\Slytherin\Routing\DispatcherInterface */ - $dispatcher = $this->container->get(self::DISPATCHER); - - if ($this->container->has(self::ROUTER)) - { - /** @var \Rougin\Slytherin\Routing\RouterInterface */ - $router = $this->container->get(self::ROUTER); - - $dispatcher = $dispatcher->setRouter($router); - } - - $path = $request->getUri()->getPath(); - - $method = $request->getMethod(); - - $route = $dispatcher->dispatch($method, $path); - - $this->middlewares = $route->getMiddlewares(); - - $callback = new FinalCallback($route, $this->container); - - return $this->middleware($callback, $request); - } - - /** - * Dispatches the middlewares of the specified request, if there are any. - * - * @param \Rougin\Slytherin\Application\FinalCallback $callback - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - protected function middleware(FinalCallback $callback, ServerRequestInterface $request) - { - $exists = interface_exists(Application::MIDDLEWARE); - - if (! $exists) return $callback($request); - - /** @var \Psr\Http\Message\ResponseInterface */ - $response = $this->container->get(self::RESPONSE); - - $middleware = new Dispatcher($this->middlewares, $response); - - $delegate = new Delegate($callback); - - return $middleware->process($request, $delegate); - } -} diff --git a/src/Application/FinalCallback.php b/src/Application/FinalCallback.php deleted file mode 100644 index 52bd7fc6..00000000 --- a/src/Application/FinalCallback.php +++ /dev/null @@ -1,144 +0,0 @@ - - */ -class FinalCallback -{ - const REQUEST = 'Psr\Http\Message\ServerRequestInterface'; - - const RESPONSE = 'Psr\Http\Message\ResponseInterface'; - - /** - * @var \Rougin\Slytherin\Container\Container - */ - protected $container; - - /** - * @var \Rougin\Slytherin\Routing\RouteInterface - */ - protected $route; - - /** - * Sets up the callback handler. - * - * @param \Rougin\Slytherin\Routing\RouteInterface $route - * @param \Rougin\Slytherin\Container\Container $container - */ - public function __construct(RouteInterface $route, Container $container) - { - $this->container = $container; - - $this->route = $route; - } - - /** - * Returns a callback for handling the application. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request) - { - // Attach the request again in the container to reflect from stack --- - $this->container->set(self::REQUEST, $request); - // ------------------------------------------------------------------- - - $handler = $this->route->getHandler(); - - if (is_array($handler) && is_string($handler[0])) - { - $handler[0] = $this->container->resolve($handler[0], $request); - - /** @var object|string */ - $objectOrMethod = $handler[0]; - - /** @var string */ - $method = $handler[1]; - - $reflector = new \ReflectionMethod($objectOrMethod, $method); - } - else - { - /** @var \Closure|string */ - $closure = $handler; - - $reflector = new \ReflectionFunction($closure); - } - - /** @var callable */ - $callable = $handler; - - $params = $this->setParams($reflector); - - $handler = call_user_func_array($callable, $params); - - return $this->finalize($handler); - } - - /** - * Converts the result into a \Psr\Http\Message\ResponseInterface instance. - * - * @param \Psr\Http\Message\ResponseInterface|mixed $function - * @return \Psr\Http\Message\ResponseInterface - */ - protected function finalize($function) - { - /** @var \Psr\Http\Message\ResponseInterface */ - $response = $this->container->get(self::RESPONSE); - - if (is_string($function)) - { - $stream = $response->getBody(); - - $stream->write($function); - } - - $instanceof = $function instanceof ResponseInterface; - - return $instanceof ? $function : $response; - } - - /** - * Parses the reflection parameters against the result parameters. - * - * @param \ReflectionFunctionAbstract $reflector - * @return array - */ - protected function setParams(\ReflectionFunctionAbstract $reflector) - { - $params = $this->route->getParams(); - - if (empty($params)) - { - return $this->container->arguments($reflector, $params); - } - - $items = $reflector->getParameters(); - - $values = $this->container->arguments($reflector, $params); - - $result = array(); - - foreach (array_keys($items) as $key) - { - $exists = array_key_exists($key, $values); - - $result[] = $exists ? $values[$key] : $params[$key]; - } - - return $result; - } -} diff --git a/src/Middleware/Dispatcher.php b/src/Middleware/Dispatcher.php index 105c25a6..c7a66df2 100644 --- a/src/Middleware/Dispatcher.php +++ b/src/Middleware/Dispatcher.php @@ -242,7 +242,9 @@ protected function transform($middleware, $wrappable = true) { $response = null; - if (is_a($middleware, Application::MIDDLEWARE)) return $middleware; + $psr = 'Interop\Http\ServerMiddleware\MiddlewareInterface'; + + if (is_a($middleware, $psr)) return $middleware; $approach = $this->approach($middleware); diff --git a/tests/Application/ApplicationTestCases.php b/tests/Application/ApplicationTestCases.php index ec146e7c..ef91162b 100644 --- a/tests/Application/ApplicationTestCases.php +++ b/tests/Application/ApplicationTestCases.php @@ -270,19 +270,6 @@ protected function request($method, $uri, $data = array(), $server = array()) break; } - // TODO: Remove this one. This was added because of Phroute will resolve it automatically. :( --- - $static = $this->application; - - if ($static && method_exists($static, 'set')) - { - $class = (string) Application::SERVER_REQUEST; - - $container = call_user_func(array($static, 'set'), $class, $request); - - $this->application = new Application($container); - } - // ---------------------------------------------------------------------------------------------- - return $request; } diff --git a/tests/Fixture/Middlewares/EmptyMiddleware.php b/tests/Fixture/Middlewares/EmptyMiddleware.php index 14aa9230..c4a06721 100644 --- a/tests/Fixture/Middlewares/EmptyMiddleware.php +++ b/tests/Fixture/Middlewares/EmptyMiddleware.php @@ -3,7 +3,7 @@ namespace Rougin\Slytherin\Fixture\Middlewares; use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; /** @@ -12,8 +12,19 @@ * @package Slytherin * @author Rougin Gutib */ -class EmptyMiddleware implements MiddlewareInterface +class EmptyMiddleware { + /** + * @param \Psr\Http\Message\ResponseInterface $request + * @param \Psr\Http\Message\ServerRequestInterface $response + * @param callable|null $next + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) + { + return $next($request, $response); + } + public function process(ServerRequestInterface $request, DelegateInterface $delegate) { return $delegate->process($request); diff --git a/tests/Fixture/Middlewares/FirstMiddleware.php b/tests/Fixture/Middlewares/FirstMiddleware.php index d7a794d4..a7038f7d 100644 --- a/tests/Fixture/Middlewares/FirstMiddleware.php +++ b/tests/Fixture/Middlewares/FirstMiddleware.php @@ -2,8 +2,7 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; /** @@ -12,14 +11,18 @@ * @package Slytherin * @author Rougin Gutib */ -class FirstMiddleware implements MiddlewareInterface +class FirstMiddleware { - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + /** + * @param \Psr\Http\Message\ResponseInterface $request + * @param \Psr\Http\Message\ServerRequestInterface $response + * @param callable|null $next + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) { - $response = $delegate->process($request); - $response->getBody()->write('First!'); - return $response; + return $next($request, $response); } } diff --git a/tests/Fixture/Middlewares/LastMiddleware.php b/tests/Fixture/Middlewares/LastMiddleware.php index 779ea66a..c9560f64 100644 --- a/tests/Fixture/Middlewares/LastMiddleware.php +++ b/tests/Fixture/Middlewares/LastMiddleware.php @@ -4,6 +4,7 @@ use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Http\Response; @@ -15,6 +16,23 @@ */ class LastMiddleware implements MiddlewareInterface { + /** + * @param \Psr\Http\Message\ResponseInterface $request + * @param \Psr\Http\Message\ServerRequestInterface $response + * @param callable|null $next + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) + { + if ($response->getBody() == '') { + $response->getBody()->write('Loaded with middleware'); + } else { + $response->getBody()->write(' Last!'); + } + + return $response; + } + public function process(ServerRequestInterface $request, DelegateInterface $delegate) { $response = new Response; diff --git a/tests/Fixture/Middlewares/SecondMiddleware.php b/tests/Fixture/Middlewares/SecondMiddleware.php index 0025cf9d..84bf6264 100644 --- a/tests/Fixture/Middlewares/SecondMiddleware.php +++ b/tests/Fixture/Middlewares/SecondMiddleware.php @@ -2,8 +2,7 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; /** @@ -12,14 +11,18 @@ * @package Slytherin * @author Rougin Gutib */ -class SecondMiddleware implements MiddlewareInterface +class SecondMiddleware { - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + /** + * @param \Psr\Http\Message\ResponseInterface $request + * @param \Psr\Http\Message\ServerRequestInterface $response + * @param callable|null $next + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) { - $response = $delegate->process($request); - $response->getBody()->write(' Second!'); - return $response; + return $next($request, $response); } } diff --git a/tests/Middleware/DispatcherTestCases.php b/tests/Middleware/DispatcherTestCases.php index e23368ec..3831fa7d 100644 --- a/tests/Middleware/DispatcherTestCases.php +++ b/tests/Middleware/DispatcherTestCases.php @@ -25,12 +25,14 @@ class DispatcherTestCases extends Testcase */ public function testProcessMethodWithDoublePassCallback() { - $this->dispatcher->push(function ($request, $response, $next) + $fn = function ($request, $response, $next) { $response = $next($request, $response)->withStatus(404); return $response->withHeader('X-Slytherin', time()); - }); + }; + + $this->dispatcher->push($fn); $expected = (integer) 404; @@ -52,9 +54,7 @@ public function testProcessMethodWithSinglePassCallback() if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) { - $message = 'Stratigility\'s current installed version'; - - $message .= ' does not accept single pass middlewares'; + $message = 'Stratigility\'s current installed version does not accept single pass middlewares'; $this->markTestSkipped((string) $message); } @@ -88,9 +88,7 @@ public function testProcessMethodWithDelagateInterfaceCallback() if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) { - $message = 'Stratigility\'s current version'; - - $message .= (string) ' does not accept delegates'; + $message = 'Stratigility\'s current version does not accept delegates'; $this->markTestSkipped((string) $message); } From 30d280d4e777d85cabd0b671074a1be47cf8cb54 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 11:34:14 +0800 Subject: [PATCH 04/46] Fix issues in php >= 8.0 --- src/Server/Handler.php | 4 ++-- src/System.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Server/Handler.php b/src/Server/Handler.php index 731a3987..b7bfeb50 100644 --- a/src/Server/Handler.php +++ b/src/Server/Handler.php @@ -14,9 +14,9 @@ class Handler implements HandlerInterface protected $index = 0; - protected $stack = array(); + protected $stack; - public function __construct($stack = array(), $default) + public function __construct(array $stack, $default) { $this->default = $default; diff --git a/src/System.php b/src/System.php index 36c3761e..2a52eb61 100644 --- a/src/System.php +++ b/src/System.php @@ -178,4 +178,4 @@ protected function emit(ServerRequestInterface $request) return $response; } -} \ No newline at end of file +} From ab9562aad19bbfe1c5a4fb9e1aa1cd073c5857c5 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 13:25:02 +0800 Subject: [PATCH 05/46] Add additional unit test in SampleTest --- src/Container/LeagueContainer.php | 2 - src/Container/Parameter.php | 9 ++++ src/Container/ReflectionContainer.php | 16 +++---- src/Server/Dispatch.php | 4 ++ src/Server/Doublepass.php | 7 +++ src/Server/Handler.php | 6 +++ src/Server/HandlerInterface.php | 4 ++ src/Server/Handlers/Handler030.php | 5 +++ src/Server/Handlers/Handler041.php | 5 +++ src/Server/Handlers/Handler050.php | 5 +++ src/Server/Handlers/Handler100.php | 5 +++ src/Server/MiddlewareInterface.php | 4 ++ src/Server/Version.php | 16 +++++++ src/Server/Wrapper.php | 19 ++++---- src/System/Resolver.php | 58 +++++-------------------- tests/Container/ContainerTest.php | 2 +- tests/Container/LeagueContainerTest.php | 24 +++++++--- tests/Sample/Depots/EsteDepot.php | 22 ++++++++++ tests/Sample/Router.php | 31 ++++++++++--- tests/Sample/Routes/Home.php | 5 ++- tests/Sample/SampleTest.php | 14 ++++++ 21 files changed, 176 insertions(+), 87 deletions(-) create mode 100644 tests/Sample/Depots/EsteDepot.php diff --git a/src/Container/LeagueContainer.php b/src/Container/LeagueContainer.php index b2b9c653..45634926 100644 --- a/src/Container/LeagueContainer.php +++ b/src/Container/LeagueContainer.php @@ -37,8 +37,6 @@ public function set($id, $concrete, $shared = false) $params = array($id, $concrete); call_user_func_array($class, $params); - - return $this; } // -------------------------------------------- diff --git a/src/Container/Parameter.php b/src/Container/Parameter.php index 14f2c3c7..bfe52a67 100644 --- a/src/Container/Parameter.php +++ b/src/Container/Parameter.php @@ -32,6 +32,7 @@ public function __construct(\ReflectionParameter $param) * Gets a \ReflectionClass object for the parameter being reflected or "null". * * @return \ReflectionClass|null + * @codeCoverageIgnore */ public function getClass() { @@ -58,4 +59,12 @@ public function getClass() return new \ReflectionClass(call_user_func($class)); } + + /** + * @return string + */ + public function getName() + { + return $this->getClass() ? $this->getClass()->getName() : $this->param->getName(); + } } diff --git a/src/Container/ReflectionContainer.php b/src/Container/ReflectionContainer.php index 1ab92a42..54c5a4e8 100644 --- a/src/Container/ReflectionContainer.php +++ b/src/Container/ReflectionContainer.php @@ -92,23 +92,17 @@ protected function resolve($reflection, $parameters = array()) foreach ($items as $key => $item) { - $name = (string) $item->getName(); - // Backward compatibility for ReflectionParameter --- $param = new Parameter($item); - if ($param->getClass()) - { - $name = $param->getClass()->getName(); - } + $name = $param->getName(); // -------------------------------------------------- - $result[$key] = $this->argument($item, $name); + $result[$key] = $this->argument($item, (string) $name); + + $exists = array_key_exists($name, $parameters); - if (array_key_exists($name, $parameters)) - { - $result[$key] = $parameters[(string) $name]; - } + if ($exists) $result[$key] = $parameters[$name]; } return $result; diff --git a/src/Server/Dispatch.php b/src/Server/Dispatch.php index d5bcbc1c..013d3b5a 100644 --- a/src/Server/Dispatch.php +++ b/src/Server/Dispatch.php @@ -4,6 +4,10 @@ use Psr\Http\Message\ServerRequestInterface; +/** + * @package Slytherin + * @author Rougin Gutib + */ class Dispatch implements MiddlewareInterface { protected $stack = array(); diff --git a/src/Server/Doublepass.php b/src/Server/Doublepass.php index 18d18431..c828d954 100644 --- a/src/Server/Doublepass.php +++ b/src/Server/Doublepass.php @@ -5,6 +5,13 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +/** + * TODO: Add unit test and apply this class. + * + * @package Slytherin + * @author Rougin Gutib + * @codeCoverageIgnore + */ class Doublepass { protected $handler; diff --git a/src/Server/Handler.php b/src/Server/Handler.php index b7bfeb50..0be127c0 100644 --- a/src/Server/Handler.php +++ b/src/Server/Handler.php @@ -8,6 +8,10 @@ use Rougin\Slytherin\Server\Handlers\Handler050; use Rougin\Slytherin\Server\Handlers\Handler100; +/** + * @package Slytherin + * @author Rougin Gutib + */ class Handler implements HandlerInterface { protected $default; @@ -38,6 +42,7 @@ public function handle(ServerRequestInterface $request) $next = $this->next(); + // @codeCoverageIgnoreStart if (Version::is('0.3.0')) { $next = new Handler030($next); @@ -57,6 +62,7 @@ public function handle(ServerRequestInterface $request) { $next = new Handler100($next); } + // @codeCoverageIgnoreEnd return $item->process($request, $next); } diff --git a/src/Server/HandlerInterface.php b/src/Server/HandlerInterface.php index 0dc340b8..e7eb2573 100644 --- a/src/Server/HandlerInterface.php +++ b/src/Server/HandlerInterface.php @@ -4,6 +4,10 @@ use Psr\Http\Message\ServerRequestInterface; +/** + * @package Slytherin + * @author Rougin Gutib + */ interface HandlerInterface { public function handle(ServerRequestInterface $request); diff --git a/src/Server/Handlers/Handler030.php b/src/Server/Handlers/Handler030.php index 20052a78..15a796e4 100644 --- a/src/Server/Handlers/Handler030.php +++ b/src/Server/Handlers/Handler030.php @@ -6,6 +6,11 @@ use Psr\Http\Message\RequestInterface; use Rougin\Slytherin\Server\Handler; +/** + * @package Slytherin + * @author Rougin Gutib + * @codeCoverageIgnore + */ class Handler030 implements DelegateInterface { /** diff --git a/src/Server/Handlers/Handler041.php b/src/Server/Handlers/Handler041.php index 5019cf0e..50a62ae8 100644 --- a/src/Server/Handlers/Handler041.php +++ b/src/Server/Handlers/Handler041.php @@ -6,6 +6,11 @@ use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Server\Handler; +/** + * @package Slytherin + * @author Rougin Gutib + * @codeCoverageIgnore + */ class Handler041 implements DelegateInterface { /** diff --git a/src/Server/Handlers/Handler050.php b/src/Server/Handlers/Handler050.php index 69e63f1d..f9961280 100644 --- a/src/Server/Handlers/Handler050.php +++ b/src/Server/Handlers/Handler050.php @@ -6,6 +6,11 @@ use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Server\Handler; +/** + * @package Slytherin + * @author Rougin Gutib + * @codeCoverageIgnore + */ class Handler050 implements RequestHandlerInterface { /** diff --git a/src/Server/Handlers/Handler100.php b/src/Server/Handlers/Handler100.php index dadc5ac9..7ab65cd0 100644 --- a/src/Server/Handlers/Handler100.php +++ b/src/Server/Handlers/Handler100.php @@ -7,6 +7,11 @@ use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Server\Handler; +/** + * @package Slytherin + * @author Rougin Gutib + * @codeCoverageIgnore + */ class Handler100 implements RequestHandlerInterface { /** diff --git a/src/Server/MiddlewareInterface.php b/src/Server/MiddlewareInterface.php index 4b79e8e4..7b8cd839 100644 --- a/src/Server/MiddlewareInterface.php +++ b/src/Server/MiddlewareInterface.php @@ -4,6 +4,10 @@ use Psr\Http\Message\ServerRequestInterface; +/** + * @package Slytherin + * @author Rougin Gutib + */ interface MiddlewareInterface { public function getStack(); diff --git a/src/Server/Version.php b/src/Server/Version.php index 27d91347..2afcaba6 100644 --- a/src/Server/Version.php +++ b/src/Server/Version.php @@ -2,6 +2,10 @@ namespace Rougin\Slytherin\Server; +/** + * @package Slytherin + * @author Rougin Gutib + */ class Version { const VERSION_0_3_0 = 'Interop\Http\Middleware\ServerMiddlewareInterface'; @@ -12,11 +16,23 @@ class Version const VERSION_1_0_0 = 'Psr\Http\Server\MiddlewareInterface'; + /** + * Checks if the specified version is installed. + * + * @param string $version + * @return boolean + */ public static function is($version) { return static::get() === $version; } + /** + * Returns the current version installed. + * + * @return string + * @codeCoverageIgnore + */ public static function get() { $hasPsr = interface_exists(self::VERSION_1_0_0); diff --git a/src/Server/Wrapper.php b/src/Server/Wrapper.php index a7b1e797..8e6d30d2 100644 --- a/src/Server/Wrapper.php +++ b/src/Server/Wrapper.php @@ -7,8 +7,6 @@ use Psr\Http\Message\ServerRequestInterface; /** - * Callable Middleware Wrapper - * * Converts callables into PSR-15 middlewares. * * @package Slytherin @@ -51,16 +49,15 @@ public function process(ServerRequestInterface $request, DelegateInterface $dele { $middleware = $this->middleware; - if (! $this->response instanceof ResponseInterface) - { - return $middleware($request, $delegate); - } + return $middleware($request, $delegate); - $fn = function ($request) use ($delegate) - { - return $delegate->process($request); - }; + // TODO: Allow only double pass callable middlewares --- + // $fn = function ($request) use ($delegate) + // { + // return $delegate->process($request); + // }; - return $middleware($request, $this->response, $fn); + // return $middleware($request, $this->response, $fn); + // ----------------------------------------------------- } } diff --git a/src/System/Resolver.php b/src/System/Resolver.php index 2608d2f1..b8e80c7c 100644 --- a/src/System/Resolver.php +++ b/src/System/Resolver.php @@ -25,19 +25,13 @@ class Resolver protected $extra; /** - * @param \Psr\Container\ContainerInterface $container - * @param \Psr\Container\ContainerInterface|null $extra + * @param \Psr\Container\ContainerInterface $container */ - public function __construct(ContainerInterface $container, ContainerInterface $extra = null) + public function __construct(ContainerInterface $container) { $this->container = $container; - if (! $extra) - { - $extra = new ReflectionContainer; - } - - $this->extra = $extra; + $this->extra = new ReflectionContainer; } /** @@ -115,15 +109,16 @@ protected function getArgument(\ReflectionParameter $parameter) } catch (\ReflectionException $exception) { + // Backward compatibility for ReflectionParameter --- $param = new Parameter($parameter); + // -------------------------------------------------- - $class = $param->getClass(); - - $name = $parameter->getName(); - - if (! is_null($class)) $name = $class->getName(); + $argument = null; $name = $param->getName(); - $argument = $this->value($name); + if ($this->container->has($name)) + { + $argument = $this->container->get($name); + } } return $argument; @@ -140,37 +135,4 @@ protected function handle($argument, ServerRequestInterface $request = null) { return $argument instanceof ServerRequestInterface && $request ? $request : $argument; } - - /** - * Returns the value of the specified argument. - * - * @param string $name - * @return mixed|null - */ - protected function value($name) - { - $object = null; - - if ($this->container->has($name)) - { - $object = $this->container->get($name); - } - - if ($object || ! $this->extra->has($name)) - { - return $object; - } - - // If the identifier does not exists from extra, --- - // Try to get again from the parent container - try - { - return $this->extra->get($name); - } - catch (NotFoundExceptionInterface $error) - { - return $this->container->get($name); - } - // ------------------------------------------------- - } } diff --git a/tests/Container/ContainerTest.php b/tests/Container/ContainerTest.php index 9917c4f6..98c219a1 100644 --- a/tests/Container/ContainerTest.php +++ b/tests/Container/ContainerTest.php @@ -11,7 +11,7 @@ class ContainerTest extends \Rougin\Slytherin\Testcase { /** - * @var \Rougin\Slytherin\Container\ContainerInterface + * @var \Rougin\Slytherin\Container\Container */ protected $container; diff --git a/tests/Container/LeagueContainerTest.php b/tests/Container/LeagueContainerTest.php index 25cc60b3..088e4060 100644 --- a/tests/Container/LeagueContainerTest.php +++ b/tests/Container/LeagueContainerTest.php @@ -2,16 +2,19 @@ namespace Rougin\Slytherin\Container; +use Rougin\Slytherin\Container\LeagueContainer; +use Rougin\Slytherin\Testcase; + /** * League Container Test Class * * @package Slytherin * @author Rougin Gutib */ -class LeagueContainerTest extends \Rougin\Slytherin\Testcase +class LeagueContainerTest extends Testcase { /** - * @var \Rougin\Slytherin\Container\ContainerInterface + * @var \Rougin\Slytherin\Container\LeagueContainer */ protected $container; @@ -22,9 +25,12 @@ class LeagueContainerTest extends \Rougin\Slytherin\Testcase */ protected function doSetUp() { - class_exists('League\Container\Container') || $this->markTestSkipped('League Container is not installed.'); + if (! class_exists('League\Container\Container')) + { + $this->markTestSkipped('League Container is not installed.'); + } - $this->container = new \Rougin\Slytherin\Container\LeagueContainer; + $this->container = new LeagueContainer; } /** @@ -36,9 +42,15 @@ public function testGetMethod() { $class = 'Rougin\Slytherin\Fixture\Classes\NewClass'; - $this->container->set($class, new $class); + $expected = (string) $class; + + // Added "$shared" to true in the unit test ---- + $this->container->set($class, new $class, true); + // --------------------------------------------- + + $actual = $this->container->get($class); - $this->assertInstanceOf($class, $this->container->get($class)); + $this->assertInstanceOf($expected, $actual); } /** diff --git a/tests/Sample/Depots/EsteDepot.php b/tests/Sample/Depots/EsteDepot.php new file mode 100644 index 00000000..bd356932 --- /dev/null +++ b/tests/Sample/Depots/EsteDepot.php @@ -0,0 +1,22 @@ + + */ +class EsteDepot +{ + protected $sest; + + public function __construct(SestDepot $sest) + { + $this->sest = $sest; + } + + public function text($data) + { + return $this->sest->text($data); + } +} diff --git a/tests/Sample/Router.php b/tests/Sample/Router.php index 10ffa628..94908737 100644 --- a/tests/Sample/Router.php +++ b/tests/Sample/Router.php @@ -2,6 +2,8 @@ namespace Rougin\Slytherin\Sample; +use Interop\Http\ServerMiddleware\DelegateInterface; +use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Routing\Router as Slytherin; /** @@ -28,20 +30,26 @@ public function routes($parsed = false) $this->get('/', 'Home@index'); - $this->get('/callable', function () + $fn = function () { return 'Welcome call!'; - }); + }; - $this->get('/call/{name}/{age}', function ($name, $age) + $this->get('/callable', $fn); + + $fn = function ($name, $age) { return 'Welcome ' . $name . ', ' . $age . '!'; - }); + }; + + $this->get('/call/{name}/{age}', $fn); - $this->get('/call/{name}', function ($name) + $fn = function ($name) { return 'Welcome ' . $name . '!'; - }); + }; + + $this->get('/call/{name}', $fn); $this->get('/param', 'Home@param'); @@ -49,6 +57,17 @@ public function routes($parsed = false) $this->get('/handler/param', 'Hello@param'); + $fn = function (ServerRequestInterface $request, DelegateInterface $delegate) + { + $response = $delegate->process($request); + + $response->getBody()->write('From callable middleware!'); + + return $response; + }; + + $this->get('middleware', 'Hello@response', $fn); + return parent::routes($parsed); } } diff --git a/tests/Sample/Routes/Home.php b/tests/Sample/Routes/Home.php index a9ec81d4..64d16f73 100644 --- a/tests/Sample/Routes/Home.php +++ b/tests/Sample/Routes/Home.php @@ -2,6 +2,7 @@ namespace Rougin\Slytherin\Sample\Routes; +use Rougin\Slytherin\Sample\Depots\EsteDepot; use Rougin\Slytherin\Sample\Depots\SestDepot; /** @@ -25,8 +26,8 @@ public function index() return $this->sest->text('Welcome home!'); } - public function param(SestDepot $sest) + public function param(EsteDepot $este) { - return $sest->text('Welcome param!'); + return $este->text('Welcome param!'); } } diff --git a/tests/Sample/SampleTest.php b/tests/Sample/SampleTest.php index 62f62743..30271fee 100644 --- a/tests/Sample/SampleTest.php +++ b/tests/Sample/SampleTest.php @@ -206,6 +206,20 @@ public function test_middleware_changing_the_response_parameter() $this->builder->make()->run(); } + /** + * @runInSeparateProcess + * + * @return void + */ + public function test_callable_middleware_changing_the_response_parameter() + { + $this->builder->setUrl('GET', '/middleware'); + + $this->expectOutputString('From callable middleware!'); + + $this->builder->make()->run(); + } + /** * @runInSeparateProcess * From c84a22251d8884aa395a808ae42c2a319eeaee09 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 17:26:03 +0800 Subject: [PATCH 06/46] Improve type hinting of Server --- phpstan.neon | 7 +++++ src/Routing/Route.php | 4 +-- src/Server/Dispatch.php | 28 +++++++++++++++-- src/Server/DispatchInterface.php | 21 +++++++++++++ src/Server/Doublepass.php | 14 +++++++++ src/Server/Handler.php | 49 ++++++++++++++++++++---------- src/Server/HandlerInterface.php | 4 +++ src/Server/Handlers/Handler030.php | 8 ++--- src/Server/Handlers/Handler041.php | 8 ++--- src/Server/Handlers/Handler050.php | 8 ++--- src/Server/Handlers/Handler100.php | 8 ++--- src/Server/MiddlewareInterface.php | 9 +++--- src/Server/Version.php | 13 +------- src/System.php | 6 ++-- src/System/Resolver.php | 1 - 15 files changed, 131 insertions(+), 57 deletions(-) create mode 100644 phpstan.neon create mode 100644 src/Server/DispatchInterface.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..6f1701ec --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,7 @@ +parameters: + level: 9 + paths: + - src + excludePaths: + analyse: + - src/Server/Handlers \ No newline at end of file diff --git a/src/Routing/Route.php b/src/Routing/Route.php index 6dbe24d9..c3fe37b3 100644 --- a/src/Routing/Route.php +++ b/src/Routing/Route.php @@ -37,7 +37,7 @@ class Route implements RouteInterface * @param string $method * @param string $uri * @param callable|string[]|string $handler - * @param mixed[] $middlewares + * @param mixed[]|string $middlewares */ public function __construct($method, $uri, $handler, $middlewares = array()) { @@ -78,7 +78,7 @@ public function getMethod() } /** - * @return \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] + * @return mixed[] */ public function getMiddlewares() { diff --git a/src/Server/Dispatch.php b/src/Server/Dispatch.php index 013d3b5a..f0f4f3dd 100644 --- a/src/Server/Dispatch.php +++ b/src/Server/Dispatch.php @@ -8,27 +8,41 @@ * @package Slytherin * @author Rougin Gutib */ -class Dispatch implements MiddlewareInterface +class Dispatch implements DispatchInterface { + /** + * @var mixed[] + */ protected $stack = array(); + /** + * @param mixed[] $stack + */ public function __construct($stack = array()) { - $this->stack = $stack; + if ($stack) $this->setStack($stack); } + /** + * @return mixed[] + */ public function getStack() { return $this->stack; } + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Server\HandlerInterface $handler + * @return \Psr\Http\Message\ResponseInterface + */ public function process(ServerRequestInterface $request, HandlerInterface $handler) { $stack = array(); foreach ($this->stack as $item) { - array_push($stack, $this->transform($item)); + $stack[] = $this->transform($item); } $handler = new Handler($stack, $handler); @@ -36,6 +50,10 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl return $handler->handle($request); } + /** + * @param mixed[] $stack + * @return self + */ public function setStack($stack) { $this->stack = $stack; @@ -43,6 +61,10 @@ public function setStack($stack) return $this; } + /** + * @param mixed $middleware + * @return \Rougin\Slytherin\Server\MiddlewareInterface + */ protected function transform($middleware) { $isClosure = is_a($middleware, 'Closure'); diff --git a/src/Server/DispatchInterface.php b/src/Server/DispatchInterface.php new file mode 100644 index 00000000..1020177f --- /dev/null +++ b/src/Server/DispatchInterface.php @@ -0,0 +1,21 @@ + + */ +interface DispatchInterface extends MiddlewareInterface +{ + /** + * @return mixed[] + */ + public function getStack(); + + /** + * @param mixed[] $stack + * @return self + */ + public function setStack($stack); +} diff --git a/src/Server/Doublepass.php b/src/Server/Doublepass.php index c828d954..eaf4b00e 100644 --- a/src/Server/Doublepass.php +++ b/src/Server/Doublepass.php @@ -14,10 +14,20 @@ */ class Doublepass { + /** + * @var callable + */ protected $handler; + /** + * @var \Psr\Http\Message\ResponseInterface + */ protected $response; + /** + * @param callable $handler + * @param \Psr\Http\Message\ResponseInterface $response + */ public function __construct($handler, ResponseInterface $response) { $this->handler = $handler; @@ -25,6 +35,10 @@ public function __construct($handler, ResponseInterface $response) $this->response = $response; } + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ public function handle(ServerRequestInterface $request) { return call_user_func($this->handler, $request, $this->response); diff --git a/src/Server/Handler.php b/src/Server/Handler.php index 0be127c0..8e0424e4 100644 --- a/src/Server/Handler.php +++ b/src/Server/Handler.php @@ -14,13 +14,26 @@ */ class Handler implements HandlerInterface { + /** + * @var \Rougin\Slytherin\Server\HandlerInterface + */ protected $default; + /** + * @var integer + */ protected $index = 0; + /** + * @var \Rougin\Slytherin\Server\MiddlewareInterface[] + */ protected $stack; - public function __construct(array $stack, $default) + /** + * @param \Rougin\Slytherin\Server\MiddlewareInterface[] $stack + * @param \Rougin\Slytherin\Server\HandlerInterface $default + */ + public function __construct(array $stack, HandlerInterface $default) { $this->default = $default; @@ -28,7 +41,7 @@ public function __construct(array $stack, $default) } /** - * @param mixed $request + * @param \Psr\Http\Message\ServerRequestInterface $request * @return \Psr\Http\Message\ResponseInterface */ public function handle(ServerRequestInterface $request) @@ -43,30 +56,34 @@ public function handle(ServerRequestInterface $request) $next = $this->next(); // @codeCoverageIgnoreStart - if (Version::is('0.3.0')) + switch (Version::get()) { - $next = new Handler030($next); - } + case '0.3.0': + $next = new Handler030($next); - if (Version::is('0.4.1')) - { - $next = new Handler041($next); - } + break; + case '0.4.1': + $next = new Handler041($next); - if (Version::is('0.5.0')) - { - $next = new Handler050($next); - } + break; + case '0.5.0': + $next = new Handler050($next); - if (Version::is('1.0.0')) - { - $next = new Handler100($next); + break; + case '1.0.0': + $next = new Handler100($next); + + break; } // @codeCoverageIgnoreEnd + /** @var \Rougin\Slytherin\Server\HandlerInterface $next */ return $item->process($request, $next); } + /** + * @return \Rougin\Slytherin\Server\HandlerInterface + */ protected function next() { $next = clone $this; diff --git a/src/Server/HandlerInterface.php b/src/Server/HandlerInterface.php index e7eb2573..32f2c6fc 100644 --- a/src/Server/HandlerInterface.php +++ b/src/Server/HandlerInterface.php @@ -10,5 +10,9 @@ */ interface HandlerInterface { + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ public function handle(ServerRequestInterface $request); } diff --git a/src/Server/Handlers/Handler030.php b/src/Server/Handlers/Handler030.php index 15a796e4..bb9e1d18 100644 --- a/src/Server/Handlers/Handler030.php +++ b/src/Server/Handlers/Handler030.php @@ -4,7 +4,7 @@ use Interop\Http\Middleware\DelegateInterface; use Psr\Http\Message\RequestInterface; -use Rougin\Slytherin\Server\Handler; +use Rougin\Slytherin\Server\HandlerInterface; /** * @package Slytherin @@ -14,14 +14,14 @@ class Handler030 implements DelegateInterface { /** - * @var \Rougin\Slytherin\Server\Handler + * @var \Rougin\Slytherin\Server\HandlerInterface */ protected $handler; /** - * @param \Rougin\Slytherin\Server\Handler $handler + * @param \Rougin\Slytherin\Server\HandlerInterface $handler */ - public function __construct(Handler $handler) + public function __construct(HandlerInterface $handler) { $this->handler = $handler; } diff --git a/src/Server/Handlers/Handler041.php b/src/Server/Handlers/Handler041.php index 50a62ae8..ad55319b 100644 --- a/src/Server/Handlers/Handler041.php +++ b/src/Server/Handlers/Handler041.php @@ -4,7 +4,7 @@ use Interop\Http\ServerMiddleware\DelegateInterface; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\Handler; +use Rougin\Slytherin\Server\HandlerInterface; /** * @package Slytherin @@ -14,14 +14,14 @@ class Handler041 implements DelegateInterface { /** - * @var \Rougin\Slytherin\Server\Handler + * @var \Rougin\Slytherin\Server\HandlerInterface */ protected $handler; /** - * @param \Rougin\Slytherin\Server\Handler $handler + * @param \Rougin\Slytherin\Server\HandlerInterface $handler */ - public function __construct(Handler $handler) + public function __construct(HandlerInterface $handler) { $this->handler = $handler; } diff --git a/src/Server/Handlers/Handler050.php b/src/Server/Handlers/Handler050.php index f9961280..e80b7397 100644 --- a/src/Server/Handlers/Handler050.php +++ b/src/Server/Handlers/Handler050.php @@ -4,7 +4,7 @@ use Interop\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\Handler; +use Rougin\Slytherin\Server\HandlerInterface; /** * @package Slytherin @@ -14,14 +14,14 @@ class Handler050 implements RequestHandlerInterface { /** - * @var \Rougin\Slytherin\Server\Handler + * @var \Rougin\Slytherin\Server\HandlerInterface */ protected $handler; /** - * @param \Rougin\Slytherin\Server\Handler $handler + * @param \Rougin\Slytherin\Server\HandlerInterface $handler */ - public function __construct(Handler $handler) + public function __construct(HandlerInterface $handler) { $this->handler = $handler; } diff --git a/src/Server/Handlers/Handler100.php b/src/Server/Handlers/Handler100.php index 7ab65cd0..19db2fd1 100644 --- a/src/Server/Handlers/Handler100.php +++ b/src/Server/Handlers/Handler100.php @@ -5,7 +5,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\Handler; +use Rougin\Slytherin\Server\HandlerInterface; /** * @package Slytherin @@ -15,14 +15,14 @@ class Handler100 implements RequestHandlerInterface { /** - * @var \Rougin\Slytherin\Server\Handler + * @var \Rougin\Slytherin\Server\HandlerInterface */ protected $handler; /** - * @param \Rougin\Slytherin\Server\Handler $handler + * @param \Rougin\Slytherin\Server\HandlerInterface $handler */ - public function __construct(Handler $handler) + public function __construct(HandlerInterface $handler) { $this->handler = $handler; } diff --git a/src/Server/MiddlewareInterface.php b/src/Server/MiddlewareInterface.php index 7b8cd839..f2d92417 100644 --- a/src/Server/MiddlewareInterface.php +++ b/src/Server/MiddlewareInterface.php @@ -10,9 +10,10 @@ */ interface MiddlewareInterface { - public function getStack(); - + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Server\HandlerInterface $handler + * @return \Psr\Http\Message\ResponseInterface + */ public function process(ServerRequestInterface $request, HandlerInterface $handler); - - public function setStack($stack); } diff --git a/src/Server/Version.php b/src/Server/Version.php index 2afcaba6..f220e1d8 100644 --- a/src/Server/Version.php +++ b/src/Server/Version.php @@ -16,21 +16,10 @@ class Version const VERSION_1_0_0 = 'Psr\Http\Server\MiddlewareInterface'; - /** - * Checks if the specified version is installed. - * - * @param string $version - * @return boolean - */ - public static function is($version) - { - return static::get() === $version; - } - /** * Returns the current version installed. * - * @return string + * @return string|null * @codeCoverageIgnore */ public static function get() diff --git a/src/System.php b/src/System.php index 2a52eb61..889bb5d0 100644 --- a/src/System.php +++ b/src/System.php @@ -14,7 +14,7 @@ class System const ERREPORT = 'Rougin\Slytherin\Ereport\EreportInterface'; - const MIDDLEWARE = 'Rougin\Slytherin\Server\MiddlewareInterface'; + const MIDDLEWARE = 'Rougin\Slytherin\Server\DispatchInterface'; const RENDERER = 'Rougin\Slytherin\Template\RendererInterface'; @@ -58,7 +58,7 @@ public function __construct(ContainerInterface $container = null, ConfigurationI * @throws \Psr\Container\ContainerExceptionInterface * * @param string $id - * @return object + * @return mixed */ public function get($id) { @@ -99,7 +99,7 @@ public function handle(ServerRequestInterface $request) return $handler->handle($request); } - /** @var \Rougin\Slytherin\Server\MiddlewareInterface */ + /** @var \Rougin\Slytherin\Server\DispatchInterface */ $middleware = $this->container->get(self::MIDDLEWARE); $stack = $middleware->getStack(); diff --git a/src/System/Resolver.php b/src/System/Resolver.php index b8e80c7c..ebea36b4 100644 --- a/src/System/Resolver.php +++ b/src/System/Resolver.php @@ -3,7 +3,6 @@ namespace Rougin\Slytherin\System; use Psr\Container\ContainerInterface; -use Psr\Container\NotFoundExceptionInterface; use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Container\Parameter; use Rougin\Slytherin\Container\ReflectionContainer; From ec2a077469e1ca92e5b613906009c84d67748674 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 18:32:08 +0800 Subject: [PATCH 07/46] Add Callback, Wrapper in Server --- src/Server/Callback.php | 61 +++++++++++++++++++++++++++++ src/Server/Dispatch.php | 6 +-- src/Server/DispatchInterface.php | 2 +- src/Server/Handler.php | 29 +------------- src/Server/Wrapper.php | 66 +++++++++++++++++++------------- tests/Sample/Router.php | 6 +-- 6 files changed, 108 insertions(+), 62 deletions(-) create mode 100644 src/Server/Callback.php diff --git a/src/Server/Callback.php b/src/Server/Callback.php new file mode 100644 index 00000000..b64167ec --- /dev/null +++ b/src/Server/Callback.php @@ -0,0 +1,61 @@ + + */ +class Callback +{ + /** + * @var callable + */ + protected $middleware; + + /** + * @var \Psr\Http\Message\ResponseInterface|null + */ + protected $response = null; + + /** + * Initializes the middleware instance. + * + * @param callable $middleware + * @param \Psr\Http\Message\ResponseInterface|null $response + */ + public function __construct($middleware, ResponseInterface $response = null) + { + $this->middleware = $middleware; + + $this->response = $response; + } + + /** + * Processes an incoming server request and return a response. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param mixed $handler + * @return \Psr\Http\Message\ResponseInterface + */ + public function process(ServerRequestInterface $request, $handler) + { + $middleware = $this->middleware; + + return $middleware($request, $handler); + + // TODO: Allow only double pass callable middlewares --- + // $fn = function ($request) use ($delegate) + // { + // return $delegate->process($request); + // }; + + // return $middleware($request, $this->response, $fn); + // ----------------------------------------------------- + } +} diff --git a/src/Server/Dispatch.php b/src/Server/Dispatch.php index f0f4f3dd..0a11d823 100644 --- a/src/Server/Dispatch.php +++ b/src/Server/Dispatch.php @@ -24,7 +24,7 @@ public function __construct($stack = array()) } /** - * @return mixed[] + * @return \Rougin\Slytherin\Server\MiddlewareInterface[] */ public function getStack() { @@ -69,8 +69,8 @@ protected function transform($middleware) { $isClosure = is_a($middleware, 'Closure'); - if (! $isClosure) return new $middleware; + if (! $isClosure) return new Wrapper($middleware); - return new Wrapper($middleware); + return new Callback($middleware); } } diff --git a/src/Server/DispatchInterface.php b/src/Server/DispatchInterface.php index 1020177f..b7e5938f 100644 --- a/src/Server/DispatchInterface.php +++ b/src/Server/DispatchInterface.php @@ -9,7 +9,7 @@ interface DispatchInterface extends MiddlewareInterface { /** - * @return mixed[] + * @return \Rougin\Slytherin\Server\MiddlewareInterface[] */ public function getStack(); diff --git a/src/Server/Handler.php b/src/Server/Handler.php index 8e0424e4..62d852da 100644 --- a/src/Server/Handler.php +++ b/src/Server/Handler.php @@ -3,10 +3,6 @@ namespace Rougin\Slytherin\Server; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\Handlers\Handler030; -use Rougin\Slytherin\Server\Handlers\Handler041; -use Rougin\Slytherin\Server\Handlers\Handler050; -use Rougin\Slytherin\Server\Handlers\Handler100; /** * @package Slytherin @@ -51,33 +47,10 @@ public function handle(ServerRequestInterface $request) return $this->default->handle($request); } - $item = $this->stack[$this->index]; + $item = $this->stack[(int) $this->index]; $next = $this->next(); - // @codeCoverageIgnoreStart - switch (Version::get()) - { - case '0.3.0': - $next = new Handler030($next); - - break; - case '0.4.1': - $next = new Handler041($next); - - break; - case '0.5.0': - $next = new Handler050($next); - - break; - case '1.0.0': - $next = new Handler100($next); - - break; - } - // @codeCoverageIgnoreEnd - - /** @var \Rougin\Slytherin\Server\HandlerInterface $next */ return $item->process($request, $next); } diff --git a/src/Server/Wrapper.php b/src/Server/Wrapper.php index 8e6d30d2..f18248ce 100644 --- a/src/Server/Wrapper.php +++ b/src/Server/Wrapper.php @@ -2,62 +2,74 @@ namespace Rougin\Slytherin\Server; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Psr\Http\Message\ResponseInterface; +use Rougin\Slytherin\Server\Handlers\Handler030; +use Rougin\Slytherin\Server\Handlers\Handler041; +use Rougin\Slytherin\Server\Handlers\Handler050; +use Rougin\Slytherin\Server\Handlers\Handler100; use Psr\Http\Message\ServerRequestInterface; /** - * Converts callables into PSR-15 middlewares. + * Converts various middlewares into Slytherin counterparts. * * @package Slytherin * @author Rougin Gutib - * @author Rasmus Schultz */ -class Wrapper +class Wrapper implements MiddlewareInterface { /** - * @var callable + * @var mixed */ protected $middleware; - /** - * @var \Psr\Http\Message\ResponseInterface|null - */ - protected $response = null; - /** * Initializes the middleware instance. * - * @param callable $middleware - * @param \Psr\Http\Message\ResponseInterface|null $response + * @param mixed $middleware */ - public function __construct($middleware, ResponseInterface $response = null) + public function __construct($middleware) { $this->middleware = $middleware; - - $this->response = $response; } /** * Processes an incoming server request and return a response. * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Server\HandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { $middleware = $this->middleware; - return $middleware($request, $delegate); + if (is_string($middleware)) + { + $middleware = new $middleware; + } + + // @codeCoverageIgnoreStart + switch (Version::get()) + { + case '0.3.0': + $next = new Handler030($handler); + + break; + case '0.4.1': + $next = new Handler041($handler); + + break; + case '0.5.0': + $next = new Handler050($handler); + + break; + default: + $next = new Handler100($handler); - // TODO: Allow only double pass callable middlewares --- - // $fn = function ($request) use ($delegate) - // { - // return $delegate->process($request); - // }; + break; + } + // @codeCoverageIgnoreEnd - // return $middleware($request, $this->response, $fn); - // ----------------------------------------------------- + /** @phpstan-ignore-next-line */ + return $middleware->process($request, $next); } } diff --git a/tests/Sample/Router.php b/tests/Sample/Router.php index 94908737..61576d66 100644 --- a/tests/Sample/Router.php +++ b/tests/Sample/Router.php @@ -2,9 +2,9 @@ namespace Rougin\Slytherin\Sample; -use Interop\Http\ServerMiddleware\DelegateInterface; use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Routing\Router as Slytherin; +use Rougin\Slytherin\Server\HandlerInterface; /** * @package Slytherin @@ -57,9 +57,9 @@ public function routes($parsed = false) $this->get('/handler/param', 'Hello@param'); - $fn = function (ServerRequestInterface $request, DelegateInterface $delegate) + $fn = function (ServerRequestInterface $request, HandlerInterface $handler) { - $response = $delegate->process($request); + $response = $handler->handle($request); $response->getBody()->write('From callable middleware!'); From 8440afc3520617b67d8dbd3327252e83b44791c5 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 18:43:58 +0800 Subject: [PATCH 08/46] Fix type hinting in Dispatch --- src/Server/Dispatch.php | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Server/Dispatch.php b/src/Server/Dispatch.php index 0a11d823..e9b997e6 100644 --- a/src/Server/Dispatch.php +++ b/src/Server/Dispatch.php @@ -11,7 +11,7 @@ class Dispatch implements DispatchInterface { /** - * @var mixed[] + * @var \Rougin\Slytherin\Server\MiddlewareInterface[] */ protected $stack = array(); @@ -38,12 +38,7 @@ public function getStack() */ public function process(ServerRequestInterface $request, HandlerInterface $handler) { - $stack = array(); - - foreach ($this->stack as $item) - { - $stack[] = $this->transform($item); - } + $stack = (array) $this->getStack(); $handler = new Handler($stack, $handler); @@ -56,7 +51,14 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl */ public function setStack($stack) { - $this->stack = $stack; + $result = array(); + + foreach ($stack as $item) + { + $result[] = $this->transform($item); + } + + $this->stack = $result; return $this; } @@ -67,10 +69,18 @@ public function setStack($stack) */ protected function transform($middleware) { - $isClosure = is_a($middleware, 'Closure'); + if ($middleware instanceof MiddlewareInterface) + { + return $middleware; + } - if (! $isClosure) return new Wrapper($middleware); + $object = is_object($middleware); + + if ($object && is_a($middleware, 'Closure')) + { + return new Callback($middleware); + } - return new Callback($middleware); + return new Wrapper($middleware); } } From ef7755bf8849c4cdcd0ae448872c683b0cd81eff Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 18:45:49 +0800 Subject: [PATCH 09/46] Implement Callback in MiddlewareInterface --- src/Server/Callback.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Server/Callback.php b/src/Server/Callback.php index b64167ec..2b2cef01 100644 --- a/src/Server/Callback.php +++ b/src/Server/Callback.php @@ -11,7 +11,7 @@ * @package Slytherin * @author Rougin Gutib */ -class Callback +class Callback implements MiddlewareInterface { /** * @var callable @@ -39,8 +39,8 @@ public function __construct($middleware, ResponseInterface $response = null) /** * Processes an incoming server request and return a response. * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param mixed $handler + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Server\HandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ public function process(ServerRequestInterface $request, $handler) From 4a0306d76af1e37183c3d5389bf5711259ea0218 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 21:17:07 +0800 Subject: [PATCH 10/46] Allow usage of double pass middlewares --- CHANGELOG.md | 1 - src/Server/Callback.php | 44 ++++++++++--- src/Server/Dispatch.php | 36 ++++++++++- .../Middlewares/BodyParametersMiddleware.php | 36 ++++------- tests/Fixture/Middlewares/CorsMiddleware.php | 31 +++------- tests/Fixture/Middlewares/EmptyMiddleware.php | 17 ++--- tests/Fixture/Middlewares/FirstMiddleware.php | 8 --- .../Fixture/Middlewares/InteropMiddleware.php | 17 +---- tests/Fixture/Middlewares/LastMiddleware.php | 29 ++------- .../Fixture/Middlewares/SecondMiddleware.php | 8 --- tests/Sample/Handlers/Cors.php | 62 +++---------------- tests/Sample/Handlers/Interop.php | 23 +++++++ tests/Sample/Handlers/Parsed/Request.php | 8 +-- tests/Sample/Handlers/Parsed/Response.php | 8 +-- tests/Sample/Handlers/ToJson.php | 8 +-- tests/Sample/Router.php | 42 ++++++------- tests/Sample/SampleTest.php | 14 +++++ 17 files changed, 180 insertions(+), 212 deletions(-) create mode 100644 tests/Sample/Handlers/Interop.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d1fad85..7a5aae63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,6 @@ All notable changes to `Slytherin` will be documented in this file. ### Removed - `__call` methods in `Router`, use the defined methods instead (e.g., `get()`, `post()`, etc.) -- Double pass middlewares (to be fully compliant for PSR-15) ## [0.9.6](https://github.com/rougin/slytherin/compare/v0.9.5...v0.9.6) - 2023-11-16 diff --git a/src/Server/Callback.php b/src/Server/Callback.php index 2b2cef01..6d3c460c 100644 --- a/src/Server/Callback.php +++ b/src/Server/Callback.php @@ -43,19 +43,45 @@ public function __construct($middleware, ResponseInterface $response = null) * @param \Rougin\Slytherin\Server\HandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ - public function process(ServerRequestInterface $request, $handler) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { $middleware = $this->middleware; - return $middleware($request, $handler); + if (is_string($middleware)) + { + /** @var callable */ + $middleware = new $middleware; + } - // TODO: Allow only double pass callable middlewares --- - // $fn = function ($request) use ($delegate) - // { - // return $delegate->process($request); - // }; + if (! $this->isDoublePass($middleware)) + { + return $middleware($request, $handler); + } - // return $middleware($request, $this->response, $fn); - // ----------------------------------------------------- + $fn = function ($request) use ($handler) + { + return $handler->handle($request); + }; + + return $middleware($request, $this->response, $fn); + } + + /** + * @param mixed $item + * @return boolean + */ + protected function isDoublePass($item) + { + if ($item instanceof \Closure) + { + $object = new \ReflectionFunction($item); + } + else + { + /** @var object|string $item */ + $object = new \ReflectionMethod($item, '__invoke'); + } + + return count($object->getParameters()) === 3; } } diff --git a/src/Server/Dispatch.php b/src/Server/Dispatch.php index e9b997e6..e95b976f 100644 --- a/src/Server/Dispatch.php +++ b/src/Server/Dispatch.php @@ -3,6 +3,7 @@ namespace Rougin\Slytherin\Server; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Http\Response; /** * @package Slytherin @@ -15,6 +16,11 @@ class Dispatch implements DispatchInterface */ protected $stack = array(); + /** + * @var \Psr\Http\Message\ResponseInterface|null + */ + protected $response = null; + /** * @param mixed[] $stack */ @@ -74,13 +80,37 @@ protected function transform($middleware) return $middleware; } - $object = is_object($middleware); + // Set empty response for double pass middlewares --- + if (! $this->response) + { + $this->response = new Response; + } + // -------------------------------------------------- - if ($object && is_a($middleware, 'Closure')) + if ($this->isCallable($middleware)) { - return new Callback($middleware); + /** @var callable $middleware */ + return new Callback($middleware, $this->response); } return new Wrapper($middleware); } + + /** + * @param mixed $item + * @return boolean + */ + protected function isCallable($item) + { + /** @var object|string $item */ + $method = method_exists($item, '__invoke'); + + $callable = is_callable($item); + + $object = is_object($item); + + $closure = (! $object) || $item instanceof \Closure; + + return ($method || $callable) && $closure; + } } diff --git a/tests/Fixture/Middlewares/BodyParametersMiddleware.php b/tests/Fixture/Middlewares/BodyParametersMiddleware.php index 8c0637c0..2ea6a73d 100644 --- a/tests/Fixture/Middlewares/BodyParametersMiddleware.php +++ b/tests/Fixture/Middlewares/BodyParametersMiddleware.php @@ -2,41 +2,29 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; - -use Rougin\Slytherin\Http\Response; -use Interop\Http\ServerMiddleware\DelegateInterface; +use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Server\MiddlewareInterface; /** - * Body Parameters Middleware - * * @package Slytherin * @author Rougin Gutib */ -class BodyParametersMiddleware implements \Interop\Http\ServerMiddleware\MiddlewareInterface +class BodyParametersMiddleware implements MiddlewareInterface { - /** - * @var array - */ protected $complex = array('PUT', 'DELETE'); - /** - * Process an incoming server request and return a response, optionally delegating - * to the next middleware component to create the response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate - * @return \Psr\Http\Message\ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { - if (in_array($request->getMethod(), $this->complex)) { - parse_str(file_get_contents('php://input'), $body); - - $request = $request->withParsedBody($body); + if (! in_array($request->getMethod(), $this->complex)) + { + return $handler->handle($request); } - return $delegate->process($request); + $file = file_get_contents('php://input'); + + parse_str($file, $body); + + return $request->withParsedBody($body); } } diff --git a/tests/Fixture/Middlewares/CorsMiddleware.php b/tests/Fixture/Middlewares/CorsMiddleware.php index 3b46d4cd..ce4caf92 100644 --- a/tests/Fixture/Middlewares/CorsMiddleware.php +++ b/tests/Fixture/Middlewares/CorsMiddleware.php @@ -2,44 +2,29 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; - -use Interop\Http\ServerMiddleware\DelegateInterface; - use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Server\MiddlewareInterface; /** - * CORS Middleware - * * @package Slytherin * @author Rougin Gutib */ -class CorsMiddleware implements \Interop\Http\ServerMiddleware\MiddlewareInterface +class CorsMiddleware implements MiddlewareInterface { - /** - * @var array - */ protected $allowed = array('*'); - /** - * @var array - */ protected $methods = array('GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'); - /** - * Process an incoming server request and return a response, optionally delegating - * to the next middleware component to create the response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate - * @return \Psr\Http\Message\ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { - $response = $request->getMethod() === 'OPTIONS' ? new Response : $delegate->process($request); + $isOptions = $request->getMethod() === 'OPTIONS'; + + $response = $isOptions ? new Response : $handler->handle($request); $response = $response->withHeader('Access-Control-Allow-Origin', $this->allowed); + $response = $response->withHeader('Access-Control-Allow-Methods', $this->methods); return $response; diff --git a/tests/Fixture/Middlewares/EmptyMiddleware.php b/tests/Fixture/Middlewares/EmptyMiddleware.php index c4a06721..d8a7d580 100644 --- a/tests/Fixture/Middlewares/EmptyMiddleware.php +++ b/tests/Fixture/Middlewares/EmptyMiddleware.php @@ -2,31 +2,24 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Interop\Http\ServerMiddleware\DelegateInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Server\MiddlewareInterface; /** - * Empty Middleware - * * @package Slytherin * @author Rougin Gutib */ -class EmptyMiddleware +class EmptyMiddleware implements MiddlewareInterface { - /** - * @param \Psr\Http\Message\ResponseInterface $request - * @param \Psr\Http\Message\ServerRequestInterface $response - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) { return $next($request, $response); } - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { - return $delegate->process($request); + return $handler->handle($request); } } diff --git a/tests/Fixture/Middlewares/FirstMiddleware.php b/tests/Fixture/Middlewares/FirstMiddleware.php index a7038f7d..b09ec1e0 100644 --- a/tests/Fixture/Middlewares/FirstMiddleware.php +++ b/tests/Fixture/Middlewares/FirstMiddleware.php @@ -6,19 +6,11 @@ use Psr\Http\Message\ServerRequestInterface; /** - * First Middleware - * * @package Slytherin * @author Rougin Gutib */ class FirstMiddleware { - /** - * @param \Psr\Http\Message\ResponseInterface $request - * @param \Psr\Http\Message\ServerRequestInterface $response - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) { $response->getBody()->write('First!'); diff --git a/tests/Fixture/Middlewares/InteropMiddleware.php b/tests/Fixture/Middlewares/InteropMiddleware.php index b07d5ea9..e863fbad 100644 --- a/tests/Fixture/Middlewares/InteropMiddleware.php +++ b/tests/Fixture/Middlewares/InteropMiddleware.php @@ -2,27 +2,16 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; - use Interop\Http\ServerMiddleware\DelegateInterface; +use Interop\Http\ServerMiddleware\MiddlewareInterface; +use Psr\Http\Message\ServerRequestInterface; /** - * Interop Middleware - * * @package Slytherin * @author Rougin Gutib */ -class InteropMiddleware implements \Interop\Http\ServerMiddleware\MiddlewareInterface +class InteropMiddleware implements MiddlewareInterface { - /** - * Process an incoming server request and return a response, optionally delegating - * to the next middleware component to create the response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate - * @return \Psr\Http\Message\ResponseInterface - */ public function process(ServerRequestInterface $request, DelegateInterface $delegate) { $response = $delegate->process($request)->withStatus(500); diff --git a/tests/Fixture/Middlewares/LastMiddleware.php b/tests/Fixture/Middlewares/LastMiddleware.php index c9560f64..40216eee 100644 --- a/tests/Fixture/Middlewares/LastMiddleware.php +++ b/tests/Fixture/Middlewares/LastMiddleware.php @@ -2,43 +2,26 @@ namespace Rougin\Slytherin\Fixture\Middlewares; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Http\Response; /** - * Last Middleware - * * @package Slytherin * @author Rougin Gutib */ -class LastMiddleware implements MiddlewareInterface +class LastMiddleware { - /** - * @param \Psr\Http\Message\ResponseInterface $request - * @param \Psr\Http\Message\ServerRequestInterface $response - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) { - if ($response->getBody() == '') { + if ($response->getBody() == '') + { $response->getBody()->write('Loaded with middleware'); - } else { + } + else + { $response->getBody()->write(' Last!'); } return $response; } - - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - $response = new Response; - - $response->getBody()->write('Loaded with middleware'); - - return $response; - } } diff --git a/tests/Fixture/Middlewares/SecondMiddleware.php b/tests/Fixture/Middlewares/SecondMiddleware.php index 84bf6264..9f18477e 100644 --- a/tests/Fixture/Middlewares/SecondMiddleware.php +++ b/tests/Fixture/Middlewares/SecondMiddleware.php @@ -6,19 +6,11 @@ use Psr\Http\Message\ServerRequestInterface; /** - * Second Middleware - * * @package Slytherin * @author Rougin Gutib */ class SecondMiddleware { - /** - * @param \Psr\Http\Message\ResponseInterface $request - * @param \Psr\Http\Message\ServerRequestInterface $response - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) { $response->getBody()->write(' Second!'); diff --git a/tests/Sample/Handlers/Cors.php b/tests/Sample/Handlers/Cors.php index 7d33bf79..641764bc 100644 --- a/tests/Sample/Handlers/Cors.php +++ b/tests/Sample/Handlers/Cors.php @@ -2,10 +2,10 @@ namespace Rougin\Slytherin\Sample\Handlers; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Server\MiddlewareInterface; /** * @package Slytherin @@ -17,71 +17,29 @@ class Cors implements MiddlewareInterface const ORIGIN = 'Access-Control-Allow-Origin'; - /** - * @var array - */ protected $allowed = array(); - /** - * @var array - */ protected $methods = array('GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'); - /** - * Initializes the middleware instance. - * - * @param array|null $allowed - * @param array|null $methods - */ public function __construct(array $allowed = null, array $methods = null) { - $this->allowed($allowed === null ? array('*') : $allowed); + if (! $allowed) $allowed = array('*'); - $this->methods($methods === null ? $this->methods : $methods); + if (! $methods) $methods = $this->methods; + + $this->allowed = $allowed; + + $this->methods = $methods; } - /** - * Process an incoming server request and return a response, optionally - * delegating to the next middleware component to create the response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate - * @return \Psr\Http\Message\ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { $options = $request->getMethod() === 'OPTIONS'; - $response = $options ? new Response : $delegate->process($request); + $response = $options ? new Response : $handler->handle($request); $response = $response->withHeader(self::ORIGIN, $this->allowed); return $response->withHeader(self::METHODS, (array) $this->methods); } - - /** - * Sets the allowed URLS. - * - * @param array $allowed - * @return self - */ - public function allowed($allowed) - { - $this->allowed = $allowed; - - return $this; - } - - /** - * Sets the allowed HTTP methods. - * - * @param array $methods - * @return self - */ - public function methods($methods) - { - $this->methods = $methods; - - return $this; - } } diff --git a/tests/Sample/Handlers/Interop.php b/tests/Sample/Handlers/Interop.php new file mode 100644 index 00000000..704ffc24 --- /dev/null +++ b/tests/Sample/Handlers/Interop.php @@ -0,0 +1,23 @@ + + */ +class Interop implements MiddlewareInterface +{ + public function process(ServerRequestInterface $request, DelegateInterface $delegate) + { + $response = $delegate->process($request); + + $response->getBody()->write('From interop!'); + + return $response; + } +} diff --git a/tests/Sample/Handlers/Parsed/Request.php b/tests/Sample/Handlers/Parsed/Request.php index 1f80872e..78a485fd 100644 --- a/tests/Sample/Handlers/Parsed/Request.php +++ b/tests/Sample/Handlers/Parsed/Request.php @@ -2,9 +2,9 @@ namespace Rougin\Slytherin\Sample\Handlers\Parsed; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Server\MiddlewareInterface; /** * @package Slytherin @@ -12,12 +12,12 @@ */ class Request implements MiddlewareInterface { - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { $data = array('name' => 'Slytherin'); $request = $request->withParsedBody($data); - return $delegate->process($request); + return $handler->handle($request); } } diff --git a/tests/Sample/Handlers/Parsed/Response.php b/tests/Sample/Handlers/Parsed/Response.php index 3d84fb6a..75da95a0 100644 --- a/tests/Sample/Handlers/Parsed/Response.php +++ b/tests/Sample/Handlers/Parsed/Response.php @@ -2,9 +2,9 @@ namespace Rougin\Slytherin\Sample\Handlers\Parsed; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Server\MiddlewareInterface; /** * @package Slytherin @@ -12,9 +12,9 @@ */ class Response implements MiddlewareInterface { - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { - $response = $delegate->process($request); + $response = $handler->handle($request); $response->getBody()->write('From middleware!'); diff --git a/tests/Sample/Handlers/ToJson.php b/tests/Sample/Handlers/ToJson.php index 06dd32c9..d8085155 100644 --- a/tests/Sample/Handlers/ToJson.php +++ b/tests/Sample/Handlers/ToJson.php @@ -2,9 +2,9 @@ namespace Rougin\Slytherin\Sample\Handlers; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Server\MiddlewareInterface; /** * @package Slytherin @@ -12,9 +12,9 @@ */ class ToJson implements MiddlewareInterface { - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { - $response = $delegate->process($request); + $response = $handler->handle($request); return $response->withHeader('Content-Type', 'application/json'); } diff --git a/tests/Sample/Router.php b/tests/Sample/Router.php index 61576d66..f93274aa 100644 --- a/tests/Sample/Router.php +++ b/tests/Sample/Router.php @@ -2,9 +2,7 @@ namespace Rougin\Slytherin\Sample; -use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Routing\Router as Slytherin; -use Rougin\Slytherin\Server\HandlerInterface; /** * @package Slytherin @@ -28,28 +26,33 @@ public function routes($parsed = false) $this->get('without-slash', 'Hello@string'); + $fn = function ($request, $handler) + { + $response = $handler->handle($request); + + $response->getBody()->write('From callable middleware!'); + + return $response; + }; + + $this->get('middleware', 'Hello@response', $fn); + $this->get('/', 'Home@index'); - $fn = function () + $this->get('/callable', function () { return 'Welcome call!'; - }; - - $this->get('/callable', $fn); + }); - $fn = function ($name, $age) + $this->get('/call/{name}/{age}', function ($name, $age) { return 'Welcome ' . $name . ', ' . $age . '!'; - }; - - $this->get('/call/{name}/{age}', $fn); + }); - $fn = function ($name) + $this->get('/call/{name}', function ($name) { return 'Welcome ' . $name . '!'; - }; - - $this->get('/call/{name}', $fn); + }); $this->get('/param', 'Home@param'); @@ -57,16 +60,9 @@ public function routes($parsed = false) $this->get('/handler/param', 'Hello@param'); - $fn = function (ServerRequestInterface $request, HandlerInterface $handler) - { - $response = $handler->handle($request); - - $response->getBody()->write('From callable middleware!'); - - return $response; - }; + $interop = 'Rougin\Slytherin\Sample\Handlers\Interop'; - $this->get('middleware', 'Hello@response', $fn); + $this->get('interop', 'Hello@response', $interop); return parent::routes($parsed); } diff --git a/tests/Sample/SampleTest.php b/tests/Sample/SampleTest.php index 30271fee..d06eedc9 100644 --- a/tests/Sample/SampleTest.php +++ b/tests/Sample/SampleTest.php @@ -220,6 +220,20 @@ public function test_callable_middleware_changing_the_response_parameter() $this->builder->make()->run(); } + /** + * @runInSeparateProcess + * + * @return void + */ + public function test_interop_middleware_changing_the_response_parameter() + { + $this->builder->setUrl('GET', '/interop'); + + $this->expectOutputString('From interop!'); + + $this->builder->make()->run(); + } + /** * @runInSeparateProcess * From 431a1fb822d55097b12373a917925e79f0141bed Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 21:28:37 +0800 Subject: [PATCH 11/46] Remove Doublepass class --- src/Server/Callback.php | 10 +++++---- src/Server/Doublepass.php | 46 --------------------------------------- 2 files changed, 6 insertions(+), 50 deletions(-) delete mode 100644 src/Server/Doublepass.php diff --git a/src/Server/Callback.php b/src/Server/Callback.php index 6d3c460c..0b2fcf0b 100644 --- a/src/Server/Callback.php +++ b/src/Server/Callback.php @@ -53,24 +53,26 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl $middleware = new $middleware; } - if (! $this->isDoublePass($middleware)) + if ($this->isSinglePass($middleware)) { return $middleware($request, $handler); } + $response = $this->response; + $fn = function ($request) use ($handler) { return $handler->handle($request); }; - return $middleware($request, $this->response, $fn); + return $middleware($request, $response, $fn); } /** * @param mixed $item * @return boolean */ - protected function isDoublePass($item) + protected function isSinglePass($item) { if ($item instanceof \Closure) { @@ -82,6 +84,6 @@ protected function isDoublePass($item) $object = new \ReflectionMethod($item, '__invoke'); } - return count($object->getParameters()) === 3; + return count($object->getParameters()) === 2; } } diff --git a/src/Server/Doublepass.php b/src/Server/Doublepass.php deleted file mode 100644 index eaf4b00e..00000000 --- a/src/Server/Doublepass.php +++ /dev/null @@ -1,46 +0,0 @@ - - * @codeCoverageIgnore - */ -class Doublepass -{ - /** - * @var callable - */ - protected $handler; - - /** - * @var \Psr\Http\Message\ResponseInterface - */ - protected $response; - - /** - * @param callable $handler - * @param \Psr\Http\Message\ResponseInterface $response - */ - public function __construct($handler, ResponseInterface $response) - { - $this->handler = $handler; - - $this->response = $response; - } - - /** - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function handle(ServerRequestInterface $request) - { - return call_user_func($this->handler, $request, $this->response); - } -} From 571b5c367f78290a5d5840c359f69a48f7c1903b Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sun, 3 Dec 2023 21:40:01 +0800 Subject: [PATCH 12/46] Change details in FastRouteRouter, PhrouteRouter --- src/Routing/FastRouteRouter.php | 1 - src/Routing/PhrouteRouter.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Routing/FastRouteRouter.php b/src/Routing/FastRouteRouter.php index fb73f8a8..98e091c7 100644 --- a/src/Routing/FastRouteRouter.php +++ b/src/Routing/FastRouteRouter.php @@ -10,7 +10,6 @@ * FastRoute Router * * A simple implementation of router that is built on top of FastRoute. - * NOTE: To be removed in v1.0.0. Must conform to one Router only. * * https://github.com/nikic/FastRoute * diff --git a/src/Routing/PhrouteRouter.php b/src/Routing/PhrouteRouter.php index 499c8130..70920f21 100644 --- a/src/Routing/PhrouteRouter.php +++ b/src/Routing/PhrouteRouter.php @@ -8,7 +8,6 @@ * Phroute Router * * A simple implementation of router that is built on top of Phroute. - * NOTE: To be removed in v1.0.0. Must conform to one Router only. * * https://github.com/mrjgreen/phroute * From 8cb8e2f5cf19f182aa8f2c3fa859b6c91a3c26f0 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Mon, 4 Dec 2023 00:43:18 +0800 Subject: [PATCH 13/46] Change details in CHANGELOG.md, Collection --- CHANGELOG.md | 3 --- src/Component/Collection.php | 40 +++++++++++++++++++----------------- src/System.php | 2 ++ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a5aae63..9d0a756b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,9 +25,6 @@ All notable changes to `Slytherin` will be documented in this file. - Backward compatibility for `TwigRenderer::render` (as of `~3.0`) - Resolving typehinted routes for third-party routers -### Removed -- `__call` methods in `Router`, use the defined methods instead (e.g., `get()`, `post()`, etc.) - ## [0.9.6](https://github.com/rougin/slytherin/compare/v0.9.5...v0.9.6) - 2023-11-16 ### Added diff --git a/src/Component/Collection.php b/src/Component/Collection.php index a2d9360b..8dc75728 100644 --- a/src/Component/Collection.php +++ b/src/Component/Collection.php @@ -9,6 +9,7 @@ use Rougin\Slytherin\Debug\ErrorHandlerInterface; use Rougin\Slytherin\Middleware\DispatcherInterface as MiddlewareDispatcher; use Rougin\Slytherin\Routing\DispatcherInterface as RouteDispatcher; +use Rougin\Slytherin\System; /** * Component Collection @@ -33,9 +34,7 @@ class Collection extends VanillaContainer */ public function getContainer() { - $interface = 'Psr\Container\ContainerInterface'; - - return (is_a($this->container, $interface)) ? $this->container : $this; + return (is_a($this->container, System::CONTAINER)) ? $this->container : $this; } /** @@ -59,7 +58,7 @@ public function setContainer(ContainerInterface $container) public function getDispatcher() { /** @var \Rougin\Slytherin\Routing\DispatcherInterface */ - return $this->get('Rougin\Slytherin\Routing\DispatcherInterface'); + return $this->get(System::DISPATCHER); } /** @@ -70,7 +69,7 @@ public function getDispatcher() */ public function setDispatcher(RouteDispatcher $dispatcher) { - $this->set('Rougin\Slytherin\Routing\DispatcherInterface', $dispatcher); + $this->set(System::DISPATCHER, $dispatcher); return $this; } @@ -105,12 +104,10 @@ public function setDebugger(ErrorHandlerInterface $debugger) */ public function getErrorHandler() { - $interface = 'Rougin\Slytherin\Debug\ErrorHandlerInterface'; - - if (! $this->getContainer()->has($interface)) return null; + if (! $this->getContainer()->has(System::ERREPORT)) return null; /** @var \Rougin\Slytherin\Debug\ErrorHandlerInterface */ - return $this->getContainer()->get((string) $interface); + return $this->getContainer()->get(System::ERREPORT); } /** @@ -119,9 +116,9 @@ public function getErrorHandler() * @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $errorHandler * @return self */ - public function setErrorHandler(\Rougin\Slytherin\Debug\ErrorHandlerInterface $errorHandler) + public function setErrorHandler(ErrorHandlerInterface $errorHandler) { - $this->set('Rougin\Slytherin\Debug\ErrorHandlerInterface', $errorHandler); + $this->set(System::ERREPORT, $errorHandler); return $this; } @@ -133,8 +130,9 @@ public function setErrorHandler(\Rougin\Slytherin\Debug\ErrorHandlerInterface $e */ public function getHttp() { - $request = $this->get('Psr\Http\Message\ServerRequestInterface'); - $response = $this->get('Psr\Http\Message\ResponseInterface'); + $request = $this->get(System::SERVER_REQUEST); + + $response = $this->get(System::RESPONSE); return array($request, $response); } @@ -148,9 +146,9 @@ public function getHttp() */ public function setHttp(ServerRequestInterface $request, ResponseInterface $response) { - $this->set('Psr\Http\Message\ServerRequestInterface', $request); + $this->set(System::SERVER_REQUEST, $request); - $this->set('Psr\Http\Message\ResponseInterface', $response); + $this->set(System::RESPONSE, $response); return $this; } @@ -163,7 +161,7 @@ public function setHttp(ServerRequestInterface $request, ResponseInterface $resp public function getHttpRequest() { /** @var \Psr\Http\Message\ServerRequestInterface */ - return $this->get('Psr\Http\Message\ServerRequestInterface'); + return $this->get(System::SERVER_REQUEST); } /** @@ -174,7 +172,7 @@ public function getHttpRequest() */ public function setHttpRequest(ServerRequestInterface $request) { - $this->set('Psr\Http\Message\ServerRequestInterface', $request); + $this->set(System::SERVER_REQUEST, $request); return $this; } @@ -187,7 +185,7 @@ public function setHttpRequest(ServerRequestInterface $request) public function getHttpResponse() { /** @var \Psr\Http\Message\ResponseInterface */ - return $this->get('Psr\Http\Message\ResponseInterface'); + return $this->get(System::RESPONSE); } /** @@ -198,12 +196,14 @@ public function getHttpResponse() */ public function setHttpResponse(ResponseInterface $response) { - $this->set('Psr\Http\Message\ResponseInterface', $response); + $this->set(System::RESPONSE, $response); return $this; } /** + * TODO: Reimplement Middleware package. + * * Gets the middleware. * * @return \Rougin\Slytherin\Middleware\DispatcherInterface|null @@ -219,6 +219,8 @@ public function getMiddleware() } /** + * TODO: Reimplement Middleware package. + * * Sets the middleware. * * @param \Rougin\Slytherin\Middleware\DispatcherInterface $middleware diff --git a/src/System.php b/src/System.php index 889bb5d0..9037bdbc 100644 --- a/src/System.php +++ b/src/System.php @@ -10,6 +10,8 @@ class System { + const CONTAINER = 'Psr\Container\ContainerInterface'; + const DISPATCHER = 'Rougin\Slytherin\Routing\DispatcherInterface'; const ERREPORT = 'Rougin\Slytherin\Ereport\EreportInterface'; From 050f461c61d4a3248d2a3ecbb44417d75381b528 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Mon, 4 Dec 2023 14:32:25 +0800 Subject: [PATCH 14/46] Integrate "Server" back to "Middleware" --- phpstan.neon | 3 +- src/Component/Collection.php | 20 +- src/Middleware/CallableMiddlewareWrapper.php | 66 ----- src/Middleware/Delegate.php | 55 +--- src/Middleware/Dispatcher.php | 246 +----------------- src/Middleware/DispatcherInterface.php | 30 +-- src/Middleware/FinalResponse.php | 31 --- src/Middleware/HandlerInterface.php | 4 +- src/Middleware/MiddlewareIntegration.php | 14 + src/Middleware/StratigilityDispatcher.php | 59 ++--- src/Middleware/VanillaDelegate.php | 3 - src/Server/Dispatch.php | 30 +++ src/Server/DispatchInterface.php | 6 + src/Server/Handler.php | 10 + src/Server/Handlers/Handler030.php | 10 + src/Server/Handlers/Handler041.php | 9 + src/Server/Handlers/Handler050.php | 9 + src/Server/Handlers/Handler100.php | 9 + src/Server/Interop.php | 45 ++++ src/Server/Wrapper.php | 26 +- src/System/Endofline.php | 23 ++ tests/Application/ApplicationTest.php | 5 - tests/Middleware/DispatcherTest.php | 10 - tests/Middleware/DispatcherTestCases.php | 40 +-- .../Stratigility/MiddlewareTest.php | 40 +-- .../Middleware/StratigilityDispatcherTest.php | 13 +- tests/Middleware/Vanilla/MiddlewareTest.php | 72 ++--- 27 files changed, 293 insertions(+), 595 deletions(-) delete mode 100644 src/Middleware/CallableMiddlewareWrapper.php delete mode 100644 src/Middleware/FinalResponse.php create mode 100644 src/Server/Interop.php create mode 100644 src/System/Endofline.php diff --git a/phpstan.neon b/phpstan.neon index 6f1701ec..b7d24fcc 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,4 +4,5 @@ parameters: - src excludePaths: analyse: - - src/Server/Handlers \ No newline at end of file + - src/Server/Handlers + - src/Server/Interop.php \ No newline at end of file diff --git a/src/Component/Collection.php b/src/Component/Collection.php index 8dc75728..41418b6b 100644 --- a/src/Component/Collection.php +++ b/src/Component/Collection.php @@ -7,8 +7,8 @@ use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Container\VanillaContainer; use Rougin\Slytherin\Debug\ErrorHandlerInterface; -use Rougin\Slytherin\Middleware\DispatcherInterface as MiddlewareDispatcher; use Rougin\Slytherin\Routing\DispatcherInterface as RouteDispatcher; +use Rougin\Slytherin\Server\DispatchInterface as MiddlewareDispatcher; use Rougin\Slytherin\System; /** @@ -104,10 +104,10 @@ public function setDebugger(ErrorHandlerInterface $debugger) */ public function getErrorHandler() { - if (! $this->getContainer()->has(System::ERREPORT)) return null; + if (! $this->has(System::ERREPORT)) return null; /** @var \Rougin\Slytherin\Debug\ErrorHandlerInterface */ - return $this->getContainer()->get(System::ERREPORT); + return $this->get(System::ERREPORT); } /** @@ -206,16 +206,14 @@ public function setHttpResponse(ResponseInterface $response) * * Gets the middleware. * - * @return \Rougin\Slytherin\Middleware\DispatcherInterface|null + * @return \Rougin\Slytherin\Server\DispatchInterface|null */ public function getMiddleware() { - $interface = 'Rougin\Slytherin\Middleware\DispatcherInterface'; + if (! $this->has(System::MIDDLEWARE)) return null; - if (! $this->getContainer()->has($interface)) return null; - - /** @var \Rougin\Slytherin\Middleware\DispatcherInterface */ - return $this->getContainer()->get((string) $interface); + /** @var \Rougin\Slytherin\Server\DispatchInterface */ + return $this->get(System::MIDDLEWARE); } /** @@ -223,12 +221,12 @@ public function getMiddleware() * * Sets the middleware. * - * @param \Rougin\Slytherin\Middleware\DispatcherInterface $middleware + * @param \Rougin\Slytherin\Server\DispatchInterface $middleware * @return self */ public function setMiddleware(MiddlewareDispatcher $middleware) { - $this->set('Rougin\Slytherin\Middleware\DispatcherInterface', $middleware); + $this->set(System::MIDDLEWARE, $middleware); return $this; } diff --git a/src/Middleware/CallableMiddlewareWrapper.php b/src/Middleware/CallableMiddlewareWrapper.php deleted file mode 100644 index 3d1d33c4..00000000 --- a/src/Middleware/CallableMiddlewareWrapper.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @author Rasmus Schultz - */ -class CallableMiddlewareWrapper implements MiddlewareInterface -{ - /** - * @var callable - */ - protected $middleware; - - /** - * @var \Psr\Http\Message\ResponseInterface|null - */ - protected $response = null; - - /** - * Initializes the middleware instance. - * - * @param callable $middleware - * @param \Psr\Http\Message\ResponseInterface|null $response - */ - public function __construct($middleware, ResponseInterface $response = null) - { - $this->middleware = $middleware; - - $this->response = $response; - } - - /** - * Processes an incoming server request and return a response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate - * @return \Psr\Http\Message\ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - $middleware = $this->middleware; - - if (! $this->response instanceof ResponseInterface) - { - return $middleware($request, $delegate); - } - - $fn = function ($request) use ($delegate) - { - return $delegate->process($request); - }; - - return $middleware($request, $this->response, $fn); - } -} diff --git a/src/Middleware/Delegate.php b/src/Middleware/Delegate.php index 17321474..d5634220 100644 --- a/src/Middleware/Delegate.php +++ b/src/Middleware/Delegate.php @@ -2,64 +2,15 @@ namespace Rougin\Slytherin\Middleware; -use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\System\Handler; /** - * Delegate - * * Calls the callback with a specified HTTP request. + * NOTE: To be removed in v1.0.0. Use "Handler" instead. * * @package Slytherin * @author Rougin Gutib - * @author Rasmus Schultz */ -class Delegate implements HandlerInterface +class Delegate extends Handler { - /** - * @var callable - */ - protected $callback; - - /** - * Initializes the delegate instance. - * - * @param callable|null $callback - */ - public function __construct($callback = null) - { - $this->callback = $callback ?: array($this, 'response'); - } - - /** - * Dispatch the next available middleware and return the response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function process(ServerRequestInterface $request) - { - return call_user_func($this->callback, $request); - } - - /** - * Dispatch the next available middleware and return the response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request) - { - return $this->process($request); - } - - /** - * Returns an empty \Psr\Http\Message\ResponseInterface - * - * @return \Psr\Http\Message\ResponseInterface - */ - protected function response() - { - return new Response; - } } diff --git a/src/Middleware/Dispatcher.php b/src/Middleware/Dispatcher.php index c7a66df2..590dc220 100644 --- a/src/Middleware/Dispatcher.php +++ b/src/Middleware/Dispatcher.php @@ -2,12 +2,7 @@ namespace Rougin\Slytherin\Middleware; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Application; -use Rougin\Slytherin\Http\Response; -use Rougin\Slytherin\Middleware\Delegate; +use Rougin\Slytherin\Server\Dispatch; /** * Dispatcher @@ -18,243 +13,6 @@ * @author Rougin Gutib * @author Rasmus Schultz */ -class Dispatcher implements DispatcherInterface +class Dispatcher extends Dispatch { - const SINGLE_PASS = false; - - const DOUBLE_PASS = true; - - /** - * @var \Psr\Http\Message\ResponseInterface - */ - protected $response; - - /** - * @var array - */ - protected $stack = array(); - - /** - * Initializes the dispatcher instance. - * - * @param array $stack - * @param \Psr\Http\Message\ResponseInterface|null $response - */ - public function __construct(array $stack = array(), ResponseInterface $response = null) - { - $this->response = $response ?: new Response; - - $this->stack = $stack; - } - - /** - * Processes the specified middlewares from stack. - * NOTE: To be removed in v1.0.0. Use MiddlewareInterface::process instead. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Psr\Http\Message\ResponseInterface $response - * @param array $stack - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $stack = array()) - { - $this->response = $response; - - $this->stack = (empty($this->stack)) ? $stack : $this->stack; - - /** @var \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface|string */ - $last = end($this->stack); - - $last = $this->callback($last, $response); - - array_pop($this->stack); - - return $this->process($request, new Delegate($last)); - } - - /** - * Returns the listing of middlewares included. - * NOTE: To be removed in v1.0.0. Use $this->stack() instead. - * - * @return array - */ - public function getStack() - { - return $this->stack(); - } - - /** - * Processes an incoming server request and return a response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate - * @return \Psr\Http\Message\ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - $original = $this->stack; - - $fn = function ($request) use ($delegate) - { - return $delegate->process($request); - }; - - $this->push($fn); - - foreach ($this->stack as $index => $middleware) - { - if (is_string($middleware)) $middleware = new $middleware; - - /** @var \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface $middleware */ - $this->stack[$index] = $this->transform($middleware); - } - - $resolved = $this->resolve(0); - - array_pop($this->stack); - - $this->stack = $original; - - return $resolved->process($request); - } - - /** - * Adds a new middleware or a list of middlewares in the stack. - * - * @param array|\Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface|string $middleware - * @return self - */ - public function push($middleware) - { - if (is_array($middleware)) - { - $this->stack = array_merge($this->stack, $middleware); - - return $this; - } - - array_push($this->stack, $middleware); - - return $this; - } - - /** - * Returns the listing of middlewares included. - * - * @return array - */ - public function stack() - { - return $this->stack; - } - - /** - * Checks if the approach of the specified middleware is either single or double pass. - * - * @param \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface $middleware - * @return boolean - */ - protected function approach($middleware) - { - if (is_a($middleware, 'Closure')) - { - $object = new \ReflectionFunction($middleware); - - return count($object->getParameters()) === 2; - } - - $class = (string) get_class($middleware); - - $object = new \ReflectionMethod($class, '__invoke'); - - return count($object->getParameters()) === 2; - } - - /** - * Returns the middleware as a single pass callable. - * - * @param \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface|string $middleware - * @param \Psr\Http\Message\ResponseInterface $response - * @return callable - */ - protected function callback($middleware, ResponseInterface $response) - { - if (is_string($middleware)) - { - /** @var \Interop\Http\ServerMiddleware\MiddlewareInterface */ - $middleware = new $middleware; - } - - $fn = function ($request, $next = null) use ($middleware) - { - /** @var callable $middleware */ - return $middleware($request, $next); - }; - - if ($this->approach($middleware) === self::SINGLE_PASS) - { - $fn = function ($request, $next = null) use ($middleware, $response) - { - /** @var callable $middleware */ - return $middleware($request, $response, $next); - }; - } - - return $fn; - } - - /** - * Resolves the whole stack through its index. - * - * @param integer $index - * @return \Interop\Http\ServerMiddleware\DelegateInterface - */ - protected function resolve($index) - { - $stack = $this->stack; - - if (! isset($this->stack[$index])) - { - return new Delegate(null); - } - - /** @var \Interop\Http\ServerMiddleware\MiddlewareInterface */ - $item = $stack[(integer) $index]; - - $next = $this->resolve($index + 1); - - $fn = function ($request) use ($item, $next) - { - return $item->process($request, $next); - }; - - return new Delegate($fn); - } - - /** - * Transforms the specified middleware into a PSR-15 middleware. - * - * @param \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface $middleware - * @param boolean $wrappable - * @return \Interop\Http\ServerMiddleware\MiddlewareInterface - */ - protected function transform($middleware, $wrappable = true) - { - $response = null; - - $psr = 'Interop\Http\ServerMiddleware\MiddlewareInterface'; - - if (is_a($middleware, $psr)) return $middleware; - - $approach = $this->approach($middleware); - - $singlePass = $approach === self::SINGLE_PASS; - - if ($singlePass) $response = $this->response; - - $wrapper = new CallableMiddlewareWrapper($middleware, $response); - - /** @var \Interop\Http\ServerMiddleware\MiddlewareInterface */ - return $wrappable ? $wrapper : $middleware; - } } diff --git a/src/Middleware/DispatcherInterface.php b/src/Middleware/DispatcherInterface.php index bb3fcc58..4e02633d 100644 --- a/src/Middleware/DispatcherInterface.php +++ b/src/Middleware/DispatcherInterface.php @@ -2,8 +2,7 @@ namespace Rougin\Slytherin\Middleware; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Server\DispatchInterface; /** * Dispatcher Interface @@ -13,31 +12,6 @@ * @package Slytherin * @author Rougin Gutib */ -interface DispatcherInterface extends MiddlewareInterface +interface DispatcherInterface extends DispatchInterface { - /** - * Processes the specified middlewares from stack. - * NOTE: To be removed in v1.0.0. Use MiddlewareInterface::process instead. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Psr\Http\Message\ResponseInterface $response - * @param array $stack - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $stack = array()); - - /** - * Adds a new middleware or a list of middlewares in the stack. - * - * @param array|\Interop\Http\ServerMiddleware\MiddlewareInterface|callable|string $middleware - * @return self - */ - public function push($middleware); - - /** - * Returns the listing of middlewares included. - * - * @return array - */ - public function stack(); } diff --git a/src/Middleware/FinalResponse.php b/src/Middleware/FinalResponse.php deleted file mode 100644 index 9a1cccaa..00000000 --- a/src/Middleware/FinalResponse.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ -class FinalResponse -{ - /** - * Initializes the response instance. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param callable|null $next - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, $next = null) - { - return new Response; - } -} diff --git a/src/Middleware/HandlerInterface.php b/src/Middleware/HandlerInterface.php index 1ca45bc8..a74059bb 100644 --- a/src/Middleware/HandlerInterface.php +++ b/src/Middleware/HandlerInterface.php @@ -2,7 +2,7 @@ namespace Rougin\Slytherin\Middleware; -use Interop\Http\ServerMiddleware\DelegateInterface; +use Rougin\Slytherin\Server\HandlerInterface as Slytherin; /** * Handler Interface @@ -12,6 +12,6 @@ * @package Slytherin * @author Rougin Gutib */ -interface HandlerInterface extends DelegateInterface +interface HandlerInterface extends Slytherin { } diff --git a/src/Middleware/MiddlewareIntegration.php b/src/Middleware/MiddlewareIntegration.php index dfae997c..769b4f97 100644 --- a/src/Middleware/MiddlewareIntegration.php +++ b/src/Middleware/MiddlewareIntegration.php @@ -7,6 +7,7 @@ use Rougin\Slytherin\Integration\IntegrationInterface; use Rougin\Slytherin\Server\Dispatch; use Rougin\Slytherin\System; +use Zend\Stratigility\MiddlewarePipe; /** * Middleware Integration @@ -41,6 +42,19 @@ public function define(ContainerInterface $container, Configuration $config) $dispatch = new Dispatch($stack); + $empty = $this->preferred === null; + + $hasZend = class_exists('Zend\Stratigility\MiddlewarePipe'); + + $wantZend = $this->preferred === 'stratigility'; + + if (($empty || $wantZend) && $hasZend) + { + $pipe = new MiddlewarePipe; + + $dispatch = new StratigilityDispatcher($pipe); + } + return $container->set($middleware, $dispatch); } } diff --git a/src/Middleware/StratigilityDispatcher.php b/src/Middleware/StratigilityDispatcher.php index 8a2e0022..78d9cdfd 100644 --- a/src/Middleware/StratigilityDispatcher.php +++ b/src/Middleware/StratigilityDispatcher.php @@ -2,10 +2,9 @@ namespace Rougin\Slytherin\Middleware; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Server\Interop; use Zend\Stratigility\MiddlewarePipe; /** @@ -22,53 +21,33 @@ class StratigilityDispatcher extends Dispatcher /** * @var \Zend\Stratigility\MiddlewarePipe */ - protected $pipeline; + protected $zend; /** - * @var \Psr\Http\Message\ResponseInterface + * @param \Zend\Stratigility\MiddlewarePipe $pipe + * @param mixed[] $stack */ - protected $response; - - /** - * @var array - */ - protected $stack = array(); - - /** - * Initializes the dispatcher instance. - * - * @param \Zend\Stratigility\MiddlewarePipe $pipeline - * @param array $stack - * @param \Psr\Http\Message\ResponseInterface|null $response - */ - public function __construct(MiddlewarePipe $pipeline, array $stack = array(), ResponseInterface $response = null) + public function __construct(MiddlewarePipe $pipe, $stack = array()) { - $this->pipeline = $pipeline; - - $this->response = $response ?: new Response; + parent::__construct($stack); - $this->stack = $stack; + $this->zend = $pipe; } /** - * Processes an incoming server request and return a response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\ServerMiddleware\DelegateInterface $delegate + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Server\HandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - $wrap = class_exists('Zend\Stratigility\Middleware\ErrorHandler'); + // public function process(ServerRequestInterface $request, HandlerInterface $handler) + // { + // foreach ($this->getStack() as $item) + // { + // $this->zend->pipe($item); + // } - foreach ($this->stack as $middleware) - { - if (is_string($middleware)) $middleware = new $middleware; + // $next = Interop::get($handler); - /** @var \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface $middleware */ - $this->pipeline->pipe($this->transform($middleware, $wrap)); - } - - return $this->pipeline->__invoke($request, $this->response, $delegate); - } + // return $this->zend->process($request, $next); + // } } diff --git a/src/Middleware/VanillaDelegate.php b/src/Middleware/VanillaDelegate.php index f98b9532..27081997 100644 --- a/src/Middleware/VanillaDelegate.php +++ b/src/Middleware/VanillaDelegate.php @@ -3,14 +3,11 @@ namespace Rougin\Slytherin\Middleware; /** - * Delegate - * * Calls the callback with a specified HTTP request. * NOTE: To be removed in v1.0.0. Use "Middleware\Delegate" instead. * * @package Slytherin * @author Rougin Gutib - * @author Rasmus Schultz */ class VanillaDelegate extends Delegate { diff --git a/src/Server/Dispatch.php b/src/Server/Dispatch.php index e95b976f..8d7ddac2 100644 --- a/src/Server/Dispatch.php +++ b/src/Server/Dispatch.php @@ -51,6 +51,26 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl return $handler->handle($request); } + /** + * @param mixed $middleware + * @return self + */ + public function push($middleware) + { + if (! is_array($middleware)) + { + $item = $this->transform($middleware); + + array_push($this->stack, $item); + + return $this; + } + + foreach ($middleware as $item) $this->push($item); + + return $this; + } + /** * @param mixed[] $stack * @return self @@ -69,6 +89,16 @@ public function setStack($stack) return $this; } + /** + * NOTE: To be removed in v1.0.0. Use $this->getStack() instead. + * + * @return \Rougin\Slytherin\Server\MiddlewareInterface[] + */ + public function stack() + { + return $this->getStack(); + } + /** * @param mixed $middleware * @return \Rougin\Slytherin\Server\MiddlewareInterface diff --git a/src/Server/DispatchInterface.php b/src/Server/DispatchInterface.php index b7e5938f..355ff883 100644 --- a/src/Server/DispatchInterface.php +++ b/src/Server/DispatchInterface.php @@ -13,6 +13,12 @@ interface DispatchInterface extends MiddlewareInterface */ public function getStack(); + /** + * @param mixed $middleware + * @return self + */ + public function push($middleware); + /** * @param mixed[] $stack * @return self diff --git a/src/Server/Handler.php b/src/Server/Handler.php index 62d852da..7d6a5f25 100644 --- a/src/Server/Handler.php +++ b/src/Server/Handler.php @@ -7,6 +7,7 @@ /** * @package Slytherin * @author Rougin Gutib + * @codeCoverageIgnore */ class Handler implements HandlerInterface { @@ -36,6 +37,15 @@ public function __construct(array $stack, HandlerInterface $default) $this->stack = $stack; } + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request) + { + return $this->handle($request); + } + /** * @param \Psr\Http\Message\ServerRequestInterface $request * @return \Psr\Http\Message\ResponseInterface diff --git a/src/Server/Handlers/Handler030.php b/src/Server/Handlers/Handler030.php index bb9e1d18..ed669943 100644 --- a/src/Server/Handlers/Handler030.php +++ b/src/Server/Handlers/Handler030.php @@ -4,6 +4,7 @@ use Interop\Http\Middleware\DelegateInterface; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Server\HandlerInterface; /** @@ -26,6 +27,15 @@ public function __construct(HandlerInterface $handler) $this->handler = $handler; } + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request) + { + return $this->process($request); + } + /** * @param \Psr\Http\Message\RequestInterface $request * @return \Psr\Http\Message\ResponseInterface diff --git a/src/Server/Handlers/Handler041.php b/src/Server/Handlers/Handler041.php index ad55319b..b75cb9c2 100644 --- a/src/Server/Handlers/Handler041.php +++ b/src/Server/Handlers/Handler041.php @@ -26,6 +26,15 @@ public function __construct(HandlerInterface $handler) $this->handler = $handler; } + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request) + { + return $this->process($request); + } + /** * @param \Psr\Http\Message\ServerRequestInterface $request * @return \Psr\Http\Message\ResponseInterface diff --git a/src/Server/Handlers/Handler050.php b/src/Server/Handlers/Handler050.php index e80b7397..c79b0956 100644 --- a/src/Server/Handlers/Handler050.php +++ b/src/Server/Handlers/Handler050.php @@ -26,6 +26,15 @@ public function __construct(HandlerInterface $handler) $this->handler = $handler; } + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request) + { + return $this->handle($request); + } + /** * @param \Psr\Http\Message\ServerRequestInterface $request * @return \Psr\Http\Message\ResponseInterface diff --git a/src/Server/Handlers/Handler100.php b/src/Server/Handlers/Handler100.php index 19db2fd1..2acc428b 100644 --- a/src/Server/Handlers/Handler100.php +++ b/src/Server/Handlers/Handler100.php @@ -27,6 +27,15 @@ public function __construct(HandlerInterface $handler) $this->handler = $handler; } + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request): ResponseInterface + { + return $this->handle($request); + } + /** * @param \Psr\Http\Message\ServerRequestInterface $request * @return \Psr\Http\Message\ResponseInterface diff --git a/src/Server/Interop.php b/src/Server/Interop.php new file mode 100644 index 00000000..c265bba9 --- /dev/null +++ b/src/Server/Interop.php @@ -0,0 +1,45 @@ + + * @codeCoverageIgnore + */ +class Interop +{ + /** + * @param \Rougin\Slytherin\Server\HandlerInterface $handler + * @return mixed + */ + public static function get(HandlerInterface $handler) + { + switch (Version::get()) + { + case '0.3.0': + $handler = new Handler030($handler); + + break; + case '0.4.1': + $handler = new Handler041($handler); + + break; + case '0.5.0': + $handler = new Handler050($handler); + + break; + case '1.0.0': + $handler = new Handler100($handler); + + break; + } + + return $handler; + } +} \ No newline at end of file diff --git a/src/Server/Wrapper.php b/src/Server/Wrapper.php index f18248ce..ed338246 100644 --- a/src/Server/Wrapper.php +++ b/src/Server/Wrapper.php @@ -2,10 +2,6 @@ namespace Rougin\Slytherin\Server; -use Rougin\Slytherin\Server\Handlers\Handler030; -use Rougin\Slytherin\Server\Handlers\Handler041; -use Rougin\Slytherin\Server\Handlers\Handler050; -use Rougin\Slytherin\Server\Handlers\Handler100; use Psr\Http\Message\ServerRequestInterface; /** @@ -47,27 +43,7 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl $middleware = new $middleware; } - // @codeCoverageIgnoreStart - switch (Version::get()) - { - case '0.3.0': - $next = new Handler030($handler); - - break; - case '0.4.1': - $next = new Handler041($handler); - - break; - case '0.5.0': - $next = new Handler050($handler); - - break; - default: - $next = new Handler100($handler); - - break; - } - // @codeCoverageIgnoreEnd + $next = Interop::get($handler); /** @phpstan-ignore-next-line */ return $middleware->process($request, $next); diff --git a/src/System/Endofline.php b/src/System/Endofline.php new file mode 100644 index 00000000..2ed1efea --- /dev/null +++ b/src/System/Endofline.php @@ -0,0 +1,23 @@ + + */ +class Endofline implements HandlerInterface +{ + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request) + { + return new Response; + } +} diff --git a/tests/Application/ApplicationTest.php b/tests/Application/ApplicationTest.php index cb19f48a..f3c1c60a 100644 --- a/tests/Application/ApplicationTest.php +++ b/tests/Application/ApplicationTest.php @@ -42,11 +42,6 @@ protected function doSetUp() 'Rougin\Slytherin\Fixture\Components\SingleComponent', ); - if (class_exists('Zend\Stratigility\MiddlewarePipe')) - { - $components[] = 'Rougin\Slytherin\Fixture\Components\MiddlewareComponent'; - } - $container = new \Rougin\Slytherin\IoC\Vanilla\Container; $globals = $GLOBALS; diff --git a/tests/Middleware/DispatcherTest.php b/tests/Middleware/DispatcherTest.php index 65e2d701..2a761c6e 100644 --- a/tests/Middleware/DispatcherTest.php +++ b/tests/Middleware/DispatcherTest.php @@ -3,26 +3,16 @@ namespace Rougin\Slytherin\Middleware; /** - * Dispatcher Test - * * @package Slytherin * @author Rougin Gutib */ class DispatcherTest extends DispatcherTestCases { /** - * Sets up the middleware dispatcher instance. - * * @return void */ protected function doSetUp() { - $interface = 'Interop\Http\ServerMiddleware\MiddlewareInterface'; - - $message = 'http-interop/http-middleware (v0.4.0) is not installed.'; - - interface_exists($interface) || $this->markTestSkipped($message); - $this->dispatcher = new Dispatcher; } } diff --git a/tests/Middleware/DispatcherTestCases.php b/tests/Middleware/DispatcherTestCases.php index 3831fa7d..17541c69 100644 --- a/tests/Middleware/DispatcherTestCases.php +++ b/tests/Middleware/DispatcherTestCases.php @@ -3,6 +3,8 @@ namespace Rougin\Slytherin\Middleware; use Rougin\Slytherin\Http\ServerRequest; +use Rougin\Slytherin\Server\Wrapper; +use Rougin\Slytherin\System\Endofline; use Rougin\Slytherin\Testcase; /** @@ -14,7 +16,7 @@ class DispatcherTestCases extends Testcase { /** - * @var \Rougin\Slytherin\Middleware\DispatcherInterface + * @var \Rougin\Slytherin\Server\Dispatch */ protected $dispatcher; @@ -61,18 +63,20 @@ public function testProcessMethodWithSinglePassCallback() $time = (integer) time(); - $this->dispatcher->push(function ($request, $next) use ($time) + $fn = function ($request, $next) use ($time) { $response = $next($request); return $response->withHeader('X-Slytherin', $time); - }); + }; - $expected = array((integer) $time); + $this->dispatcher->push($fn); - $result = $this->process()->getHeader('X-Slytherin'); + $expected = array($time); - $this->assertEquals($expected, $result); + $actual = $this->process()->getHeader('X-Slytherin'); + + $this->assertEquals($expected, $actual); } /** @@ -93,12 +97,14 @@ public function testProcessMethodWithDelagateInterfaceCallback() $this->markTestSkipped((string) $message); } - $this->dispatcher->push(function ($request, $delegate) + $fn = function ($request, $next) { - $response = $delegate->process($request); + $response = $next($request); return $response->withHeader('Content-Type', 'application/json'); - }); + }; + + $this->dispatcher->push($fn); $expected = array('application/json'); @@ -143,11 +149,11 @@ public function testProcessMethodWithString() */ public function testPushMethodWithArray() { - $expected = array('Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'); + $interop = 'Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'; - $expected[] = 'Rougin\Slytherin\Middleware\FinalResponse'; + $expected = array(new Wrapper($interop)); - $this->dispatcher->push($expected); + $this->dispatcher->push(array($interop)); $result = $this->dispatcher->stack(); @@ -163,13 +169,9 @@ public function testStackMethod() { $this->dispatcher->push('Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'); - $this->dispatcher->push('Rougin\Slytherin\Middleware\FinalResponse'); - - $expected = (integer) 2; - - $result = $this->dispatcher->stack(); + $actual = $this->dispatcher->stack(); - $this->assertCount($expected, $result); + $this->assertCount(1, $actual); } /** @@ -189,6 +191,6 @@ protected function process() $request = new ServerRequest($server); - return $this->dispatcher->process($request, new Delegate); + return $this->dispatcher->process($request, new Endofline); } } diff --git a/tests/Middleware/Stratigility/MiddlewareTest.php b/tests/Middleware/Stratigility/MiddlewareTest.php index 7e30b23e..5677a912 100644 --- a/tests/Middleware/Stratigility/MiddlewareTest.php +++ b/tests/Middleware/Stratigility/MiddlewareTest.php @@ -2,29 +2,34 @@ namespace Rougin\Slytherin\Middleware\Stratigility; +use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Http\ServerRequest; +use Rougin\Slytherin\Middleware\Stratigility\Middleware; +use Rougin\Slytherin\System\Endofline; +use Rougin\Slytherin\Testcase; +use Zend\Stratigility\MiddlewarePipe; + /** - * Stratigility Middleware Test - * * @package Slytherin * @author Rougin Gutib */ -class MiddlewareTest extends \Rougin\Slytherin\Testcase +class MiddlewareTest extends Testcase { /** - * Tests __invoke() method - * * @return void */ public function testInvokeMethod() { - if (! class_exists('Zend\Stratigility\MiddlewarePipe')) { + if (! class_exists('Zend\Stratigility\MiddlewarePipe')) + { $this->markTestSkipped('Zend Stratigility is not installed.'); } - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = '/'; - $_SERVER['SERVER_NAME'] = 'localhost'; - $_SERVER['SERVER_PORT'] = '8000'; + $server = array(); + $server['REQUEST_METHOD'] = 'GET'; + $server['REQUEST_URI'] = '/'; + $server['SERVER_NAME'] = 'localhost'; + $server['SERVER_PORT'] = '8000'; $stack = array(); @@ -32,14 +37,17 @@ public function testInvokeMethod() $stack[] = 'Rougin\Slytherin\Fixture\Middlewares\SecondMiddleware'; $stack[] = 'Rougin\Slytherin\Fixture\Middlewares\LastMiddleware'; - $pipeline = new \Zend\Stratigility\MiddlewarePipe; - $middleware = new \Rougin\Slytherin\Middleware\Stratigility\Middleware($pipeline); + $pipeline = new MiddlewarePipe; + $middleware = new Middleware($pipeline, $stack); + + $request = new ServerRequest($server); + $response = new Response; - $request = new \Rougin\Slytherin\Http\ServerRequest($_SERVER); - $response = new \Rougin\Slytherin\Http\Response; + $expected = 'First! Second! Last!'; - $response = $middleware($request, $response, $stack); + $response = $middleware->process($request, new Endofline); + $actual = (string) $response->getBody(); - $this->assertEquals('First! Second! Last!', (string) $response->getBody()); + $this->assertEquals($expected, $actual); } } diff --git a/tests/Middleware/StratigilityDispatcherTest.php b/tests/Middleware/StratigilityDispatcherTest.php index 08bba6c4..56f41505 100644 --- a/tests/Middleware/StratigilityDispatcherTest.php +++ b/tests/Middleware/StratigilityDispatcherTest.php @@ -5,8 +5,6 @@ use Zend\Stratigility\MiddlewarePipe; /** - * Stratigility Dispatcher Test - * * @package Slytherin * @author Rougin Gutib */ @@ -19,12 +17,13 @@ class StratigilityDispatcherTest extends DispatcherTestCases */ protected function doSetUp() { - $class = (string) 'Zend\Stratigility\MiddlewarePipe'; + if (! class_exists('Zend\Stratigility\MiddlewarePipe')) + { + $this->markTestSkipped('Zend Stratigility is not installed'); + } - $message = 'Zend Stratigility is not installed'; + $pipe = new MiddlewarePipe; - class_exists($class) || $this->markTestSkipped($message); - - $this->dispatcher = new StratigilityDispatcher(new MiddlewarePipe); + $this->dispatcher = new StratigilityDispatcher($pipe); } } diff --git a/tests/Middleware/Vanilla/MiddlewareTest.php b/tests/Middleware/Vanilla/MiddlewareTest.php index 9bc58a26..78a645ec 100644 --- a/tests/Middleware/Vanilla/MiddlewareTest.php +++ b/tests/Middleware/Vanilla/MiddlewareTest.php @@ -2,70 +2,72 @@ namespace Rougin\Slytherin\Middleware\Vanilla; +use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Http\ServerRequest; +use Rougin\Slytherin\Middleware\Vanilla\Middleware; +use Rougin\Slytherin\System\Endofline; +use Rougin\Slytherin\Testcase; + /** - * Stratigility Middleware Test - * * @package Slytherin * @author Rougin Gutib */ -class MiddlewareTest extends \Rougin\Slytherin\Testcase +class MiddlewareTest extends Testcase { /** - * Tests __invoke() method - * * @return void */ public function testInvokeMethod() { - if (! interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) { - $this->markTestSkipped('Interop Middleware is not installed.'); - } - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = '/'; - $_SERVER['SERVER_NAME'] = 'localhost'; - $_SERVER['SERVER_PORT'] = '8000'; + $server = array(); + $server['REQUEST_METHOD'] = 'GET'; + $server['REQUEST_URI'] = '/'; + $server['SERVER_NAME'] = 'localhost'; + $server['SERVER_PORT'] = '8000'; - $middleware = new \Rougin\Slytherin\Middleware\Vanilla\Middleware; - $request = new \Rougin\Slytherin\Http\ServerRequest($_SERVER); - $response = new \Rougin\Slytherin\Http\Response; + $middleware = new Middleware; + $request = new ServerRequest($server); + $response = new Response; - $middleware->push(function ($request, $next = null) { + $fn = function ($request, $next) + { return $next($request); - }); + }; + + $middleware->push($fn); $middleware->push('Rougin\Slytherin\Fixture\Middlewares\FirstMiddleware'); $middleware->push('Rougin\Slytherin\Fixture\Middlewares\SecondMiddleware'); $middleware->push('Rougin\Slytherin\Fixture\Middlewares\LastMiddleware'); - $response = $middleware($request, $response, $middleware->getStack()); + $expected = 'First! Second! Last!'; + + $response = $middleware->process($request, new Endofline); + $actual = (string) $response->getBody(); - $this->assertEquals('First! Second! Last!', (string) $response->getBody()); + $this->assertEquals($expected, $actual); } /** - * Tests process() method - * * @return void */ public function testProcessMethod() { - if (! interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) { - $this->markTestSkipped('Interop Middleware is not installed.'); - } + $server = array(); + $server['REQUEST_METHOD'] = 'GET'; + $server['REQUEST_URI'] = '/'; + $server['SERVER_NAME'] = 'localhost'; + $server['SERVER_PORT'] = '8000'; - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = '/'; - $_SERVER['SERVER_NAME'] = 'localhost'; - $_SERVER['SERVER_PORT'] = '8000'; - - $middleware = new \Rougin\Slytherin\Middleware\Vanilla\Middleware; - $request = new \Rougin\Slytherin\Http\ServerRequest($_SERVER); - $response = new \Rougin\Slytherin\Http\Response; + $middleware = new Middleware; + $request = new ServerRequest($server); $middleware->push('Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'); - $middleware->push('Rougin\Slytherin\Middleware\FinalResponse'); - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $middleware($request, $response)); + $expected = 'Psr\Http\Message\ResponseInterface'; + + $actual = $middleware->process($request, new Endofline); + + $this->assertInstanceOf($expected, $actual); } } From 91c8a5a00d4cabbc185c41fbab8dde919bca0083 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Mon, 4 Dec 2023 16:46:26 +0800 Subject: [PATCH 15/46] Reimplement zend/stratigility package --- src/Middleware/StratigilityDispatcher.php | 36 ++++++++++++---- src/Server/Callback.php | 12 ++++++ src/Server/Interop.php | 42 +++++++++++++++++-- src/Server/Wrapper.php | 7 +--- tests/Application/ApplicationTestCases.php | 6 +-- .../Application/IntegrationInterfaceTest.php | 23 ++++------ tests/Fixture/Middlewares/FinalMiddleware.php | 20 +++++++++ tests/Fixture/Middlewares/LastMiddleware.php | 9 +--- tests/Sample/Router.php | 4 +- 9 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 tests/Fixture/Middlewares/FinalMiddleware.php diff --git a/src/Middleware/StratigilityDispatcher.php b/src/Middleware/StratigilityDispatcher.php index 78d9cdfd..0b99c28e 100644 --- a/src/Middleware/StratigilityDispatcher.php +++ b/src/Middleware/StratigilityDispatcher.php @@ -3,8 +3,10 @@ namespace Rougin\Slytherin\Middleware; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Http\Response; use Rougin\Slytherin\Server\HandlerInterface; use Rougin\Slytherin\Server\Interop; +use Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory; use Zend\Stratigility\MiddlewarePipe; /** @@ -39,15 +41,31 @@ public function __construct(MiddlewarePipe $pipe, $stack = array()) * @param \Rougin\Slytherin\Server\HandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ - // public function process(ServerRequestInterface $request, HandlerInterface $handler) - // { - // foreach ($this->getStack() as $item) - // { - // $this->zend->pipe($item); - // } + public function process(ServerRequestInterface $request, HandlerInterface $handler) + { + $response = new Response; + + $factory = new CallableMiddlewareWrapperFactory($response); + + $this->zend->setCallableMiddlewareDecorator($factory); + + foreach ($this->getStack() as $item) + { + // Convert the handler into a callable ----------------- + $fn = function ($request, $response, $next) use ($item) + { + return $item->process($request, new Interop($next)); + }; + // ----------------------------------------------------- - // $next = Interop::get($handler); + $this->zend->pipe($fn); + } - // return $this->zend->process($request, $next); - // } + $next = Interop::getHandler($handler); + + $zend = $this->zend; + + /** @phpstan-ignore-next-line */ + return $zend($request, $response, $next); + } } diff --git a/src/Server/Callback.php b/src/Server/Callback.php index 0b2fcf0b..942b4df8 100644 --- a/src/Server/Callback.php +++ b/src/Server/Callback.php @@ -36,6 +36,18 @@ public function __construct($middleware, ResponseInterface $response = null) $this->response = $response; } + /** + * Processes an incoming server request and return a response. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Server\HandlerInterface $handler + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke($request, $handler) + { + return $this->process($request, $handler); + } + /** * Processes an incoming server request and return a response. * diff --git a/src/Server/Interop.php b/src/Server/Interop.php index c265bba9..db89b4f6 100644 --- a/src/Server/Interop.php +++ b/src/Server/Interop.php @@ -2,6 +2,7 @@ namespace Rougin\Slytherin\Server; +use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Server\Handlers\Handler030; use Rougin\Slytherin\Server\Handlers\Handler041; use Rougin\Slytherin\Server\Handlers\Handler050; @@ -12,13 +13,46 @@ * @author Rougin Gutib * @codeCoverageIgnore */ -class Interop +class Interop implements HandlerInterface { /** - * @param \Rougin\Slytherin\Server\HandlerInterface $handler + * @var mixed + */ + protected $handler; + + /** + * @param mixed $handler + */ + public function __construct($handler) + { + $this->handler = $handler; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request) + { + return $this->handle($request); + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request) + { + $handler = $this->handler; + + return $handler($request); + } + + /** + * @param mixed $handler * @return mixed */ - public static function get(HandlerInterface $handler) + public static function getHandler($handler) { switch (Version::get()) { @@ -42,4 +76,4 @@ public static function get(HandlerInterface $handler) return $handler; } -} \ No newline at end of file +} diff --git a/src/Server/Wrapper.php b/src/Server/Wrapper.php index ed338246..4a51ff74 100644 --- a/src/Server/Wrapper.php +++ b/src/Server/Wrapper.php @@ -38,12 +38,9 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl { $middleware = $this->middleware; - if (is_string($middleware)) - { - $middleware = new $middleware; - } + if (is_string($middleware)) $middleware = new $middleware; - $next = Interop::get($handler); + $next = Interop::getHandler($handler); /** @phpstan-ignore-next-line */ return $middleware->process($request, $next); diff --git a/tests/Application/ApplicationTestCases.php b/tests/Application/ApplicationTestCases.php index ef91162b..4c35f651 100644 --- a/tests/Application/ApplicationTestCases.php +++ b/tests/Application/ApplicationTestCases.php @@ -90,10 +90,6 @@ public function testHandleMethodWithHttp401Response() */ public function testHandleMethodWithMiddleware() { - $interface = 'Interop\Http\ServerMiddleware\MiddlewareInterface'; - - interface_exists($interface) || $this->markTestSkipped('PSR-15 is not installed.'); - $request = $this->request('GET', '/middleware'); $expected = (string) 'Loaded with middleware'; @@ -280,7 +276,7 @@ protected function request($method, $uri, $data = array(), $server = array()) */ protected function router() { - $middleware = 'Rougin\Slytherin\Fixture\Middlewares\LastMiddleware'; + $middleware = 'Rougin\Slytherin\Fixture\Middlewares\FinalMiddleware'; $router = new Router; diff --git a/tests/Application/IntegrationInterfaceTest.php b/tests/Application/IntegrationInterfaceTest.php index 02c0c597..dc09557d 100644 --- a/tests/Application/IntegrationInterfaceTest.php +++ b/tests/Application/IntegrationInterfaceTest.php @@ -2,6 +2,8 @@ namespace Rougin\Slytherin\Application; +use Rougin\Slytherin\Integration\Configuration; + /** * Integration Interface Test * @@ -22,24 +24,17 @@ protected function doSetUp() $integrations[] = 'Rougin\Slytherin\Debug\ErrorHandlerIntegration'; $integrations[] = 'Rougin\Slytherin\Http\HttpIntegration'; $integrations[] = 'Rougin\Slytherin\Routing\RoutingIntegration'; + $integrations[] = 'Rougin\Slytherin\Middleware\MiddlewareIntegration'; + $integrations[] = 'Rougin\Slytherin\Template\RendererIntegration'; + $integrations[] = 'Rougin\Slytherin\Integration\ConfigurationIntegration'; - $config = new \Rougin\Slytherin\Integration\Configuration; - - $config->set('app.router', $this->router()); - - if (interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) - { - $integrations[] = 'Rougin\Slytherin\Middleware\MiddlewareIntegration'; - - $middlewares = array('Rougin\Slytherin\Fixture\Middlewares\EmptyMiddleware'); + $config = new Configuration; - $config->set('app.middlewares', $middlewares); - } + $router = $this->router(); - $app = new \Rougin\Slytherin\Application; + $config->set('app.router', $router); - $app->integrate('Rougin\Slytherin\Template\RendererIntegration'); - $app->integrate('Rougin\Slytherin\Integration\ConfigurationIntegration'); + $app = new Application; $this->application = $app->integrate($integrations, $config); } diff --git a/tests/Fixture/Middlewares/FinalMiddleware.php b/tests/Fixture/Middlewares/FinalMiddleware.php new file mode 100644 index 00000000..e0cf731e --- /dev/null +++ b/tests/Fixture/Middlewares/FinalMiddleware.php @@ -0,0 +1,20 @@ + + */ +class FinalMiddleware +{ + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) + { + $response->getBody()->write('Loaded with middleware'); + + return $response; + } +} diff --git a/tests/Fixture/Middlewares/LastMiddleware.php b/tests/Fixture/Middlewares/LastMiddleware.php index 40216eee..24a13683 100644 --- a/tests/Fixture/Middlewares/LastMiddleware.php +++ b/tests/Fixture/Middlewares/LastMiddleware.php @@ -13,14 +13,7 @@ class LastMiddleware { public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) { - if ($response->getBody() == '') - { - $response->getBody()->write('Loaded with middleware'); - } - else - { - $response->getBody()->write(' Last!'); - } + $response->getBody()->write(' Last!'); return $response; } diff --git a/tests/Sample/Router.php b/tests/Sample/Router.php index f93274aa..2c7cb123 100644 --- a/tests/Sample/Router.php +++ b/tests/Sample/Router.php @@ -26,9 +26,9 @@ public function routes($parsed = false) $this->get('without-slash', 'Hello@string'); - $fn = function ($request, $handler) + $fn = function ($request, $next) { - $response = $handler->handle($request); + $response = $next($request); $response->getBody()->write('From callable middleware!'); From 20850a0cd0df7b10c1151cf2b4ba0f1e4500baaa Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Mon, 4 Dec 2023 16:56:55 +0800 Subject: [PATCH 16/46] Remove unused __invoke in Callback --- src/Server/Callback.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Server/Callback.php b/src/Server/Callback.php index 942b4df8..0b2fcf0b 100644 --- a/src/Server/Callback.php +++ b/src/Server/Callback.php @@ -36,18 +36,6 @@ public function __construct($middleware, ResponseInterface $response = null) $this->response = $response; } - /** - * Processes an incoming server request and return a response. - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Rougin\Slytherin\Server\HandlerInterface $handler - * @return \Psr\Http\Message\ResponseInterface - */ - public function __invoke($request, $handler) - { - return $this->process($request, $handler); - } - /** * Processes an incoming server request and return a response. * From 5cc307037d76475d9053309e2ac13da26ccdbf19 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 09:16:04 +0800 Subject: [PATCH 17/46] Add backward compatibility for zend/stratigility in v2.0, v3.0 --- .github/workflows/build.yml | 22 +----- src/Middleware/StratigilityDispatcher.php | 83 ++++++++++++++++++++--- src/Server/Interop.php | 69 +++++++++++++++---- tests/Middleware/DispatcherTestCases.php | 33 --------- 4 files changed, 130 insertions(+), 77 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae1abaed..4d6176ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,27 +39,7 @@ jobs: - name: Install third-party packages not for PHP 5.3 if: ${{ matrix.php-versions != '5.3' }} - run: composer require filp/whoops league/container nikic/fast-route phroute/phroute rdlowrey/auryn twig/twig zendframework/zend-diactoros http-interop/http-middleware:^0.4.1 --dev - - - name: Install third-party packages for PHP 7.1 - if: ${{ matrix.php-versions == '7.1' }} - run: composer require zendframework/zend-stratigility:~2.0 --dev - - - name: Install third-party packages for PHP 7.2 - if: ${{ matrix.php-versions == '7.2' }} - run: composer require zendframework/zend-stratigility:~2.0 --dev - - - name: Install third-party packages for PHP 7.3 - if: ${{ matrix.php-versions == '7.3' }} - run: composer require zendframework/zend-stratigility:~2.0 --dev - - - name: Install third-party packages for PHP 7.4 - if: ${{ matrix.php-versions == '7.4' }} - run: composer require zendframework/zend-stratigility:~2.0 --dev - - - name: Install third-party packages for PHP 8.1 - if: ${{ matrix.php-versions == '8.1' }} - run: composer require twig/twig --dev + run: composer require filp/whoops league/container nikic/fast-route phroute/phroute rdlowrey/auryn twig/twig zendframework/zend-diactoros zendframework/zend-stratigility http-interop/http-middleware:^0.4.1 --dev - name: Run test suite run: vendor/bin/phpunit --coverage-clover=coverage.clover diff --git a/src/Middleware/StratigilityDispatcher.php b/src/Middleware/StratigilityDispatcher.php index 0b99c28e..1db0523b 100644 --- a/src/Middleware/StratigilityDispatcher.php +++ b/src/Middleware/StratigilityDispatcher.php @@ -2,11 +2,12 @@ namespace Rougin\Slytherin\Middleware; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Http\Response; use Rougin\Slytherin\Server\HandlerInterface; use Rougin\Slytherin\Server\Interop; -use Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory; +use Rougin\Slytherin\Server\MiddlewareInterface; use Zend\Stratigility\MiddlewarePipe; /** @@ -45,27 +46,89 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl { $response = new Response; - $factory = new CallableMiddlewareWrapperFactory($response); + $this->setFactory($response); - $this->zend->setCallableMiddlewareDecorator($factory); + /** @var class-string */ + $psr = 'Zend\Stratigility\Middleware\CallableMiddlewareDecorator'; foreach ($this->getStack() as $item) { - // Convert the handler into a callable ----------------- - $fn = function ($request, $response, $next) use ($item) + $item = $this->setMiddleware($item); + + if (! $this->hasFactory()) { - return $item->process($request, new Interop($next)); - }; - // ----------------------------------------------------- + $class = new \ReflectionClass($psr); - $this->zend->pipe($fn); + $item = $class->newInstance($item); + } + + $this->zend->pipe($item); } - $next = Interop::getHandler($handler); + // Force version check to 1.0.0 if using v3.0 --- + $version = class_exists($psr) ? '1.0.0' : null; + // ---------------------------------------------- + + $next = Interop::getHandler($handler, $version); $zend = $this->zend; + if (class_exists($psr)) + { + /** @phpstan-ignore-next-line */ + return $zend->process($request, $next); + } + /** @phpstan-ignore-next-line */ return $zend($request, $response, $next); } + + /** + * @return boolean + */ + protected function hasFactory() + { + return class_exists('Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory'); + } + + /** + * @param \Psr\Http\Message\ResponseInterface $response + * @return void + */ + protected function setFactory(ResponseInterface $response) + { + if (! $this->hasFactory()) return; + + $factory = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory'; + + $class = new \ReflectionClass((string) $factory); + + $factory = $class->newInstance($response); + + $class = array($this->zend, 'setCallableMiddlewareDecorator'); + + call_user_func_array($class, array($factory)); + } + + /** + * @param \Rougin\Slytherin\Server\MiddlewareInterface $item + * @return callable + */ + protected function setMiddleware(MiddlewareInterface $item) + { + // Convert the handler into a callable if has a factory ---- + if ($this->hasFactory()) + { + return function ($request, $response, $next) use ($item) + { + return $item->process($request, new Interop($next)); + }; + } + // --------------------------------------------------------- + + return function ($request, $handler) use ($item) + { + return $item->process($request, new Interop($handler)); + }; + } } diff --git a/src/Server/Interop.php b/src/Server/Interop.php index db89b4f6..9729d2bb 100644 --- a/src/Server/Interop.php +++ b/src/Server/Interop.php @@ -15,6 +15,14 @@ */ class Interop implements HandlerInterface { + const VERSION_0_3_0 = 'Interop\Http\Middleware\DelegateInterface'; + + const VERSION_0_4_1 = 'Interop\Http\ServerMiddleware\DelegateInterface'; + + const VERSION_0_5_0 = 'Interop\Http\Server\RequestHandlerInterface'; + + const VERSION_1_0_0 = 'Psr\Http\Server\RequestHandlerInterface'; + /** * @var mixed */ @@ -43,37 +51,72 @@ public function __invoke(ServerRequestInterface $request) */ public function handle(ServerRequestInterface $request) { - $handler = $this->handler; + $version = Version::get(); - return $handler($request); + $method = 'handle'; + + if ($version === '0.3.0' || $version === '0.4.1') + { + $method = 'process'; + } + + $class = array($this->handler, $method); + + return call_user_func($class, $request); } /** - * @param mixed $handler + * @param mixed $handler + * @param string $version * @return mixed */ - public static function getHandler($handler) + public static function getHandler($handler, $version = null) { - switch (Version::get()) + switch ($version) { case '0.3.0': - $handler = new Handler030($handler); + return new Handler030($handler); - break; case '0.4.1': - $handler = new Handler041($handler); + return new Handler041($handler); - break; case '0.5.0': - $handler = new Handler050($handler); + return new Handler050($handler); - break; case '1.0.0': - $handler = new Handler100($handler); + return new Handler100($handler); + } - break; + if (self::exists($handler, self::VERSION_0_3_0)) + { + return new Handler030($handler); + } + + if (self::exists($handler, self::VERSION_0_4_1)) + { + return new Handler041($handler); + } + + if (self::exists($handler, self::VERSION_0_5_0)) + { + return new Handler050($handler); + } + + if (self::exists($handler, self::VERSION_1_0_0)) + { + return new Handler100($handler); } return $handler; } + + /** + * @param mixed $handler + * @param string $version + * @return boolean + */ + public static function exists($handler, $class) + { + return interface_exists($class) || is_a($handler, $class); + } } diff --git a/tests/Middleware/DispatcherTestCases.php b/tests/Middleware/DispatcherTestCases.php index 17541c69..3ad0ca4e 100644 --- a/tests/Middleware/DispatcherTestCases.php +++ b/tests/Middleware/DispatcherTestCases.php @@ -50,17 +50,6 @@ public function testProcessMethodWithDoublePassCallback() */ public function testProcessMethodWithSinglePassCallback() { - $stratigility = 'Rougin\Slytherin\Middleware\StratigilityDispatcher'; - - $wrapper = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapper'; - - if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) - { - $message = 'Stratigility\'s current installed version does not accept single pass middlewares'; - - $this->markTestSkipped((string) $message); - } - $time = (integer) time(); $fn = function ($request, $next) use ($time) @@ -86,17 +75,6 @@ public function testProcessMethodWithSinglePassCallback() */ public function testProcessMethodWithDelagateInterfaceCallback() { - $stratigility = 'Rougin\Slytherin\Middleware\StratigilityDispatcher'; - - $wrapper = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapper'; - - if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) - { - $message = 'Stratigility\'s current version does not accept delegates'; - - $this->markTestSkipped((string) $message); - } - $fn = function ($request, $next) { $response = $next($request); @@ -120,17 +98,6 @@ public function testProcessMethodWithDelagateInterfaceCallback() */ public function testProcessMethodWithString() { - $stratigility = 'Rougin\Slytherin\Middleware\StratigilityDispatcher'; - - $wrapper = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapper'; - - if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) - { - $message = 'Stratigility\'s current version does not accept PSR-15 middlewares'; - - $this->markTestSkipped((string) $message); - } - $interop = 'Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'; $this->dispatcher->push($interop); From d53609bf7d493e3a0aedaa96609566633114a410 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 09:44:11 +0800 Subject: [PATCH 18/46] Do not support zend/stratigility ~1.0 --- .github/workflows/build.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d6176ef..d4f7da00 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,23 @@ jobs: - name: Install third-party packages not for PHP 5.3 if: ${{ matrix.php-versions != '5.3' }} - run: composer require filp/whoops league/container nikic/fast-route phroute/phroute rdlowrey/auryn twig/twig zendframework/zend-diactoros zendframework/zend-stratigility http-interop/http-middleware:^0.4.1 --dev + run: composer require filp/whoops league/container nikic/fast-route phroute/phroute rdlowrey/auryn twig/twig zendframework/zend-diactoros http-interop/http-middleware:^0.4.1 --dev + + - name: Install third-party packages for PHP 7.1 + if: ${{ matrix.php-versions == '7.1' }} + run: composer require zendframework/zend-stratigility --dev + + - name: Install third-party packages for PHP 7.2 + if: ${{ matrix.php-versions == '7.2' }} + run: composer require zendframework/zend-stratigility --dev + + - name: Install third-party packages for PHP 7.3 + if: ${{ matrix.php-versions == '7.3' }} + run: composer require zendframework/zend-stratigility --dev + + - name: Install third-party packages for PHP 7.4 + if: ${{ matrix.php-versions == '7.4' }} + run: composer require zendframework/zend-stratigility --dev - name: Run test suite run: vendor/bin/phpunit --coverage-clover=coverage.clover From 430980a60b34993e7289998e0944c61d3683a03e Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 10:35:00 +0800 Subject: [PATCH 19/46] Allow zend/stratigility ~1.0 except single pass middlewares --- .github/workflows/build.yml | 16 ++++++ src/Middleware/StratigilityDispatcher.php | 70 +++++++++++++++-------- src/Server/Doublepass.php | 52 +++++++++++++++++ src/Server/Interop.php | 4 +- tests/Middleware/DispatcherTestCases.php | 26 +++++++++ 5 files changed, 142 insertions(+), 26 deletions(-) create mode 100644 src/Server/Doublepass.php diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d4f7da00..966b954a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,6 +41,22 @@ jobs: if: ${{ matrix.php-versions != '5.3' }} run: composer require filp/whoops league/container nikic/fast-route phroute/phroute rdlowrey/auryn twig/twig zendframework/zend-diactoros http-interop/http-middleware:^0.4.1 --dev + - name: Install third-party packages for PHP 5.4 + if: ${{ matrix.php-versions == '5.4' }} + run: composer require zendframework/zend-stratigility --dev + + - name: Install third-party packages for PHP 5.5 + if: ${{ matrix.php-versions == '5.5' }} + run: composer require zendframework/zend-stratigility --dev + + - name: Install third-party packages for PHP 5.6 + if: ${{ matrix.php-versions == '5.6' }} + run: composer require zendframework/zend-stratigility --dev + + - name: Install third-party packages for PHP 7.0 + if: ${{ matrix.php-versions == '7.0' }} + run: composer require zendframework/zend-stratigility --dev + - name: Install third-party packages for PHP 7.1 if: ${{ matrix.php-versions == '7.1' }} run: composer require zendframework/zend-stratigility --dev diff --git a/src/Middleware/StratigilityDispatcher.php b/src/Middleware/StratigilityDispatcher.php index 1db0523b..a6d8b7ca 100644 --- a/src/Middleware/StratigilityDispatcher.php +++ b/src/Middleware/StratigilityDispatcher.php @@ -5,6 +5,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Server\Doublepass; use Rougin\Slytherin\Server\HandlerInterface; use Rougin\Slytherin\Server\Interop; use Rougin\Slytherin\Server\MiddlewareInterface; @@ -37,6 +38,22 @@ public function __construct(MiddlewarePipe $pipe, $stack = array()) $this->zend = $pipe; } + /** + * @return boolean + */ + public function hasFactory() + { + return class_exists('Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory'); + } + + /** + * @return boolean + */ + public function hasPsr() + { + return class_exists('Zend\Stratigility\Middleware\CallableMiddlewareDecorator'); + } + /** * @param \Psr\Http\Message\ServerRequestInterface $request * @param \Rougin\Slytherin\Server\HandlerInterface $handler @@ -48,32 +65,28 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl $this->setFactory($response); - /** @var class-string */ - $psr = 'Zend\Stratigility\Middleware\CallableMiddlewareDecorator'; - foreach ($this->getStack() as $item) { $item = $this->setMiddleware($item); - if (! $this->hasFactory()) + if (! $this->hasFactory() && $this->hasPsr()) { - $class = new \ReflectionClass($psr); - - $item = $class->newInstance($item); + $item = $this->setPsrMiddleware($item); } + /** @phpstan-ignore-next-line */ $this->zend->pipe($item); } // Force version check to 1.0.0 if using v3.0 --- - $version = class_exists($psr) ? '1.0.0' : null; + $version = $this->hasPsr() ? '1.0.0' : null; // ---------------------------------------------- $next = Interop::getHandler($handler, $version); $zend = $this->zend; - if (class_exists($psr)) + if ($this->hasPsr()) { /** @phpstan-ignore-next-line */ return $zend->process($request, $next); @@ -83,14 +96,6 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl return $zend($request, $response, $next); } - /** - * @return boolean - */ - protected function hasFactory() - { - return class_exists('Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory'); - } - /** * @param \Psr\Http\Message\ResponseInterface $response * @return void @@ -99,12 +104,14 @@ protected function setFactory(ResponseInterface $response) { if (! $this->hasFactory()) return; + /** @var class-string */ $factory = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory'; $class = new \ReflectionClass((string) $factory); $factory = $class->newInstance($response); + /** @var callable */ $class = array($this->zend, 'setCallableMiddlewareDecorator'); call_user_func_array($class, array($factory)); @@ -116,19 +123,34 @@ protected function setFactory(ResponseInterface $response) */ protected function setMiddleware(MiddlewareInterface $item) { - // Convert the handler into a callable if has a factory ---- - if ($this->hasFactory()) + if ($this->hasPsr()) { - return function ($request, $response, $next) use ($item) + return function ($request, $handler) use ($item) { - return $item->process($request, new Interop($next)); + return $item->process($request, new Interop($handler)); }; } - // --------------------------------------------------------- - return function ($request, $handler) use ($item) + return function ($request, $response, $next) use ($item) { - return $item->process($request, new Interop($handler)); + /** @var callable $next */ + $handle = new Doublepass($next, $response); + + return $item->process($request, $handle); }; } + + /** + * @param callable $item + * @return object + */ + protected function setPsrMiddleware($item) + { + /** @var class-string */ + $psr = 'Zend\Stratigility\Middleware\CallableMiddlewareDecorator'; + + $class = new \ReflectionClass($psr); + + return $class->newInstance($item); + } } diff --git a/src/Server/Doublepass.php b/src/Server/Doublepass.php new file mode 100644 index 00000000..adec6d0c --- /dev/null +++ b/src/Server/Doublepass.php @@ -0,0 +1,52 @@ + + */ +class Doublepass implements HandlerInterface +{ + /** + * @var callable + */ + protected $handler; + + /** + * @var \Psr\Http\Message\ResponseInterface + */ + protected $response; + + /** + * @param callable $handler + * @param \Psr\Http\Message\ResponseInterface $response + */ + public function __construct($handler, ResponseInterface $response) + { + $this->handler = $handler; + + $this->response = $response; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request) + { + return $this->handle($request); + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request) + { + return call_user_func($this->handler, $request, $this->response); + } +} diff --git a/src/Server/Interop.php b/src/Server/Interop.php index 9729d2bb..8a48e842 100644 --- a/src/Server/Interop.php +++ b/src/Server/Interop.php @@ -66,8 +66,8 @@ public function handle(ServerRequestInterface $request) } /** - * @param mixed $handler - * @param string $version + * @param mixed $handler + * @param string|null $version * @return mixed */ public static function getHandler($handler, $version = null) diff --git a/tests/Middleware/DispatcherTestCases.php b/tests/Middleware/DispatcherTestCases.php index 3ad0ca4e..93c5655e 100644 --- a/tests/Middleware/DispatcherTestCases.php +++ b/tests/Middleware/DispatcherTestCases.php @@ -50,6 +50,19 @@ public function testProcessMethodWithDoublePassCallback() */ public function testProcessMethodWithSinglePassCallback() { + $class = 'Rougin\Slytherin\Middleware\StratigilityDispatcher'; + + if (is_a($this->dispatcher, $class)) + { + /** @var \Rougin\Slytherin\Middleware\StratigilityDispatcher */ + $zend = $this->dispatcher; + + if (! $zend->hasPsr() && ! $zend->hasFactory()) + { + $this->markTestSkipped('Current Stratigility version does not support single pass callbacks'); + } + } + $time = (integer) time(); $fn = function ($request, $next) use ($time) @@ -75,6 +88,19 @@ public function testProcessMethodWithSinglePassCallback() */ public function testProcessMethodWithDelagateInterfaceCallback() { + $class = 'Rougin\Slytherin\Middleware\StratigilityDispatcher'; + + if (is_a($this->dispatcher, $class)) + { + /** @var \Rougin\Slytherin\Middleware\StratigilityDispatcher */ + $zend = $this->dispatcher; + + if (! $zend->hasPsr() && ! $zend->hasFactory()) + { + $this->markTestSkipped('Current Stratigility version does not support single pass callbacks'); + } + } + $fn = function ($request, $next) { $response = $next($request); From 3ebc02db4975f3b329e7a56c5d3557a29213ed23 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 10:39:27 +0800 Subject: [PATCH 20/46] Fix issue in php 5.6, 7.0 --- .github/workflows/build.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 966b954a..0b54345a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,14 +49,6 @@ jobs: if: ${{ matrix.php-versions == '5.5' }} run: composer require zendframework/zend-stratigility --dev - - name: Install third-party packages for PHP 5.6 - if: ${{ matrix.php-versions == '5.6' }} - run: composer require zendframework/zend-stratigility --dev - - - name: Install third-party packages for PHP 7.0 - if: ${{ matrix.php-versions == '7.0' }} - run: composer require zendframework/zend-stratigility --dev - - name: Install third-party packages for PHP 7.1 if: ${{ matrix.php-versions == '7.1' }} run: composer require zendframework/zend-stratigility --dev From ea847173618d6764b5d93d461ae3d678441346be Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 10:44:38 +0800 Subject: [PATCH 21/46] Add @codeCoverageIgnore in StratigilityDispatcher --- src/Middleware/StratigilityDispatcher.php | 1 + src/Server/Doublepass.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Middleware/StratigilityDispatcher.php b/src/Middleware/StratigilityDispatcher.php index a6d8b7ca..7ec7b306 100644 --- a/src/Middleware/StratigilityDispatcher.php +++ b/src/Middleware/StratigilityDispatcher.php @@ -99,6 +99,7 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl /** * @param \Psr\Http\Message\ResponseInterface $response * @return void + * @codeCoverageIgnore */ protected function setFactory(ResponseInterface $response) { diff --git a/src/Server/Doublepass.php b/src/Server/Doublepass.php index adec6d0c..90da5ef0 100644 --- a/src/Server/Doublepass.php +++ b/src/Server/Doublepass.php @@ -8,6 +8,7 @@ /** * @package Slytherin * @author Rougin Gutib + * @codeCoverageIgnore */ class Doublepass implements HandlerInterface { From 13d14d2a2fece66630c34ed407cdae7729285cf3 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 11:41:50 +0800 Subject: [PATCH 22/46] Move "Server" back to "Middleware" --- phpstan.neon | 4 +- src/Component/Collection.php | 14 +- src/{Server => Middleware}/Callback.php | 6 +- src/Middleware/Delegate.php | 2 - src/Middleware/Dispatcher.php | 142 ++++++++++++++++- src/Middleware/DispatcherInterface.php | 20 ++- src/{Server => Middleware}/Doublepass.php | 2 +- src/{Server => Middleware}/Handler.php | 12 +- src/Middleware/HandlerInterface.php | 9 +- .../Handlers/Handler030.php | 8 +- .../Handlers/Handler041.php | 8 +- .../Handlers/Handler050.php | 8 +- .../Handlers/Handler100.php | 10 +- src/{Server => Middleware}/Interop.php | 38 +++-- src/Middleware/MiddlewareIntegration.php | 3 +- src/Middleware/MiddlewareInterface.php | 13 +- src/Middleware/StratigilityDispatcher.php | 15 +- src/{Server => Middleware}/Version.php | 2 +- src/{Server => Middleware}/Wrapper.php | 6 +- src/Server/Dispatch.php | 146 ------------------ src/Server/DispatchInterface.php | 27 ---- src/Server/HandlerInterface.php | 18 --- src/Server/MiddlewareInterface.php | 19 --- src/System.php | 4 +- src/System/Endofline.php | 2 +- src/System/Handler.php | 2 +- tests/Application/AurynContainerTest.php | 8 +- tests/Application/ContainerInterfaceTest.php | 4 +- .../Middlewares/BodyParametersMiddleware.php | 4 +- tests/Fixture/Middlewares/CorsMiddleware.php | 4 +- tests/Fixture/Middlewares/EmptyMiddleware.php | 4 +- tests/Middleware/DispatcherTestCases.php | 20 ++- tests/Sample/Handlers/Cors.php | 4 +- tests/Sample/Handlers/Parsed/Request.php | 4 +- tests/Sample/Handlers/Parsed/Response.php | 4 +- tests/Sample/Handlers/ToJson.php | 4 +- tests/Sample/SampleTest.php | 6 + 37 files changed, 291 insertions(+), 315 deletions(-) rename src/{Server => Middleware}/Callback.php (91%) rename src/{Server => Middleware}/Doublepass.php (96%) rename src/{Server => Middleware}/Handler.php (78%) rename src/{Server => Middleware}/Handlers/Handler030.php (80%) rename src/{Server => Middleware}/Handlers/Handler041.php (80%) rename src/{Server => Middleware}/Handlers/Handler050.php (80%) rename src/{Server => Middleware}/Handlers/Handler100.php (81%) rename src/{Server => Middleware}/Interop.php (73%) rename src/{Server => Middleware}/Version.php (96%) rename src/{Server => Middleware}/Wrapper.php (84%) delete mode 100644 src/Server/Dispatch.php delete mode 100644 src/Server/DispatchInterface.php delete mode 100644 src/Server/HandlerInterface.php delete mode 100644 src/Server/MiddlewareInterface.php diff --git a/phpstan.neon b/phpstan.neon index b7d24fcc..bc1bfd76 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,5 +4,5 @@ parameters: - src excludePaths: analyse: - - src/Server/Handlers - - src/Server/Interop.php \ No newline at end of file + - src/Middleware/Handlers + - src/Middleware/Interop.php \ No newline at end of file diff --git a/src/Component/Collection.php b/src/Component/Collection.php index 41418b6b..00f72a69 100644 --- a/src/Component/Collection.php +++ b/src/Component/Collection.php @@ -7,8 +7,8 @@ use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Container\VanillaContainer; use Rougin\Slytherin\Debug\ErrorHandlerInterface; -use Rougin\Slytherin\Routing\DispatcherInterface as RouteDispatcher; -use Rougin\Slytherin\Server\DispatchInterface as MiddlewareDispatcher; +use Rougin\Slytherin\Middleware\DispatcherInterface as Middleware; +use Rougin\Slytherin\Routing\DispatcherInterface as Routing; use Rougin\Slytherin\System; /** @@ -67,7 +67,7 @@ public function getDispatcher() * @param \Rougin\Slytherin\Routing\DispatcherInterface $dispatcher * @return self */ - public function setDispatcher(RouteDispatcher $dispatcher) + public function setDispatcher(Routing $dispatcher) { $this->set(System::DISPATCHER, $dispatcher); @@ -206,13 +206,13 @@ public function setHttpResponse(ResponseInterface $response) * * Gets the middleware. * - * @return \Rougin\Slytherin\Server\DispatchInterface|null + * @return \Rougin\Slytherin\Middleware\DispatcherInterface|null */ public function getMiddleware() { if (! $this->has(System::MIDDLEWARE)) return null; - /** @var \Rougin\Slytherin\Server\DispatchInterface */ + /** @var \Rougin\Slytherin\Middleware\DispatcherInterface */ return $this->get(System::MIDDLEWARE); } @@ -221,10 +221,10 @@ public function getMiddleware() * * Sets the middleware. * - * @param \Rougin\Slytherin\Server\DispatchInterface $middleware + * @param \Rougin\Slytherin\Middleware\DispatcherInterface $middleware * @return self */ - public function setMiddleware(MiddlewareDispatcher $middleware) + public function setMiddleware(Middleware $middleware) { $this->set(System::MIDDLEWARE, $middleware); diff --git a/src/Server/Callback.php b/src/Middleware/Callback.php similarity index 91% rename from src/Server/Callback.php rename to src/Middleware/Callback.php index 0b2fcf0b..d0e3587b 100644 --- a/src/Server/Callback.php +++ b/src/Middleware/Callback.php @@ -1,6 +1,6 @@ - * @author Rasmus Schultz */ -class Dispatcher extends Dispatch +class Dispatcher implements DispatcherInterface { + /** + * @var \Rougin\Slytherin\Middleware\MiddlewareInterface[] + */ + protected $stack = array(); + + /** + * @var \Psr\Http\Message\ResponseInterface|null + */ + protected $response = null; + + /** + * @param mixed[] $stack + */ + public function __construct($stack = array()) + { + if ($stack) $this->setStack($stack); + } + + /** + * @return \Rougin\Slytherin\Middleware\MiddlewareInterface[] + */ + public function getStack() + { + return $this->stack; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Middleware\HandlerInterface $handler + * @return \Psr\Http\Message\ResponseInterface + */ + public function process(ServerRequestInterface $request, HandlerInterface $handler) + { + $stack = (array) $this->getStack(); + + $handler = new Handler($stack, $handler); + + return $handler->handle($request); + } + + /** + * @param mixed $middleware + * @return self + */ + public function push($middleware) + { + if (! is_array($middleware)) + { + $item = $this->transform($middleware); + + array_push($this->stack, $item); + + return $this; + } + + foreach ($middleware as $item) $this->push($item); + + return $this; + } + + /** + * @param mixed[] $stack + * @return self + */ + public function setStack($stack) + { + $result = array(); + + foreach ($stack as $item) + { + $result[] = $this->transform($item); + } + + $this->stack = $result; + + return $this; + } + + /** + * NOTE: To be removed in v1.0.0. Use $this->getStack() instead. + * + * @return \Rougin\Slytherin\Middleware\MiddlewareInterface[] + */ + public function stack() + { + return $this->getStack(); + } + + /** + * @param mixed $middleware + * @return \Rougin\Slytherin\Middleware\MiddlewareInterface + */ + protected function transform($middleware) + { + if ($middleware instanceof MiddlewareInterface) + { + return $middleware; + } + + // Set empty response for double pass middlewares --- + if (! $this->response) + { + $this->response = new Response; + } + // -------------------------------------------------- + + if ($this->isCallable($middleware)) + { + /** @var callable $middleware */ + return new Callback($middleware, $this->response); + } + + return new Wrapper($middleware); + } + + /** + * @param mixed $item + * @return boolean + */ + protected function isCallable($item) + { + /** @var object|string $item */ + $method = method_exists($item, '__invoke'); + + $callable = is_callable($item); + + $object = is_object($item); + + $closure = (! $object) || $item instanceof \Closure; + + return ($method || $callable) && $closure; + } } diff --git a/src/Middleware/DispatcherInterface.php b/src/Middleware/DispatcherInterface.php index 4e02633d..5f46435a 100644 --- a/src/Middleware/DispatcherInterface.php +++ b/src/Middleware/DispatcherInterface.php @@ -2,8 +2,6 @@ namespace Rougin\Slytherin\Middleware; -use Rougin\Slytherin\Server\DispatchInterface; - /** * Dispatcher Interface * @@ -12,6 +10,22 @@ * @package Slytherin * @author Rougin Gutib */ -interface DispatcherInterface extends DispatchInterface +interface DispatcherInterface extends MiddlewareInterface { + /** + * @return \Rougin\Slytherin\Middleware\MiddlewareInterface[] + */ + public function getStack(); + + /** + * @param mixed $middleware + * @return self + */ + public function push($middleware); + + /** + * @param mixed[] $stack + * @return self + */ + public function setStack($stack); } diff --git a/src/Server/Doublepass.php b/src/Middleware/Doublepass.php similarity index 96% rename from src/Server/Doublepass.php rename to src/Middleware/Doublepass.php index 90da5ef0..3da8d6b3 100644 --- a/src/Server/Doublepass.php +++ b/src/Middleware/Doublepass.php @@ -1,6 +1,6 @@ */ -interface HandlerInterface extends Slytherin +interface HandlerInterface { + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request); } diff --git a/src/Server/Handlers/Handler030.php b/src/Middleware/Handlers/Handler030.php similarity index 80% rename from src/Server/Handlers/Handler030.php rename to src/Middleware/Handlers/Handler030.php index ed669943..693dc8ad 100644 --- a/src/Server/Handlers/Handler030.php +++ b/src/Middleware/Handlers/Handler030.php @@ -1,11 +1,11 @@ */ $stack = $config->get('app.middlewares', array()); - $dispatch = new Dispatch($stack); + $dispatch = new Dispatcher($stack); $empty = $this->preferred === null; diff --git a/src/Middleware/MiddlewareInterface.php b/src/Middleware/MiddlewareInterface.php index ccd33170..aa27c5e3 100644 --- a/src/Middleware/MiddlewareInterface.php +++ b/src/Middleware/MiddlewareInterface.php @@ -2,17 +2,22 @@ namespace Rougin\Slytherin\Middleware; -use Interop\Http\ServerMiddleware\MiddlewareInterface as InteropMiddlewareInterface; +use Psr\Http\Message\ServerRequestInterface; /** * Middleware Interface * - * An interface for handling third party middlewares. - * NOTE: To be removed in v1.0.0. Use DispatcherInterface instead. + * An interface for handling middlewares to Slytherin. * * @package Slytherin * @author Rougin Gutib */ -interface MiddlewareInterface extends InteropMiddlewareInterface +interface MiddlewareInterface { + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Middleware\HandlerInterface $handler + * @return \Psr\Http\Message\ResponseInterface + */ + public function process(ServerRequestInterface $request, HandlerInterface $handler); } diff --git a/src/Middleware/StratigilityDispatcher.php b/src/Middleware/StratigilityDispatcher.php index 7ec7b306..8b555b16 100644 --- a/src/Middleware/StratigilityDispatcher.php +++ b/src/Middleware/StratigilityDispatcher.php @@ -5,10 +5,10 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Http\Response; -use Rougin\Slytherin\Server\Doublepass; -use Rougin\Slytherin\Server\HandlerInterface; -use Rougin\Slytherin\Server\Interop; -use Rougin\Slytherin\Server\MiddlewareInterface; +use Rougin\Slytherin\Middleware\Doublepass; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\Interop; +use Rougin\Slytherin\Middleware\MiddlewareInterface; use Zend\Stratigility\MiddlewarePipe; /** @@ -55,8 +55,8 @@ public function hasPsr() } /** - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Rougin\Slytherin\Server\HandlerInterface $handler + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Middleware\HandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ public function process(ServerRequestInterface $request, HandlerInterface $handler) @@ -74,7 +74,6 @@ public function process(ServerRequestInterface $request, HandlerInterface $handl $item = $this->setPsrMiddleware($item); } - /** @phpstan-ignore-next-line */ $this->zend->pipe($item); } @@ -119,7 +118,7 @@ protected function setFactory(ResponseInterface $response) } /** - * @param \Rougin\Slytherin\Server\MiddlewareInterface $item + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface $item * @return callable */ protected function setMiddleware(MiddlewareInterface $item) diff --git a/src/Server/Version.php b/src/Middleware/Version.php similarity index 96% rename from src/Server/Version.php rename to src/Middleware/Version.php index f220e1d8..5775f4d8 100644 --- a/src/Server/Version.php +++ b/src/Middleware/Version.php @@ -1,6 +1,6 @@ - */ -class Dispatch implements DispatchInterface -{ - /** - * @var \Rougin\Slytherin\Server\MiddlewareInterface[] - */ - protected $stack = array(); - - /** - * @var \Psr\Http\Message\ResponseInterface|null - */ - protected $response = null; - - /** - * @param mixed[] $stack - */ - public function __construct($stack = array()) - { - if ($stack) $this->setStack($stack); - } - - /** - * @return \Rougin\Slytherin\Server\MiddlewareInterface[] - */ - public function getStack() - { - return $this->stack; - } - - /** - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Rougin\Slytherin\Server\HandlerInterface $handler - * @return \Psr\Http\Message\ResponseInterface - */ - public function process(ServerRequestInterface $request, HandlerInterface $handler) - { - $stack = (array) $this->getStack(); - - $handler = new Handler($stack, $handler); - - return $handler->handle($request); - } - - /** - * @param mixed $middleware - * @return self - */ - public function push($middleware) - { - if (! is_array($middleware)) - { - $item = $this->transform($middleware); - - array_push($this->stack, $item); - - return $this; - } - - foreach ($middleware as $item) $this->push($item); - - return $this; - } - - /** - * @param mixed[] $stack - * @return self - */ - public function setStack($stack) - { - $result = array(); - - foreach ($stack as $item) - { - $result[] = $this->transform($item); - } - - $this->stack = $result; - - return $this; - } - - /** - * NOTE: To be removed in v1.0.0. Use $this->getStack() instead. - * - * @return \Rougin\Slytherin\Server\MiddlewareInterface[] - */ - public function stack() - { - return $this->getStack(); - } - - /** - * @param mixed $middleware - * @return \Rougin\Slytherin\Server\MiddlewareInterface - */ - protected function transform($middleware) - { - if ($middleware instanceof MiddlewareInterface) - { - return $middleware; - } - - // Set empty response for double pass middlewares --- - if (! $this->response) - { - $this->response = new Response; - } - // -------------------------------------------------- - - if ($this->isCallable($middleware)) - { - /** @var callable $middleware */ - return new Callback($middleware, $this->response); - } - - return new Wrapper($middleware); - } - - /** - * @param mixed $item - * @return boolean - */ - protected function isCallable($item) - { - /** @var object|string $item */ - $method = method_exists($item, '__invoke'); - - $callable = is_callable($item); - - $object = is_object($item); - - $closure = (! $object) || $item instanceof \Closure; - - return ($method || $callable) && $closure; - } -} diff --git a/src/Server/DispatchInterface.php b/src/Server/DispatchInterface.php deleted file mode 100644 index 355ff883..00000000 --- a/src/Server/DispatchInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ -interface DispatchInterface extends MiddlewareInterface -{ - /** - * @return \Rougin\Slytherin\Server\MiddlewareInterface[] - */ - public function getStack(); - - /** - * @param mixed $middleware - * @return self - */ - public function push($middleware); - - /** - * @param mixed[] $stack - * @return self - */ - public function setStack($stack); -} diff --git a/src/Server/HandlerInterface.php b/src/Server/HandlerInterface.php deleted file mode 100644 index 32f2c6fc..00000000 --- a/src/Server/HandlerInterface.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ -interface HandlerInterface -{ - /** - * @param \Psr\Http\Message\ServerRequestInterface $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function handle(ServerRequestInterface $request); -} diff --git a/src/Server/MiddlewareInterface.php b/src/Server/MiddlewareInterface.php deleted file mode 100644 index f2d92417..00000000 --- a/src/Server/MiddlewareInterface.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -interface MiddlewareInterface -{ - /** - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Rougin\Slytherin\Server\HandlerInterface $handler - * @return \Psr\Http\Message\ResponseInterface - */ - public function process(ServerRequestInterface $request, HandlerInterface $handler); -} diff --git a/src/System.php b/src/System.php index 9037bdbc..19da2a5a 100644 --- a/src/System.php +++ b/src/System.php @@ -16,7 +16,7 @@ class System const ERREPORT = 'Rougin\Slytherin\Ereport\EreportInterface'; - const MIDDLEWARE = 'Rougin\Slytherin\Server\DispatchInterface'; + const MIDDLEWARE = 'Rougin\Slytherin\Middleware\DispatcherInterface'; const RENDERER = 'Rougin\Slytherin\Template\RendererInterface'; @@ -101,7 +101,7 @@ public function handle(ServerRequestInterface $request) return $handler->handle($request); } - /** @var \Rougin\Slytherin\Server\DispatchInterface */ + /** @var \Rougin\Slytherin\Middleware\DispatcherInterface */ $middleware = $this->container->get(self::MIDDLEWARE); $stack = $middleware->getStack(); diff --git a/src/System/Endofline.php b/src/System/Endofline.php index 2ed1efea..7af3de8e 100644 --- a/src/System/Endofline.php +++ b/src/System/Endofline.php @@ -4,7 +4,7 @@ use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Http\Response; -use Rougin\Slytherin\Server\HandlerInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; /** * @package Slytherin diff --git a/src/System/Handler.php b/src/System/Handler.php index bb14276c..903dcea3 100644 --- a/src/System/Handler.php +++ b/src/System/Handler.php @@ -5,8 +5,8 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Container\ContainerInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; use Rougin\Slytherin\Routing\RouteInterface; -use Rougin\Slytherin\Server\HandlerInterface; use Rougin\Slytherin\System; /** diff --git a/tests/Application/AurynContainerTest.php b/tests/Application/AurynContainerTest.php index d89d2e74..cb044657 100644 --- a/tests/Application/AurynContainerTest.php +++ b/tests/Application/AurynContainerTest.php @@ -4,8 +4,8 @@ use Rougin\Slytherin\Container\AurynContainer; use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Middleware\Dispatcher as Middleware; use Rougin\Slytherin\Routing\Dispatcher; -use Rougin\Slytherin\Server\Dispatch; use Rougin\Slytherin\System; /** @@ -38,10 +38,8 @@ protected function doSetUp() $container->share(new Dispatcher($router)); $container->alias(System::DISPATCHER, 'Rougin\Slytherin\Routing\Dispatcher'); - // TODO: Reimplement "Server" with "Middleware" -------------------------- - $container->share(new Dispatch); - $container->alias(System::MIDDLEWARE, 'Rougin\Slytherin\Server\Dispatch'); - // ----------------------------------------------------------------------- + $container->share(new Middleware); + $container->alias(System::MIDDLEWARE, 'Rougin\Slytherin\Middleware\Dispatcher'); $this->application = new Application($container); } diff --git a/tests/Application/ContainerInterfaceTest.php b/tests/Application/ContainerInterfaceTest.php index bfe23381..496fdc9d 100644 --- a/tests/Application/ContainerInterfaceTest.php +++ b/tests/Application/ContainerInterfaceTest.php @@ -5,8 +5,8 @@ use Rougin\Slytherin\Application; use Rougin\Slytherin\Container\Container; use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Middleware\Dispatcher as Middleware; use Rougin\Slytherin\Routing\Dispatcher; -use Rougin\Slytherin\Server\Dispatch; use Rougin\Slytherin\System; /** @@ -35,7 +35,7 @@ protected function doSetUp() $response = new Response; $container->set(System::RESPONSE, $response); - $dispatch = new Dispatch; + $dispatch = new Middleware; $container->set(System::MIDDLEWARE, $dispatch); $this->application = new Application($container); diff --git a/tests/Fixture/Middlewares/BodyParametersMiddleware.php b/tests/Fixture/Middlewares/BodyParametersMiddleware.php index 2ea6a73d..35281a6c 100644 --- a/tests/Fixture/Middlewares/BodyParametersMiddleware.php +++ b/tests/Fixture/Middlewares/BodyParametersMiddleware.php @@ -3,8 +3,8 @@ namespace Rougin\Slytherin\Fixture\Middlewares; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\HandlerInterface; -use Rougin\Slytherin\Server\MiddlewareInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\MiddlewareInterface; /** * @package Slytherin diff --git a/tests/Fixture/Middlewares/CorsMiddleware.php b/tests/Fixture/Middlewares/CorsMiddleware.php index ce4caf92..dbd1c157 100644 --- a/tests/Fixture/Middlewares/CorsMiddleware.php +++ b/tests/Fixture/Middlewares/CorsMiddleware.php @@ -4,8 +4,8 @@ use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Http\Response; -use Rougin\Slytherin\Server\HandlerInterface; -use Rougin\Slytherin\Server\MiddlewareInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\MiddlewareInterface; /** * @package Slytherin diff --git a/tests/Fixture/Middlewares/EmptyMiddleware.php b/tests/Fixture/Middlewares/EmptyMiddleware.php index d8a7d580..4a9b34d0 100644 --- a/tests/Fixture/Middlewares/EmptyMiddleware.php +++ b/tests/Fixture/Middlewares/EmptyMiddleware.php @@ -4,8 +4,8 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\HandlerInterface; -use Rougin\Slytherin\Server\MiddlewareInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\MiddlewareInterface; /** * @package Slytherin diff --git a/tests/Middleware/DispatcherTestCases.php b/tests/Middleware/DispatcherTestCases.php index 93c5655e..f6232552 100644 --- a/tests/Middleware/DispatcherTestCases.php +++ b/tests/Middleware/DispatcherTestCases.php @@ -3,7 +3,8 @@ namespace Rougin\Slytherin\Middleware; use Rougin\Slytherin\Http\ServerRequest; -use Rougin\Slytherin\Server\Wrapper; +use Rougin\Slytherin\Middleware\Interop; +use Rougin\Slytherin\Middleware\Wrapper; use Rougin\Slytherin\System\Endofline; use Rougin\Slytherin\Testcase; @@ -16,7 +17,7 @@ class DispatcherTestCases extends Testcase { /** - * @var \Rougin\Slytherin\Server\Dispatch + * @var \Rougin\Slytherin\Middleware\Dispatch */ protected $dispatcher; @@ -124,6 +125,11 @@ public function testProcessMethodWithDelagateInterfaceCallback() */ public function testProcessMethodWithString() { + if (! Interop::exists()) + { + $this->markTestSkipped('Interop middleware/s not yet installed'); + } + $interop = 'Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'; $this->dispatcher->push($interop); @@ -142,6 +148,11 @@ public function testProcessMethodWithString() */ public function testPushMethodWithArray() { + if (! Interop::exists()) + { + $this->markTestSkipped('Interop middleware/s not yet installed'); + } + $interop = 'Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'; $expected = array(new Wrapper($interop)); @@ -160,6 +171,11 @@ public function testPushMethodWithArray() */ public function testStackMethod() { + if (! Interop::exists()) + { + $this->markTestSkipped('Interop middleware/s not yet installed'); + } + $this->dispatcher->push('Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'); $actual = $this->dispatcher->stack(); diff --git a/tests/Sample/Handlers/Cors.php b/tests/Sample/Handlers/Cors.php index 641764bc..3fc189fe 100644 --- a/tests/Sample/Handlers/Cors.php +++ b/tests/Sample/Handlers/Cors.php @@ -4,8 +4,8 @@ use Psr\Http\Message\ServerRequestInterface; use Rougin\Slytherin\Http\Response; -use Rougin\Slytherin\Server\HandlerInterface; -use Rougin\Slytherin\Server\MiddlewareInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\MiddlewareInterface; /** * @package Slytherin diff --git a/tests/Sample/Handlers/Parsed/Request.php b/tests/Sample/Handlers/Parsed/Request.php index 78a485fd..734fe9e7 100644 --- a/tests/Sample/Handlers/Parsed/Request.php +++ b/tests/Sample/Handlers/Parsed/Request.php @@ -3,8 +3,8 @@ namespace Rougin\Slytherin\Sample\Handlers\Parsed; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\HandlerInterface; -use Rougin\Slytherin\Server\MiddlewareInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\MiddlewareInterface; /** * @package Slytherin diff --git a/tests/Sample/Handlers/Parsed/Response.php b/tests/Sample/Handlers/Parsed/Response.php index 75da95a0..dd012eb8 100644 --- a/tests/Sample/Handlers/Parsed/Response.php +++ b/tests/Sample/Handlers/Parsed/Response.php @@ -3,8 +3,8 @@ namespace Rougin\Slytherin\Sample\Handlers\Parsed; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\HandlerInterface; -use Rougin\Slytherin\Server\MiddlewareInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\MiddlewareInterface; /** * @package Slytherin diff --git a/tests/Sample/Handlers/ToJson.php b/tests/Sample/Handlers/ToJson.php index d8085155..ea3352a3 100644 --- a/tests/Sample/Handlers/ToJson.php +++ b/tests/Sample/Handlers/ToJson.php @@ -3,8 +3,8 @@ namespace Rougin\Slytherin\Sample\Handlers; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Server\HandlerInterface; -use Rougin\Slytherin\Server\MiddlewareInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\MiddlewareInterface; /** * @package Slytherin diff --git a/tests/Sample/SampleTest.php b/tests/Sample/SampleTest.php index d06eedc9..d24e4e89 100644 --- a/tests/Sample/SampleTest.php +++ b/tests/Sample/SampleTest.php @@ -2,6 +2,7 @@ namespace Rougin\Slytherin\Forward; +use Rougin\Slytherin\Middleware\Interop; use Rougin\Slytherin\Sample\Builder; use Rougin\Slytherin\Sample\Handlers\Parsed\Request; use Rougin\Slytherin\Sample\Handlers\Parsed\Response; @@ -227,6 +228,11 @@ public function test_callable_middleware_changing_the_response_parameter() */ public function test_interop_middleware_changing_the_response_parameter() { + if (! Interop::exists()) + { + $this->markTestSkipped('Interop middleware/s not yet installed'); + } + $this->builder->setUrl('GET', '/interop'); $this->expectOutputString('From interop!'); From d84208bcf557844e2fde5106b329f828e07663f4 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 12:10:24 +0800 Subject: [PATCH 23/46] Change type hinting to Rougin\Slytherin\Middleware\MiddlewareInterface --- src/Routing/FastRouteRouter.php | 2 +- src/Routing/PhrouteRouter.php | 10 +-- src/Routing/Router.php | 42 +++++----- src/Routing/RouterInterface.php | 38 ++++----- tests/Application/ApplicationTest.php | 60 +++++++------- tests/Component/CollectionTest.php | 110 ++++++++++++++++---------- 6 files changed, 145 insertions(+), 117 deletions(-) diff --git a/src/Routing/FastRouteRouter.php b/src/Routing/FastRouteRouter.php index 98e091c7..9119010a 100644 --- a/src/Routing/FastRouteRouter.php +++ b/src/Routing/FastRouteRouter.php @@ -26,7 +26,7 @@ class FastRouteRouter extends Router /** * Initializes the router instance. * - * @param array> $routes + * @param array> $routes */ public function __construct(array $routes = array()) { diff --git a/src/Routing/PhrouteRouter.php b/src/Routing/PhrouteRouter.php index 70920f21..f8a7aa58 100644 --- a/src/Routing/PhrouteRouter.php +++ b/src/Routing/PhrouteRouter.php @@ -24,7 +24,7 @@ class PhrouteRouter extends Router /** * Initializes the router instance. * - * @param array> $routes + * @param array> $routes */ public function __construct(array $routes = array()) { @@ -36,10 +36,10 @@ public function __construct(array $routes = array()) /** * Adds a new raw route. * - * @param string $method - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $method + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function add($method, $uri, $handler, $middlewares = array()) diff --git a/src/Routing/Router.php b/src/Routing/Router.php index 478ca833..c51a2e13 100644 --- a/src/Routing/Router.php +++ b/src/Routing/Router.php @@ -30,7 +30,7 @@ class Router implements RouterInterface /** * Initializes the router instance. * - * @param array> $routes + * @param array> $routes */ public function __construct(array $routes = array()) { @@ -49,7 +49,7 @@ public function __construct(array $routes = array()) /** @var callable|string[]|string */ $handler = $route[2]; - /** @var \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[]|string */ + /** @var \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[]|string */ $middlewares = isset($route[3]) ? $route[3] : array(); if (is_string($middlewares)) $middlewares = array($middlewares); @@ -62,9 +62,9 @@ public function __construct(array $routes = array()) * Adds a new raw route. * * @param string $method - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function add($method, $uri, $handler, $middlewares = array()) @@ -103,7 +103,7 @@ public function add($method, $uri, $handler, $middlewares = array()) * @param string $method * @param string $route * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function addRoute($method, $route, $handler, $middlewares = array()) @@ -126,9 +126,9 @@ public function addRoutes(array $routes) /** * Adds a DELETE route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function delete($uri, $handler, $middlewares = array()) @@ -162,9 +162,9 @@ public function find($method, $uri) /** * Adds a GET route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function get($uri, $handler, $middlewares = array()) @@ -235,9 +235,9 @@ public function parsed(array $routes = array()) /** * Adds a PATCH route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function patch($uri, $handler, $middlewares = array()) @@ -248,9 +248,9 @@ public function patch($uri, $handler, $middlewares = array()) /** * Adds a POST route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function post($uri, $handler, $middlewares = array()) @@ -284,9 +284,9 @@ public function prefix($prefix = '', $namespace = null) /** * Adds a PUT route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function put($uri, $handler, $middlewares = array()) diff --git a/src/Routing/RouterInterface.php b/src/Routing/RouterInterface.php index 87fd35b6..145d49e1 100644 --- a/src/Routing/RouterInterface.php +++ b/src/Routing/RouterInterface.php @@ -15,10 +15,10 @@ interface RouterInterface /** * Adds a new raw route. * - * @param string $method - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $method + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function add($method, $uri, $handler, $middlewares = array()); @@ -26,9 +26,9 @@ public function add($method, $uri, $handler, $middlewares = array()); /** * Adds a DELETE route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function delete($uri, $handler, $middlewares = array()); @@ -45,9 +45,9 @@ public function find($method, $uri); /** * Adds a GET route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function get($uri, $handler, $middlewares = array()); @@ -80,9 +80,9 @@ public function parsed(array $routes = array()); /** * Adds a PATCH route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function patch($uri, $handler, $middlewares = array()); @@ -90,9 +90,9 @@ public function patch($uri, $handler, $middlewares = array()); /** * Adds a POST route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function post($uri, $handler, $middlewares = array()); @@ -109,9 +109,9 @@ public function prefix($prefix = '', $namespace = null); /** * Adds a PUT route. * - * @param string $uri - * @param callable|string[]|string $handler - * @param \Interop\Http\ServerMiddleware\MiddlewareInterface[]|string[] $middlewares + * @param string $uri + * @param callable|string[]|string $handler + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares * @return self */ public function put($uri, $handler, $middlewares = array()); diff --git a/tests/Application/ApplicationTest.php b/tests/Application/ApplicationTest.php index f3c1c60a..002cf34b 100644 --- a/tests/Application/ApplicationTest.php +++ b/tests/Application/ApplicationTest.php @@ -2,6 +2,13 @@ namespace Rougin\Slytherin\Application; +use Rougin\Slytherin\Component\Collector; +use Rougin\Slytherin\Configuration; +use Rougin\Slytherin\Dispatching\Phroute\Dispatcher as PhrouteDispatcher; +use Rougin\Slytherin\Dispatching\Phroute\Router as PhrouteRouter; +use Rougin\Slytherin\Dispatching\Vanilla\Router; +use Rougin\Slytherin\Http\Uri; +use Rougin\Slytherin\IoC\Vanilla\Container; use Rougin\Slytherin\Testcase; /** @@ -34,19 +41,17 @@ protected function doSetUp() $this->markTestSkipped('PSR-7 HTTP Message is not installed.'); } - $components = array( - 'Rougin\Slytherin\Fixture\Components\CollectionComponent', - 'Rougin\Slytherin\Fixture\Components\DebuggerComponent', - 'Rougin\Slytherin\Fixture\Components\DispatcherComponent', - 'Rougin\Slytherin\Fixture\Components\HttpComponent', - 'Rougin\Slytherin\Fixture\Components\SingleComponent', - ); + $items = array(); - $container = new \Rougin\Slytherin\IoC\Vanilla\Container; + $items[] = 'Rougin\Slytherin\Fixture\Components\CollectionComponent'; + $items[] = 'Rougin\Slytherin\Fixture\Components\DebuggerComponent'; + $items[] = 'Rougin\Slytherin\Fixture\Components\DispatcherComponent'; + $items[] = 'Rougin\Slytherin\Fixture\Components\HttpComponent'; + $items[] = 'Rougin\Slytherin\Fixture\Components\SingleComponent'; - $globals = $GLOBALS; + $container = new Container; - $components = \Rougin\Slytherin\Component\Collector::get($container, $components, $globals); + $components = Collector::get($container, $items, $GLOBALS); $this->components = $components; } @@ -148,9 +153,9 @@ public function testRunMethodWithPhroute() $routes = array(array('GET', '/', array($class, 'index'))); - $router = new \Rougin\Slytherin\Dispatching\Phroute\Router($routes); + $router = new PhrouteRouter((array) $routes); - $dispatcher = new \Rougin\Slytherin\Dispatching\Phroute\Dispatcher($router); + $dispatcher = new PhrouteDispatcher($router); $this->components->setDispatcher($dispatcher); @@ -171,34 +176,29 @@ public function testRunMethodWithIntegrateMethod() header('X-SLYTHERIN-HEADER: foobar'); - $router = new \Rougin\Slytherin\Dispatching\Vanilla\Router; + $router = new Router; $router->get('/', array('Rougin\Slytherin\Fixture\Classes\NewClass', 'index')); - $application = new \Rougin\Slytherin\Application; + $application = new Application; - $config = new \Rougin\Slytherin\Configuration(__DIR__ . '/../Fixture/Configurations'); + $config = new Configuration(__DIR__ . '/../Fixture/Configurations'); $config->set('app.environment', 'development'); $config->set('app.router', $router); - $config->set('app.views', $root); + $config->set('app.views', (string) $root); - $integrations = array('Rougin\Slytherin\Http\HttpIntegration'); + $items = array('Rougin\Slytherin\Http\HttpIntegration'); - $integrations[] = 'Rougin\Slytherin\Routing\RoutingIntegration'; - $integrations[] = 'Rougin\Slytherin\Template\RendererIntegration'; - $integrations[] = 'Rougin\Slytherin\Debug\ErrorHandlerIntegration'; - - if (interface_exists('Interop\Http\ServerMiddleware\MiddlewareInterface')) - { - $integrations[] = 'Rougin\Slytherin\Middleware\MiddlewareIntegration'; - } - - $integrations[] = 'Rougin\Slytherin\Integration\ConfigurationIntegration'; + $items[] = 'Rougin\Slytherin\Routing\RoutingIntegration'; + $items[] = 'Rougin\Slytherin\Integration\ConfigurationIntegration'; + $items[] = 'Rougin\Slytherin\Template\RendererIntegration'; + $items[] = 'Rougin\Slytherin\Debug\ErrorHandlerIntegration'; + $items[] = 'Rougin\Slytherin\Middleware\MiddlewareIntegration'; $this->expectOutputString('Hello'); - $application->integrate($integrations, $config)->run(); + $application->integrate($items, $config)->run(); } /** @@ -213,7 +213,7 @@ private function runApplication($httpMethod, $uriEndpoint, $data = array()) { list($request, $response) = $this->components->getHttp(); - $uri = new \Rougin\Slytherin\Http\Uri('http://localhost:8000' . $uriEndpoint); + $uri = new Uri('http://localhost:8000' . $uriEndpoint); $request = $request->withMethod($httpMethod)->withUri($uri); @@ -233,6 +233,6 @@ private function runApplication($httpMethod, $uriEndpoint, $data = array()) $this->components->setHttp($request, $response); - return new \Rougin\Slytherin\Application($this->components); + return new Application($this->components); } } diff --git a/tests/Component/CollectionTest.php b/tests/Component/CollectionTest.php index 956ed045..8977118f 100644 --- a/tests/Component/CollectionTest.php +++ b/tests/Component/CollectionTest.php @@ -2,13 +2,24 @@ namespace Rougin\Slytherin\Component; +use Rougin\Slytherin\Component\Collection; +use Rougin\Slytherin\Debug\Vanilla\Debugger; +use Rougin\Slytherin\Dispatching\Vanilla\Dispatcher; +use Rougin\Slytherin\Dispatching\Vanilla\Router; +use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\Http\ServerRequest; +use Rougin\Slytherin\IoC\Vanilla\Container; +use Rougin\Slytherin\Middleware\Interop; +use Rougin\Slytherin\Middleware\VanillaMiddleware; +use Rougin\Slytherin\Testcase; + /** * Component Collection Test * * @package Slytherin * @author Rougin Gutib */ -class CollectionTest extends \Rougin\Slytherin\Testcase +class CollectionTest extends Testcase { /** * @var \Rougin\Slytherin\Component\Collection @@ -22,7 +33,7 @@ class CollectionTest extends \Rougin\Slytherin\Testcase */ protected function doSetUp() { - $this->components = new \Rougin\Slytherin\Component\Collection; + $this->components = new Collection; } /** @@ -37,11 +48,13 @@ public function testSetContainerMethod() $this->markTestSkipped('Container Interop is not installed.'); } - $container = new \Rougin\Slytherin\IoC\Vanilla\Container; + $expected = new Container; + + $this->components->setContainer($expected); - $this->components->setContainer($container); + $actual = $this->components->getContainer(); - $this->assertEquals($container, $this->components->getContainer()); + $this->assertEquals($expected, $actual); } /** @@ -51,13 +64,15 @@ public function testSetContainerMethod() */ public function testSetDispatcherMethod() { - $router = new \Rougin\Slytherin\Dispatching\Vanilla\Router; + $router = new Router; - $dispatcher = new \Rougin\Slytherin\Dispatching\Vanilla\Dispatcher($router); + $expected = new Dispatcher($router); - $this->components->setDispatcher($dispatcher); + $this->components->setDispatcher($expected); - $this->assertEquals($dispatcher, $this->components->getDispatcher()); + $actual = $this->components->getDispatcher(); + + $this->assertEquals($expected, $actual); } /** @@ -67,11 +82,13 @@ public function testSetDispatcherMethod() */ public function testSetDebuggerMethod() { - $debugger = new \Rougin\Slytherin\Debug\Vanilla\Debugger; + $expected = new Debugger; + + $this->components->setDebugger($expected); - $this->components->setDebugger($debugger); + $actual = $this->components->getDebugger(); - $this->assertEquals($debugger, $this->components->getDebugger()); + $this->assertEquals($expected, $actual); } /** @@ -86,19 +103,23 @@ public function testSetHttpMethod() $this->markTestSkipped('PSR HTTP Message is not installed.'); } - $server = array( - 'SERVER_NAME' => 'localhost', - 'SERVER_PORT' => '8000', - 'REQUEST_URI' => '/', - 'REQUEST_METHOD' => 'GET', - ); + $server = array(); + $server['REQUEST_METHOD'] = 'GET'; + $server['REQUEST_URI'] = '/'; + $server['SERVER_NAME'] = 'localhost'; + $server['SERVER_PORT'] = '8000'; - $response = new \Rougin\Slytherin\Http\Response; - $request = new \Rougin\Slytherin\Http\ServerRequest($server); + $request = new ServerRequest($server); + + $response = new Response; + + $expected = array($request, $response); $this->components->setHttp($request, $response); - $this->assertEquals(array($request, $response), $this->components->getHttp()); + $actual = $this->components->getHttp(); + + $this->assertEquals($expected, $actual); } /** @@ -113,18 +134,19 @@ public function testSetHttpRequestMethod() $this->markTestSkipped('PSR HTTP Message is not installed.'); } - $server = array( - 'SERVER_NAME' => 'localhost', - 'SERVER_PORT' => '8000', - 'REQUEST_URI' => '/', - 'REQUEST_METHOD' => 'GET', - ); + $server = array(); + $server['REQUEST_METHOD'] = 'GET'; + $server['REQUEST_URI'] = '/'; + $server['SERVER_NAME'] = 'localhost'; + $server['SERVER_PORT'] = '8000'; + + $expected = new ServerRequest($server); - $request = new \Rougin\Slytherin\Http\ServerRequest($server); + $this->components->setHttpRequest($expected); - $this->components->setHttpRequest($request); + $actual = $this->components->getHttpRequest(); - $this->assertEquals($request, $this->components->getHttpRequest()); + $this->assertEquals($expected, $actual); } /** @@ -139,11 +161,13 @@ public function testSetHttpResponseMethod() $this->markTestSkipped('PSR HTTP Message is not installed.'); } - $response = new \Rougin\Slytherin\Http\Response; + $expected = new Response; - $this->components->setHttpResponse($response); + $this->components->setHttpResponse($expected); - $this->assertEquals($response, $this->components->getHttpResponse()); + $actual = $this->components->getHttpResponse(); + + $this->assertEquals($expected, $actual); } /** @@ -153,19 +177,23 @@ public function testSetHttpResponseMethod() */ public function testSetMiddlewareMethod() { - $response = 'Psr\Http\Message\ResponseInterface'; - - interface_exists($response) || $this->markTestSkipped('PSR HTTP Message is not installed.'); + if (! interface_exists('Psr\Http\Message\ResponseInterface')) + { + $this->markTestSkipped('PSR HTTP Message is not installed.'); + } - $middleware = 'Interop\Http\ServerMiddleware\MiddlewareInterface'; + if (! Interop::exists()) + { + $this->markTestSkipped('Interop middleware/s not yet installed'); + } - interface_exists($middleware) || $this->markTestSkipped('Interop Middleware is not installed.'); + $expected = new VanillaMiddleware; - $middleware = new \Rougin\Slytherin\Middleware\VanillaMiddleware; + $this->components->setMiddleware($expected); - $this->components->setMiddleware($middleware); + $actual = $this->components->getMiddleware(); - $this->assertEquals($middleware, $this->components->getMiddleware()); + $this->assertEquals($expected, $actual); } /** From 32eccf9d946c0c821ed1a4030345977b073a4a25 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 13:49:26 +0800 Subject: [PATCH 24/46] Fix issue in build.yml --- tests/Application/ApplicationTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Application/ApplicationTest.php b/tests/Application/ApplicationTest.php index 002cf34b..4e200b7c 100644 --- a/tests/Application/ApplicationTest.php +++ b/tests/Application/ApplicationTest.php @@ -51,7 +51,9 @@ protected function doSetUp() $container = new Container; - $components = Collector::get($container, $items, $GLOBALS); + $globals = $GLOBALS; + + $components = Collector::get($container, $items, $globals); $this->components = $components; } From c5a087e8d3ddbe5ef9e090d0ee3503278e6904a8 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 14:01:04 +0800 Subject: [PATCH 25/46] Change details in CHANGELOG.md --- CHANGELOG.md | 4 +++- tests/Application/AurynContainerTest.php | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d0a756b..dc83ff63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to `Slytherin` will be documented in this file. - Conformed all application logic to `RouteInterface` - `UnexpectedValueException` to `BadMethodCallException` in `DispatcherInterface` - Conformed `Middleware` to the official `PSR-15` package (`psr/http-server-middleware`) +- `ERROR_HANDLER` to `ERREPORT` ### Fixed - Type hinting of all classes using `PHPStan` (up to `level 9`) @@ -23,7 +24,8 @@ All notable changes to `Slytherin` will be documented in this file. - If `ServerRequestInterface` is an argument with a middleware - Backward compatibility for `LeagueContainer::set` (as of `~3.0`) - Backward compatibility for `TwigRenderer::render` (as of `~3.0`) -- Resolving typehinted routes for third-party routers +- Backward compatibility for `StratigilityDispatcher::process` (until `~3.0`) +- Resolving type hinted routes for third-party routers ## [0.9.6](https://github.com/rougin/slytherin/compare/v0.9.5...v0.9.6) - 2023-11-16 diff --git a/tests/Application/AurynContainerTest.php b/tests/Application/AurynContainerTest.php index cb044657..5d249aee 100644 --- a/tests/Application/AurynContainerTest.php +++ b/tests/Application/AurynContainerTest.php @@ -23,7 +23,10 @@ class AurynContainerTest extends ApplicationTestCases */ protected function doSetUp() { - if (! class_exists('Auryn\Injector')) $this->markTestSkipped('Auryn is not installed.'); + if (! class_exists('Auryn\Injector')) + { + $this->markTestSkipped('Auryn is not installed.'); + } $container = new AurynContainer; From 96754b96f26a93d9326f7fccb828258244209915 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Tue, 5 Dec 2023 14:05:48 +0800 Subject: [PATCH 26/46] Change details in CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc83ff63..b63b0ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,8 @@ All notable changes to `Slytherin` will be documented in this file. - Conformed all application logic to `RouteInterface` - `UnexpectedValueException` to `BadMethodCallException` in `DispatcherInterface` - Conformed `Middleware` to the official `PSR-15` package (`psr/http-server-middleware`) -- `ERROR_HANDLER` to `ERREPORT` +- `Application` class to `System` +- `ERROR_HANDLER` constant to `ERREPORT` in `System` ### Fixed - Type hinting of all classes using `PHPStan` (up to `level 9`) From deec233095150a93ad7555085974d4d8020d5cb6 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 02:30:55 +0800 Subject: [PATCH 27/46] Add v0.2.2 in CHANGELOG.md --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b63b0ddb..e2caae70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -241,7 +241,7 @@ All notable changes to `Slytherin` will be documented in this file. - `Http\ResponseInterface` dependency in `Dispatching\Dispatcher` - Dependency of [nikic/fast-route](https://github.com/nikic/FastRoute) in `Dispatching` (use `Dispatching\FastRoute` instead) -## [0.3.0](https://github.com/rougin/slytherin/compare/v0.2.1...v0.3.0) - 2015-11-02 +## [0.3.0](https://github.com/rougin/slytherin/compare/v0.2.2...v0.3.0) - 2015-11-02 **NOTE**: This release will break your application if upgrading from `v0.2.0` release. @@ -255,6 +255,16 @@ All notable changes to `Slytherin` will be documented in this file. ### Removed - Almost everything, this release will be no longer an application skeleton +## [0.2.2](https://github.com/rougin/slytherin/compare/v0.2.1...v0.2.2) - 2023-12-08 + +**NOTE**: This is a backport fix to prevent any backward compatability issues from previous versions. + +### Changed +- `post-install-cmd` to `post-update-cmd` in `README.md` + +### Fixed +- Missing `APPPATH` after creating `index.php` from `Installer` + ## [0.2.1](https://github.com/rougin/slytherin/compare/v0.2.0...v0.2.1) - 2015-09-30 ### Added From f0c7b13e6978466fbe5015192eb2ea9cdccdf260 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 05:00:54 +0800 Subject: [PATCH 28/46] Add ERRATUM.md, UPGRADE.md --- CHANGELOG.md | 2 + ERRATUM.md | 47 +++++++++ UPGRADE.md | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 ERRATUM.md create mode 100644 UPGRADE.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e2caae70..e5d5930b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ All notable changes to `Slytherin` will be documented in this file. - `ContainerException` in `Container` - `RouteInterface` for handling specific routes - Support for all versions of `http-interop/http-middleware` (`0.3`, `0.4`, `0.5`) +- `ERRATUM.md` for changes in `README.md` for specified versions +- `UPGRADE.md` for documentation on how to upgrade versions with BC breaks ### Changed - Third-party packages in `Routing` extends to Slytherin's `Dispatcher`, `Router` diff --git a/ERRATUM.md b/ERRATUM.md new file mode 100644 index 00000000..8434f7a1 --- /dev/null +++ b/ERRATUM.md @@ -0,0 +1,47 @@ +# Erratum + +The following are the erratum for each `README.md` found from the previous versions: + +## 0.4.0 + +In this version, the `patricklouys/http` has been removed in favor for PSR-07 (`psr/http-message`). With this, kindly add a package that is compliant to PSR-07 (e.g., `zendframework/zend-diactoros`) in the `composer.json`: + +``` diff + { + "require": + { + "filp/whoops": "~2.0", + "nikic/fast-route": "~1.0", +- "patricklouys/http": "~1.0", + "rdlowrey/auryn": "~1.0", +- "rougin/slytherin": "~0.3.0", +- "twig/twig": "~1.0" ++ "rougin/slytherin": "~0.4.0", ++ "twig/twig": "~1.0", ++ "zendframework/zend-diactoros": "~1.0" + } + } +``` + +Perform `composer update` afterwards to update the specified packages. + +## 0.3.0 + +### Usage + +As per documentation, implementing interfaces are required to use Slytherin components. However in this release, the implemented third-party packages are not included (e.g., `patricklouys/http`, etc.) and needs to be installed manually. Kindly include the said packages in the `composer.json`: + +``` diff + { + "require": + { ++ "filp/whoops": "~1.0", ++ "nikic/fast-route": "~1.0", ++ "patricklouys/http": "~1.0", ++ "rdlowrey/auryn": "~1.0", +- "rougin/slytherin": "~0.3.0" ++ "rougin/slytherin": "~0.3.0", ++ "twig/twig": "~1.0" + } + } +``` \ No newline at end of file diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 00000000..b2580ad0 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,269 @@ +# Slytherin Upgrade Guide + +Below are the guides when upgrading from specified versions due to backward compatibility breaks: + +## From `v0.3.0` to `v0.4.0` + +The `v0.4.0` version requires a PSR-07 compliant package. See the `v0.4.0` in `ERRATUM` for updating the `composer.json`. + +With the transition to PSR-07, kindly update the following classes from `index.php`: + +``` diff + use Rougin\Slytherin\Application; + use Rougin\Slytherin\ComponentCollection; + use Rougin\Slytherin\Dispatching\Dispatcher; + use Rougin\Slytherin\ErrorHandler\Whoops; +-use Rougin\Slytherin\Http\Request; +-use Rougin\Slytherin\Http\Response; + use Rougin\Slytherin\IoC\Auryn; + use Rougin\Slytherin\Template\RendererInterface; + use Rougin\Slytherin\Template\Twig; ++use Zend\Diactoros\Response; ++use Zend\Diactoros\ServerRequestFactory; + + // ... + +-// Initialize the RequestInterface and ResponseInterface ----------------------------- +-$stream = file_get_contents('php://input'); +-$request = new \Http\HttpRequest($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER, $stream); +-$request = new Request($request); +-$response = new Response(new \Http\HttpResponse); ++// Initialize the ServerRequestInterface and ResponseInterface --- ++$request = ServerRequestFactory::fromGlobals(); ++$response = new Response; + $component->setHttp($request, $response); +-// ----------------------------------------------------------------------------------- ++// --------------------------------------------------------------- + + // ... +``` + +## From `v0.2.1` to `v0.3.0` + +Due to transition from an application project to a micro-framework package, the following updates must be performed: + +Update the following details in `composer.json`: + +``` diff + { + "require": + { +- "rougin/slytherin": "~0.2.0" ++ "filp/whoops": "~2.0", ++ "nikic/fast-route": "~1.0", ++ "patricklouys/http": "~1.0", ++ "rdlowrey/auryn": "~1.0", ++ "rougin/slytherin": "~0.3.0", ++ "twig/twig": "~1.0" + }, + "autoload": + { + "psr-4": + { +- "Controllers\\": "app/controllers", +- "Libraries\\": "app/libraries", +- "Models\\": "app/models" ++ "Rougin\\Nostalgia\\": "src" + } +- }, +- "scripts": +- { +- "post-update-cmd": +- [ +- "Rougin\\Slytherin\\Installer::deploy" +- ] + } + } +``` + +**NOTE**: `Rougin\\Nostalgia\\` is only a example namespace. The said namespace can be changed for the whole project. + +Then execute `composer update` to update the packages: + +``` bash +$ composer update +``` + +After updating, copy the `index.php` to `app/web` directory: + +``` diff ++app/ ++├─ web/ ++│ ├─ index.php +-index.php +``` + +From the `index.php`, paste the following code: + +``` php +use Rougin\Slytherin\Application; +use Rougin\Slytherin\ComponentCollection; +use Rougin\Slytherin\Dispatching\Dispatcher; +use Rougin\Slytherin\ErrorHandler\Whoops; +use Rougin\Slytherin\Http\Request; +use Rougin\Slytherin\Http\Response; +use Rougin\Slytherin\IoC\Auryn; +use Rougin\Slytherin\Template\RendererInterface; +use Rougin\Slytherin\Template\Twig; + +$root = dirname(dirname(__DIR__)); + +require $root . '/vendor/autoload.php'; + +$component = new ComponentCollection; + +// Initialize the RendererInterface ------------- +$views = (string) realpath($root . '/app/views'); +$loader = new Twig_Loader_Filesystem($views); +$twig = new Twig(new Twig_Environment($loader)); +$renderer = RendererInterface::class; +// ---------------------------------------------- + +// Initialize the DependencyInjectorInterface --- +$auryn = new Auryn(new \Auryn\Injector); +// Create an alias for the RendererInterface --- +$auryn->share($twig); +$auryn->alias($renderer, get_class($twig)); +// --------------------------------------------- +$component->setDependencyInjector($auryn); +// ---------------------------------------------- + +// Initialize the ErrorHandlerInterface --- +$whoops = new Whoops(new \Whoops\Run); +$component->setErrorHandler($whoops); +// ---------------------------------------- + +// Initialize the RequestInterface and ResponseInterface ----------------------------- +$stream = file_get_contents('php://input'); +$request = new \Http\HttpRequest($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER, $stream); +$request = new Request($request); +$response = new Response(new \Http\HttpResponse); +$component->setHttp($request, $response); +// ----------------------------------------------------------------------------------- + +// Initialize the routing dispatcher interface --- +$router = require "$root/app/config/routes.php"; +$dispatcher = new Dispatcher($router, $response); +$component->setDispatcher($dispatcher); +// ----------------------------------------------- + +// Initialize then run the Application --- +$app = new Application($component); + +$app->run(); +// --------------------------------------- +``` + +From the `app/config/routes.php` file, change the syntax with the following below: + +``` php +use Rougin\Slytherin\Dispatching\Router; + +$name = 'Rougin\Nostalgia\Routes'; + +$router = new Router; + +$router->addRoute('GET', '/', [ "$name\Welcome", 'index' ]); + +$router->addRoute('GET', '/users', [ "$name\Users", 'index' ]); + +return $router; +``` + +Create a `src` directory then copy `controllers`, `libraries`, and `models` to `src/Routes`, `src/Packages`, and `src/Models` respectively: + +``` diff + app/ +-├─ controllers/ +-├─ packages/ +-├─ models/ ++src/ ++├─ Models/ ++├─ Packages/ ++├─ Routes/ +``` + +Once copied, remove the `extends Controller` in each of the files in the `src/Routes` directory. If the route uses `View`, replace it with `RendererInterface`: + +``` diff +-namespace Controllers; ++namespace Routes; + +-use Rougin\Slytherin\Controller; +-use Rougin\Slytherin\View; ++use Rougin\Slytherin\Template\RendererInterface; + +-class Welcome extends Controller ++class Welcome + { ++ protected $renderer; ++ ++ public function __construct(RendererInterface $renderer) ++ { ++ $this->renderer = $renderer; ++ } ++ + public function index() + { +- return View::render('welcome/index'); ++ return $this->renderer->render('welcome/index'); + } + } +``` + +If using the `View` class for handling templates, rename the files in the `view` directory with `.html`: + +``` diff + app/ + ├─ views/ + │ ├─ users/ ++│ │ ├─ index.html +-│ │ ├─ index.php + │ ├─ welcome/ ++│ │ ├─ index.html +-│ │ ├─ index.php +``` + +If using the `Model` class for handling database results, replace it with the implementation of `PDO`: + +``` diff +-namespace Models; ++namespace Rougin\Nostalgia\Models; + +-use Rougin\Slytherin\Model; +- +-class User extends Model ++class User + { +- public function get($page = 1, $limit = 10) ++ protected $pdo; ++ ++ public function __construct() + { +- $pdo = $this->databaseHandle; ++ $items = require __DIR__ . '/../../app/config/databases.php'; ++ ++ $data = $items['default']; ++ ++ $dsn = "{$data['driver']}:host={$data['hostname']};dbname={$data['database']}"; ++ ++ $pdo = new \PDO($dsn, $data['username'], $data['password']); + ++ $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); ++ ++ $this->pdo = $pdo; ++ } ++ ++ public function get($page = 1, $limit = 10) ++ { + $query = 'SELECT * FROM users'; + + $offset = ($page - 1) * $limit; + + $query .= " LIMIT $limit OFFSET $offset"; + +- $st = $pdo->prepare($query); ++ $st = $this->pdo->prepare($query); + + $st->execute(); +``` \ No newline at end of file From 722a9ac184c91181afda5e7a32b728dc92016797 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 09:52:11 +0800 Subject: [PATCH 29/46] Change details in CHANGELOG.md, UPGRADE.md --- CHANGELOG.md | 18 ++++++++++++++++-- UPGRADE.md | 4 ++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5d5930b..5e96f670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -199,6 +199,19 @@ All notable changes to `Slytherin` will be documented in this file. - PHP version to `v5.4.0` - Interface from `RequestInterface` to `ServerRequestInterface` in `Components` +## [0.4.4](https://github.com/rougin/slytherin/compare/v0.4.3...v0.4.4) - 2023-12-08 + +**NOTE**: This is a backport fix to lessen backward compatibility issues. + +### Added +- `ComponentCollection` +- `ErrorHandler` (`ErrorHandlerInterface`, `Whoops`) +- `IoC\Auryn` +- `Template\Twig` + +### Fixed +- Missing `container-interop/container-interop` package in `composer.json` + ## [0.4.3](https://github.com/rougin/slytherin/compare/v0.4.2...v0.4.3) - 2016-02-19 ### Added @@ -259,13 +272,14 @@ All notable changes to `Slytherin` will be documented in this file. ## [0.2.2](https://github.com/rougin/slytherin/compare/v0.2.1...v0.2.2) - 2023-12-08 -**NOTE**: This is a backport fix to prevent any backward compatability issues from previous versions. +**NOTE**: This is a backport fix to lessen backward compatibility issues. ### Changed - `post-install-cmd` to `post-update-cmd` in `README.md` +- Detailed example in creating the `CRUD` code ### Fixed -- Missing `APPPATH` after creating `index.php` from `Installer` +- Missing `APPPATH` after generating `index.php` from `Installer` ## [0.2.1](https://github.com/rougin/slytherin/compare/v0.2.0...v0.2.1) - 2015-09-30 diff --git a/UPGRADE.md b/UPGRADE.md index b2580ad0..54d7ecaa 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -76,7 +76,7 @@ Update the following details in `composer.json`: } ``` -**NOTE**: `Rougin\\Nostalgia\\` is only a example namespace. The said namespace can be changed for the whole project. +**NOTE**: `Rougin\\Nostalgia\\` is only an example namespace. The said namespace can be changed for the whole project. Then execute `composer update` to update the packages: @@ -187,7 +187,7 @@ Once copied, remove the `extends Controller` in each of the files in the `src/Ro ``` diff -namespace Controllers; -+namespace Routes; ++namespace Rougin\Nostalgia\Routes; -use Rougin\Slytherin\Controller; -use Rougin\Slytherin\View; From b4bad6e1e859dc732174837a520cf21d5a7e97a2 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 10:02:59 +0800 Subject: [PATCH 30/46] Change details in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e96f670..dcb10c3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -187,7 +187,7 @@ All notable changes to `Slytherin` will be documented in this file. ### Changed - File and directory structure -## [0.5.0](https://github.com/rougin/slytherin/compare/v0.4.3...v0.5.0) - 2016-04-14 +## [0.5.0](https://github.com/rougin/slytherin/compare/v0.4.4...v0.5.0) - 2016-04-14 ### Added - `Middleware` component From f43ecaba191626f2e564ff2fe0fc315710b39dc2 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 12:01:05 +0800 Subject: [PATCH 31/46] Change details in CHANGELOG.md --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcb10c3d..1c4783a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -178,7 +178,7 @@ All notable changes to `Slytherin` will be documented in this file. ### Removed - `HttpKernelInterface` -## [0.6.0](https://github.com/rougin/slytherin/compare/v0.5.0...v0.6.0) - 2016-05-24 +## [0.6.0](https://github.com/rougin/slytherin/compare/v0.5.1...v0.6.0) - 2016-05-24 ### Added - Parameter for adding default data and file extension in `Template\TwigRenderer` @@ -187,6 +187,17 @@ All notable changes to `Slytherin` will be documented in this file. ### Changed - File and directory structure +## [0.5.1](https://github.com/rougin/slytherin/compare/v0.5.0...v0.5.1) - 2023-12-08 + +**NOTE**: This is a backport fix to lessen backward compatibility issues. + +### Added +- `NoopFinalHandler` in `StratigilityMiddleware` + +### Fixed +- Compatibility issues from `v0.4.4` release +- Issue if no middleware was defined in `Application` + ## [0.5.0](https://github.com/rougin/slytherin/compare/v0.4.4...v0.5.0) - 2016-04-14 ### Added From 58a809bd046f61fdfae9d8804c7c1a2f21f0c648 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 13:02:35 +0800 Subject: [PATCH 32/46] Add v0.6.1 backport fix in CHANGELOG.md --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c4783a7..c89210dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -163,7 +163,7 @@ All notable changes to `Slytherin` will be documented in this file. ### Removed - Third party packages in `require-dev` -## [0.7.0](https://github.com/rougin/slytherin/compare/v0.6.0...v0.7.0) - 2016-07-17 +## [0.7.0](https://github.com/rougin/slytherin/compare/v0.6.1...v0.7.0) - 2016-07-17 ### Added - HTTP method spoofing @@ -178,6 +178,13 @@ All notable changes to `Slytherin` will be documented in this file. ### Removed - `HttpKernelInterface` +## [0.6.1](https://github.com/rougin/slytherin/compare/v0.6.0...v0.6.1) - 2023-12-08 + +**NOTE**: This is a backport fix to lessen backward compatibility issues. + +### Fixed +- Compatibility issues from `v0.5.1` release + ## [0.6.0](https://github.com/rougin/slytherin/compare/v0.5.1...v0.6.0) - 2016-05-24 ### Added From c9409a6441e95cb32e5c6627106ce63e38ea039b Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 13:39:16 +0800 Subject: [PATCH 33/46] Add v0.7.1 backport fix in CHANGELOG.md --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c89210dd..2d6749ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -149,7 +149,7 @@ All notable changes to `Slytherin` will be documented in this file. - Traits (in order to achieve PHP `v5.3.0` as the minimum required version) - `getEnvironment` and `setEnvironment` in `Debug\ErrorHandlerInterface` -## [0.8.0](https://github.com/rougin/slytherin/compare/v0.7.0...v0.8.0) - 2016-09-08 +## [0.8.0](https://github.com/rougin/slytherin/compare/v0.7.1...v0.8.0) - 2016-09-08 ### Added - Implementation for [Phroute](https://github.com/mrjgreen/phroute) package @@ -163,6 +163,13 @@ All notable changes to `Slytherin` will be documented in this file. ### Removed - Third party packages in `require-dev` +## [0.7.1](https://github.com/rougin/slytherin/compare/v0.7.0...v0.7.1) - 2023-12-08 + +**NOTE**: This is a backport fix to lessen backward compatibility issues. + +### Fixed +- Compatibility issues from `v0.6.1` release + ## [0.7.0](https://github.com/rougin/slytherin/compare/v0.6.1...v0.7.0) - 2016-07-17 ### Added From 5e77433e082884d527be818cc7af118f2beb7ac2 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 15:05:29 +0800 Subject: [PATCH 34/46] Add v0.8.1 backport fix in CHANGELOG.md --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d6749ec..203b457b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,7 +96,7 @@ All notable changes to `Slytherin` will be documented in this file. ### Fixed - Retrieving a single uploaded file in `ServerRequest::getUploadedFiles` -## [0.9.0](https://github.com/rougin/slytherin/compare/v0.8.0...v0.9.0) - 2017-07-08 +## [0.9.0](https://github.com/rougin/slytherin/compare/v0.8.1...v0.9.0) - 2017-07-08 **NOTE**: This release may break your application if upgrading from `v0.8.0` release. @@ -149,6 +149,17 @@ All notable changes to `Slytherin` will be documented in this file. - Traits (in order to achieve PHP `v5.3.0` as the minimum required version) - `getEnvironment` and `setEnvironment` in `Debug\ErrorHandlerInterface` +## [0.8.1](https://github.com/rougin/slytherin/compare/v0.8.0...v0.8.1) - 2023-12-08 + +**NOTE**: This is a backport fix to lessen backward compatibility issues. + +### Added +- `alias` method in `AurynContainer` for backport fix from previous versions + +### Fixed +- Compatibility issues from `v0.7.1` release +- `html` as the default `$fileExtension` instead of `twig` + ## [0.8.0](https://github.com/rougin/slytherin/compare/v0.7.1...v0.8.0) - 2016-09-08 ### Added From d001c1ff98a76bf81909acd03db4bc009bcf3eab Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 15:40:45 +0800 Subject: [PATCH 35/46] Fix compatibility issues from v0.8.1 --- src/Application.php | 3 +-- src/Component/Collection.php | 29 +++++++++++++++++----- src/ComponentCollection.php | 3 +-- src/Components.php | 1 - src/Configuration.php | 2 +- src/ErrorHandler/ErrorHandlerInterface.php | 3 +-- src/ErrorHandler/Whoops.php | 7 ++---- src/IoC/Auryn.php | 15 +++++++++++ src/IoC/Auryn/Container.php | 4 --- src/IoC/AurynContainer.php | 6 +---- src/IoC/ContainerInterface.php | 17 +++++++++++++ src/System.php | 8 ++++++ 12 files changed, 70 insertions(+), 28 deletions(-) create mode 100644 src/IoC/Auryn.php create mode 100644 src/IoC/ContainerInterface.php diff --git a/src/Application.php b/src/Application.php index ff913ce8..e85d2b47 100644 --- a/src/Application.php +++ b/src/Application.php @@ -5,8 +5,7 @@ /** * Application * - * Integrates all specified components into the application. - * NOTE: To be removed in v1.0.0. Use \System instead. + * NOTE: To be removed in v1.0.0. Use "System" instead. * * @package Slytherin * @author Rougin Gutib diff --git a/src/Component/Collection.php b/src/Component/Collection.php index 00f72a69..4636868a 100644 --- a/src/Component/Collection.php +++ b/src/Component/Collection.php @@ -14,7 +14,6 @@ /** * Component Collection * - * Contains all the required components for Slytherin. * NOTE: To be removed in v1.0.0. Use "Integration" instead. * * @package Slytherin @@ -50,6 +49,28 @@ public function setContainer(ContainerInterface $container) return $this; } + /** + * Gets an instance of the dependency injector. + * NOTE: To be removed in v1.0.0. Use "getContainer" instead. + * + * @return \Rougin\Slytherin\IoC\ContainerInterface + */ + public function getDependencyInjector() + { + return $this->getContainer(); + } + + /** + * Sets the dependency injector. + * NOTE: To be removed in v1.0.0. Use "setContainer" instead. + * + * @param \Rougin\Slytherin\IoC\ContainerInterface $injector + */ + public function setDependencyInjector(ContainerInterface $injector) + { + return $this->setContainer($injector); + } + /** * Gets the dispatcher. * @@ -130,11 +151,7 @@ public function setErrorHandler(ErrorHandlerInterface $errorHandler) */ public function getHttp() { - $request = $this->get(System::SERVER_REQUEST); - - $response = $this->get(System::RESPONSE); - - return array($request, $response); + return array($this->getHttpRequest(), $this->getHttpResponse()); } /** diff --git a/src/ComponentCollection.php b/src/ComponentCollection.php index 4e3dafde..861d5137 100644 --- a/src/ComponentCollection.php +++ b/src/ComponentCollection.php @@ -5,8 +5,7 @@ /** * Component Collection * - * Contains all the required components for Slytherin. - * NOTE: To be removed in v1.0.0. Use "Component\Collection" instead. + * NOTE: To be removed in v1.0.0. Use "Components" instead. * * @package Slytherin * @author Rougin Gutib diff --git a/src/Components.php b/src/Components.php index 28f9e2b1..55f9f19c 100644 --- a/src/Components.php +++ b/src/Components.php @@ -5,7 +5,6 @@ /** * Component Collection * - * Contains all the required components for Slytherin. * NOTE: To be removed in v1.0.0. Use "Component\Collection" instead. * * @package Slytherin diff --git a/src/Configuration.php b/src/Configuration.php index 95022a78..97abfb93 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -5,7 +5,7 @@ /** * Configuration * - * Serves as a storage for configuration data. + * NOTE: To be removed in v1.0.0. Use "Integration/Configuration" instead. * * @package Slytherin * @author Rougin Gutib diff --git a/src/ErrorHandler/ErrorHandlerInterface.php b/src/ErrorHandler/ErrorHandlerInterface.php index f97b1314..d65d1277 100644 --- a/src/ErrorHandler/ErrorHandlerInterface.php +++ b/src/ErrorHandler/ErrorHandlerInterface.php @@ -7,8 +7,7 @@ /** * Error Handler Interface * - * An interface for handling third party debuggers. - * NOTE: To be removed in v1.0.0. Use "ErrorHandlerIntegration" instead. + * NOTE: To be removed in v1.0.0. Use "Debug\DebuggerInterface" instead. * * @package Slytherin * @author Rougin Gutib diff --git a/src/ErrorHandler/Whoops.php b/src/ErrorHandler/Whoops.php index b6edc5d5..b415f675 100644 --- a/src/ErrorHandler/Whoops.php +++ b/src/ErrorHandler/Whoops.php @@ -2,15 +2,12 @@ namespace Rougin\Slytherin\ErrorHandler; -use Rougin\Slytherin\Debug\Whoops\Debugger; +use Rougin\Slytherin\Debug\Debugger; /** * Debugger * - * A simple implementation of a debugger built on top of Filipe Dobreira's - * Whoops. NOTE: To be removed in v1.0.0. Use "ErrorHandlerIntegration" instead. - * - * http://filp.github.io/whoops + * NOTE: To be removed in v1.0.0. Use "Debug\Debugger" instead. * * @package Slytherin * @author Rougin Gutib diff --git a/src/IoC/Auryn.php b/src/IoC/Auryn.php new file mode 100644 index 00000000..dde3021c --- /dev/null +++ b/src/IoC/Auryn.php @@ -0,0 +1,15 @@ + + */ +class Auryn extends AurynContainer +{ +} diff --git a/src/IoC/Auryn/Container.php b/src/IoC/Auryn/Container.php index 586344fb..da092167 100644 --- a/src/IoC/Auryn/Container.php +++ b/src/IoC/Auryn/Container.php @@ -7,12 +7,8 @@ /** * Auryn Container * - * A simple implementation of a container that is built on top of - * Daniel Lowrey's Auryn Inversion Of Control (IoC) Dependency Injector. * NOTE: To be removed in v1.0.0. Use "Container\AurynContainer" instead. * - * https://github.com/rdlowrey/auryn - * * @package Slytherin * @author Rougin Gutib */ diff --git a/src/IoC/AurynContainer.php b/src/IoC/AurynContainer.php index efb21f9b..5e92a9a7 100644 --- a/src/IoC/AurynContainer.php +++ b/src/IoC/AurynContainer.php @@ -5,11 +5,7 @@ /** * Auryn Container * - * A simple implementation of a container that is built on top of - * Daniel Lowrey's Auryn Inversion Of Control (IoC) Dependency Injector. - * NOTE: To be removed in v1.0.0. Use "Container\AurynContainer" instead. - * - * https://github.com/rdlowrey/auryn + * NOTE: To be removed in v1.0.0. Use "Auryn\Container" instead. * * @package Slytherin * @author Rougin Gutib diff --git a/src/IoC/ContainerInterface.php b/src/IoC/ContainerInterface.php new file mode 100644 index 00000000..dc325dba --- /dev/null +++ b/src/IoC/ContainerInterface.php @@ -0,0 +1,17 @@ + + */ +interface ContainerInterface extends Slytherin +{ +} diff --git a/src/System.php b/src/System.php index 19da2a5a..bdfe4c5a 100644 --- a/src/System.php +++ b/src/System.php @@ -8,6 +8,14 @@ use Rougin\Slytherin\Integration\ConfigurationInterface; use Rougin\Slytherin\System\Handler; +/** + * System Application + * + * Integrates all specified components into the application. + * + * @package Slytherin + * @author Rougin Gutib + */ class System { const CONTAINER = 'Psr\Container\ContainerInterface'; From 394c210391b2c732be5e8cb70085e9c7761a8bb4 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 16:08:02 +0800 Subject: [PATCH 36/46] Change details in TwigRenderer --- src/Template/Twig.php | 12 ++++-------- src/Template/Twig/Renderer.php | 4 ---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/Template/Twig.php b/src/Template/Twig.php index f5e6a7ea..3fd056b0 100644 --- a/src/Template/Twig.php +++ b/src/Template/Twig.php @@ -3,17 +3,13 @@ namespace Rougin\Slytherin\Template; /** - * Twig Renderer - * - * A simple implementation of a template renderer that is based on top of - * Sensiolab's Twig - a flexible, fast, and secure template engine for PHP. - * NOTE: To be removed in v1.0.0. Use "Template\TwigRenderer" instead. - * - * http://twig.sensiolabs.org + * Renderer * + * NOTE: To be removed in v1.0.0. Use "TwigRenderer" instead. + * * @package Slytherin * @author Rougin Gutib */ -class Twig extends Twig\Renderer +class Twig extends TwigRenderer { } diff --git a/src/Template/Twig/Renderer.php b/src/Template/Twig/Renderer.php index 68adf604..e487b67a 100644 --- a/src/Template/Twig/Renderer.php +++ b/src/Template/Twig/Renderer.php @@ -7,12 +7,8 @@ /** * Renderer * - * A simple implementation of a template renderer that is based on top of - * Sensiolab's Twig - a flexible, fast, and secure template engine for PHP. * NOTE: To be removed in v1.0.0. Use "Template\TwigRenderer" instead. * - * http://twig.sensiolabs.org - * * @package Slytherin * @author Rougin Gutib */ From 66e4f376f6d1a7251bfee10f35c4101c6b1ec1da Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 16:09:27 +0800 Subject: [PATCH 37/46] Fix styling issues from compatibility fixes --- src/IoC/Auryn.php | 2 +- src/IoC/ContainerInterface.php | 2 +- src/Template/Twig.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IoC/Auryn.php b/src/IoC/Auryn.php index dde3021c..678855be 100644 --- a/src/IoC/Auryn.php +++ b/src/IoC/Auryn.php @@ -6,7 +6,7 @@ * Auryn Container * * NOTE: To be removed in v1.0.0. Use "AurynContainer" instead. - * + * * @package Slytherin * @author Rougin Gutib */ diff --git a/src/IoC/ContainerInterface.php b/src/IoC/ContainerInterface.php index dc325dba..1dd4c6d8 100644 --- a/src/IoC/ContainerInterface.php +++ b/src/IoC/ContainerInterface.php @@ -8,7 +8,7 @@ * Dependency Injection Container Interface * * NOTE: To be removed in v1.0.0. Use "Container\ContainerInterface" instead. - * + * * @package Slytherin * @author Rougin Gutib */ diff --git a/src/Template/Twig.php b/src/Template/Twig.php index 3fd056b0..0a257e1f 100644 --- a/src/Template/Twig.php +++ b/src/Template/Twig.php @@ -6,7 +6,7 @@ * Renderer * * NOTE: To be removed in v1.0.0. Use "TwigRenderer" instead. - * + * * @package Slytherin * @author Rougin Gutib */ From 2da2ee00eb21f629ec0152c6694e4511aeb81c5d Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 17:56:06 +0800 Subject: [PATCH 38/46] Change details in ERRATUM.md, UPGRADE.md --- ERRATUM.md | 23 ++++++++++++++++++++++- UPGRADE.md | 24 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/ERRATUM.md b/ERRATUM.md index 8434f7a1..33b2eb5c 100644 --- a/ERRATUM.md +++ b/ERRATUM.md @@ -2,6 +2,27 @@ The following are the erratum for each `README.md` found from the previous versions: +## 0.9.0 + +This version introduced a PSR-15 implementation based on `http-interop/http-middleware`. With this, kindly add the said package and update the packages using `composer update`: + +``` diff + { + "require": + { + "filp/whoops": "~1.0", ++ "http-interop/http-middleware": "0.4.1", + "nikic/fast-route": "~1.0", + "rdlowrey/auryn": "~1.0", +- "rougin/slytherin": "~0.8.0", ++ "rougin/slytherin": "~0.9.0", + "twig/twig": "~1.0", + "zendframework/zend-diactoros": "~1.0", + "zendframework/zend-stratigility": "~1.0" + } + } +``` + ## 0.4.0 In this version, the `patricklouys/http` has been removed in favor for PSR-07 (`psr/http-message`). With this, kindly add a package that is compliant to PSR-07 (e.g., `zendframework/zend-diactoros`) in the `composer.json`: @@ -29,7 +50,7 @@ Perform `composer update` afterwards to update the specified packages. ### Usage -As per documentation, implementing interfaces are required to use Slytherin components. However in this release, the implemented third-party packages are not included (e.g., `patricklouys/http`, etc.) and needs to be installed manually. Kindly include the said packages in the `composer.json`: +As per documentation, implementing interfaces are required to use Slytherin components. However in this version, the implemented third-party packages are not included (e.g., `patricklouys/http`, etc.) and needs to be installed manually. Kindly include the said packages in the `composer.json`: ``` diff { diff --git a/UPGRADE.md b/UPGRADE.md index 54d7ecaa..fac3977c 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,6 +2,30 @@ Below are the guides when upgrading from specified versions due to backward compatibility breaks: +## From `v0.6.0` to `v0.7.0` + +Although no backward compatibility issues found in Slytherin's code, one of the Slytherin's supported packages, `filp/whoops`, has an issue regarding PHP errors. With this, kindly change its version to `~2.0` in the `composer.json` then perform `composer update` after: + +``` diff + { + "require": + { +- "filp/whoops": "~1.0", ++ "filp/whoops": "~2.0", + } + } +``` + +See the [Issue #341](https://github.com/filp/whoops/issues/341) from the `filp/whoops` repository for reference. + +## From `v0.5.0` to `v0.6.0` + +No known backward compatibility issues found. + +## From `v0.4.0` to `v0.5.0` + +No known backward compatibility issues found. + ## From `v0.3.0` to `v0.4.0` The `v0.4.0` version requires a PSR-07 compliant package. See the `v0.4.0` in `ERRATUM` for updating the `composer.json`. From 4d209b3f0df0b6039771ffeb522d73af16a007e0 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 18:05:56 +0800 Subject: [PATCH 39/46] Change details in UPGRADE.md --- UPGRADE.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UPGRADE.md b/UPGRADE.md index fac3977c..2eac32a1 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,6 +2,14 @@ Below are the guides when upgrading from specified versions due to backward compatibility breaks: +## From `v0.8.0` to `v0.9.0` + +No known backward compatibility issues found. + +## From `v0.7.0` to `v0.8.0` + +No known backward compatibility issues found. + ## From `v0.6.0` to `v0.7.0` Although no backward compatibility issues found in Slytherin's code, one of the Slytherin's supported packages, `filp/whoops`, has an issue regarding PHP errors. With this, kindly change its version to `~2.0` in the `composer.json` then perform `composer update` after: From 355616db39cf26c27a1112a4012799f1010cc803 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Fri, 8 Dec 2023 18:09:09 +0800 Subject: [PATCH 40/46] Add test app for backward compatibility --- src/Template/TwigRenderer.php | 5 ++- tests/Component/Server.php | 27 +++++++++++++ tests/Previous/Handlers/Hello.php | 15 ++++++++ tests/Previous/Plates/Greet.html | 1 + tests/Previous/Plates/Hello.html | 1 + tests/Previous/Router.php | 19 +++++++++ tests/Previous/Routes/Hello.php | 25 ++++++++++++ tests/Previous/Server.php | 64 +++++++++++++++++++++++++++++++ 8 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 tests/Component/Server.php create mode 100644 tests/Previous/Handlers/Hello.php create mode 100644 tests/Previous/Plates/Greet.html create mode 100644 tests/Previous/Plates/Hello.html create mode 100644 tests/Previous/Router.php create mode 100644 tests/Previous/Routes/Hello.php create mode 100644 tests/Previous/Server.php diff --git a/src/Template/TwigRenderer.php b/src/Template/TwigRenderer.php index 34998889..a39c5533 100644 --- a/src/Template/TwigRenderer.php +++ b/src/Template/TwigRenderer.php @@ -51,10 +51,11 @@ public function addGlobal($name, $value) * * @param string $template * @param array $data + * @param string $extension * @return string */ - public function render($template, array $data = array()) + public function render($template, array $data = [], $extension = 'html') { - return $this->twig->render($template, $data); + return $this->twig->render("$template.$extension", $data); } } diff --git a/tests/Component/Server.php b/tests/Component/Server.php new file mode 100644 index 00000000..c7c4f7c6 --- /dev/null +++ b/tests/Component/Server.php @@ -0,0 +1,27 @@ +run(); \ No newline at end of file diff --git a/tests/Previous/Handlers/Hello.php b/tests/Previous/Handlers/Hello.php new file mode 100644 index 00000000..67026daf --- /dev/null +++ b/tests/Previous/Handlers/Hello.php @@ -0,0 +1,15 @@ +getBody()->write('Hello from middleware'); + + return $response; + } +} diff --git a/tests/Previous/Plates/Greet.html b/tests/Previous/Plates/Greet.html new file mode 100644 index 00000000..6769dd60 --- /dev/null +++ b/tests/Previous/Plates/Greet.html @@ -0,0 +1 @@ +Hello world! \ No newline at end of file diff --git a/tests/Previous/Plates/Hello.html b/tests/Previous/Plates/Hello.html new file mode 100644 index 00000000..c7a43bc1 --- /dev/null +++ b/tests/Previous/Plates/Hello.html @@ -0,0 +1 @@ +Hello {{ name }}! \ No newline at end of file diff --git a/tests/Previous/Router.php b/tests/Previous/Router.php new file mode 100644 index 00000000..c2833a77 --- /dev/null +++ b/tests/Previous/Router.php @@ -0,0 +1,19 @@ +addRoute('GET', '/', [ "$name\Hello", 'index' ]); + +$router->addRoute('GET', '/hi/:name', [ "$name\Hello", 'hi' ]); + +// Add the middlewares to a specified route --------------- +$items = array('Rougin\Slytherin\Previous\Handlers\Hello'); + +$router->addRoute('GET', '/hello', function () {}, $items); +// -------------------------------------------------------- + +return $router; diff --git a/tests/Previous/Routes/Hello.php b/tests/Previous/Routes/Hello.php new file mode 100644 index 00000000..446f500d --- /dev/null +++ b/tests/Previous/Routes/Hello.php @@ -0,0 +1,25 @@ +renderer = $renderer; + } + + public function index() + { + return $this->renderer->render('Greet'); + } + + public function hi($name) + { + return $this->renderer->render('Hello', compact('name')); + } +} diff --git a/tests/Previous/Server.php b/tests/Previous/Server.php new file mode 100644 index 00000000..45387d6d --- /dev/null +++ b/tests/Previous/Server.php @@ -0,0 +1,64 @@ +share($twig); +$auryn->alias($renderer, get_class($twig)); +// --------------------------------------------- +$component->setDependencyInjector($auryn); +// ---------------------------------------------- + +// Initialize the ErrorHandlerInterface --- +$whoops = new Whoops(new \Whoops\Run); +$component->setErrorHandler($whoops); +// ---------------------------------------- + +// Initialize the ServerRequestInterface and ResponseInterface --- +$request = ServerRequestFactory::fromGlobals(); +$response = new Response; +$component->setHttp($request, $response); +// --------------------------------------------------------------- + +// Initialize the routing dispatcher interface ----- +$router = require realpath(__DIR__ . '/Router.php'); +$dispatcher = new Dispatcher($router, $response); +$component->setDispatcher($dispatcher); +// ------------------------------------------------- + +// Initialize the middleware ------------------- +$pipe = new MiddlewarePipe; +$middleware = new StratigilityMiddleware($pipe); +$component->setMiddleware($middleware); +// --------------------------------------------- + +// Initialize then run the Application --- +$app = new Application($component); + +$app->run(); +// --------------------------------------- From b5587a1ce363755e39f7a2b22be9ed7060ad1858 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sat, 9 Dec 2023 15:01:30 +0800 Subject: [PATCH 41/46] Add v0.7.2, v0.8.2 backport fix in CHANGELOG.md --- CHANGELOG.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 203b457b..3e4693df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,7 +96,7 @@ All notable changes to `Slytherin` will be documented in this file. ### Fixed - Retrieving a single uploaded file in `ServerRequest::getUploadedFiles` -## [0.9.0](https://github.com/rougin/slytherin/compare/v0.8.1...v0.9.0) - 2017-07-08 +## [0.9.0](https://github.com/rougin/slytherin/compare/v0.8.2...v0.9.0) - 2017-07-08 **NOTE**: This release may break your application if upgrading from `v0.8.0` release. @@ -149,6 +149,13 @@ All notable changes to `Slytherin` will be documented in this file. - Traits (in order to achieve PHP `v5.3.0` as the minimum required version) - `getEnvironment` and `setEnvironment` in `Debug\ErrorHandlerInterface` +## [0.8.2](https://github.com/rougin/slytherin/compare/v0.8.1...v0.8.2) - 2023-12-09 + +**NOTE**: This is a backport fix to lessen backward compatibility issues. + +### Fixed +- Compatibility issues from `v0.7.2` release + ## [0.8.1](https://github.com/rougin/slytherin/compare/v0.8.0...v0.8.1) - 2023-12-08 **NOTE**: This is a backport fix to lessen backward compatibility issues. @@ -160,7 +167,7 @@ All notable changes to `Slytherin` will be documented in this file. - Compatibility issues from `v0.7.1` release - `html` as the default `$fileExtension` instead of `twig` -## [0.8.0](https://github.com/rougin/slytherin/compare/v0.7.1...v0.8.0) - 2016-09-08 +## [0.8.0](https://github.com/rougin/slytherin/compare/v0.7.2...v0.8.0) - 2016-09-08 ### Added - Implementation for [Phroute](https://github.com/mrjgreen/phroute) package @@ -174,6 +181,18 @@ All notable changes to `Slytherin` will be documented in this file. ### Removed - Third party packages in `require-dev` +## [0.7.2](https://github.com/rougin/slytherin/compare/v0.7.1...v0.7.2) - 2023-12-09 + +### Added +- `setTemplate` for setting `RendererInterface` in `Collection` +- `ComponentInterface` for defining Slytherin implementations + +### Changed +- Rework `Collection`, `Collector` + +### Fixed +- Add items in `Auryn\Container` + ## [0.7.1](https://github.com/rougin/slytherin/compare/v0.7.0...v0.7.1) - 2023-12-08 **NOTE**: This is a backport fix to lessen backward compatibility issues. From beddc7f83b4c6b294f95ef27b15aefb3f43f2ae7 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sat, 9 Dec 2023 16:38:30 +0800 Subject: [PATCH 42/46] Fix issue in TwigLoader::load --- src/Template/TwigLoader.php | 10 +++------- tests/Template/Twig/RendererTest.php | 8 ++++---- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Template/TwigLoader.php b/src/Template/TwigLoader.php index c759b1b0..a1fdfd88 100644 --- a/src/Template/TwigLoader.php +++ b/src/Template/TwigLoader.php @@ -22,7 +22,7 @@ public function exists() /** * @param string|string[] $path - * @return \Rougin\Slytherin\Template\RendererInterface + * @return \Twig\Environment */ public function load($path) { @@ -45,11 +45,7 @@ public function load($path) $environment = new \ReflectionClass($environment); - /** - * @var \Twig\Environment - */ - $environment = $environment->newInstance($loader); - - return new TwigRenderer($environment); + /** @var \Twig\Environment */ + return $environment->newInstance($loader); } } diff --git a/tests/Template/Twig/RendererTest.php b/tests/Template/Twig/RendererTest.php index 4ecb8a78..bdc6c0e9 100644 --- a/tests/Template/Twig/RendererTest.php +++ b/tests/Template/Twig/RendererTest.php @@ -32,7 +32,7 @@ protected function doSetUp() $this->markTestSkipped('Twig is not installed.'); } - $path = __DIR__ . '/../../Fixture/Templates'; + $path = realpath(__DIR__ . '/../../Fixture/Templates'); $this->twig = $twig->load((string) $path); @@ -48,7 +48,7 @@ public function testRenderMethod() { $expected = 'This is a text from a template.'; - $rendered = $this->renderer->render('test.php'); + $rendered = $this->renderer->render('test', array(), 'php'); $this->assertEquals($expected, $rendered); } @@ -64,7 +64,7 @@ public function testRenderMethodWithData() $data = array('name' => 'template'); - $rendered = $this->renderer->render('test-with-twig-data.php', $data); + $rendered = $this->renderer->render('test-with-twig-data', $data, 'php'); $this->assertEquals($expected, $rendered); } @@ -84,7 +84,7 @@ public function testRenderMethodWithGlobals() $renderer->addGlobal('test', 'wew'); - $rendered = $renderer->render('test-with-twig-data.php'); + $rendered = $renderer->render('test-with-twig-data', array(), 'php'); $this->assertEquals($expected, $rendered); } From 2a10eac3fd382041e38490ce222cc92bec725963 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sat, 9 Dec 2023 16:41:47 +0800 Subject: [PATCH 43/46] Fix issue in TwigRenderer::render --- src/Template/TwigRenderer.php | 2 +- tests/Component/Server.php | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Template/TwigRenderer.php b/src/Template/TwigRenderer.php index a39c5533..a177e215 100644 --- a/src/Template/TwigRenderer.php +++ b/src/Template/TwigRenderer.php @@ -54,7 +54,7 @@ public function addGlobal($name, $value) * @param string $extension * @return string */ - public function render($template, array $data = [], $extension = 'html') + public function render($template, array $data = array(), $extension = 'html') { return $this->twig->render("$template.$extension", $data); } diff --git a/tests/Component/Server.php b/tests/Component/Server.php index c7c4f7c6..9ad30e91 100644 --- a/tests/Component/Server.php +++ b/tests/Component/Server.php @@ -20,8 +20,6 @@ $components = Collector::get($container, $items, $globals); -var_dump($components); - $app = new Application($components); -$app->run(); \ No newline at end of file +$app->run(); From c254f5125744e0b32cdbfdc6eb4261ef3a5ebd07 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sat, 9 Dec 2023 17:26:36 +0800 Subject: [PATCH 44/46] Fix compatibility issue from v0.8.2 --- CHANGELOG.md | 1 - src/Component/AbstractComponent.php | 42 +--- src/Component/Collection.php | 237 +++++++++++------- src/Component/Collector.php | 142 +++++++---- src/Component/ComponentInterface.php | 20 +- src/Container/AurynContainer.php | 2 +- src/Container/Container.php | 2 +- src/Debug/WhoopsErrorHandler.php | 4 +- src/Middleware/Dispatcher.php | 2 +- src/Middleware/StratigilityDispatcher.php | 1 + src/Routing/Router.php | 12 +- src/System.php | 14 +- src/System/Handler.php | 2 +- src/Template/RendererIntegration.php | 2 +- tests/Application/ApplicationTest.php | 15 +- tests/Application/AurynContainerTest.php | 2 +- tests/Application/ContainerInterfaceTest.php | 2 +- tests/Component/CollectionTest.php | 86 ++++--- tests/Component/Server.php | 5 +- ...onComponent.php => ContainerComponent.php} | 18 +- .../Fixture/Components/DebuggerComponent.php | 9 +- .../Components/DispatcherComponent.php | 29 +-- tests/Fixture/Components/HttpComponent.php | 6 +- .../Components/MiddlewareComponent.php | 9 +- tests/Fixture/Components/SingleComponent.php | 13 - .../Fixture/Components/TemplateComponent.php | 34 +++ tests/Sample/Config/app.php | 5 +- tests/Sample/SampleTest.php | 11 + 28 files changed, 426 insertions(+), 301 deletions(-) rename tests/Fixture/Components/{CollectionComponent.php => ContainerComponent.php} (54%) delete mode 100644 tests/Fixture/Components/SingleComponent.php create mode 100644 tests/Fixture/Components/TemplateComponent.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e4693df..86a08eef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,6 @@ All notable changes to `Slytherin` will be documented in this file. - `UnexpectedValueException` to `BadMethodCallException` in `DispatcherInterface` - Conformed `Middleware` to the official `PSR-15` package (`psr/http-server-middleware`) - `Application` class to `System` -- `ERROR_HANDLER` constant to `ERREPORT` in `System` ### Fixed - Type hinting of all classes using `PHPStan` (up to `level 9`) diff --git a/src/Component/AbstractComponent.php b/src/Component/AbstractComponent.php index 62b5659e..60972a72 100644 --- a/src/Component/AbstractComponent.php +++ b/src/Component/AbstractComponent.php @@ -2,15 +2,10 @@ namespace Rougin\Slytherin\Component; -use Psr\Container\ContainerInterface as PsrContainerInterface; -use Rougin\Slytherin\Container\ContainerInterface; -use Rougin\Slytherin\Integration\Configuration; - /** * Component Abstract * * Methods used for integrating a component to Slytherin. - * NOTE: To be removed in v1.0.0. Use "Integration" instead. * * @package Slytherin * @author Rougin Gutib @@ -18,9 +13,9 @@ abstract class AbstractComponent implements ComponentInterface { /** - * The type of component can be the following: - * dispatcher, error_handler, http, middleware - * + * Type of the component: + * container, dispatcher, debugger, http, middleware, template + * * @var string */ protected $type = ''; @@ -30,35 +25,8 @@ abstract class AbstractComponent implements ComponentInterface * * @return string */ - public function type() - { - // Converts the string from "snake_case" to "camelCase" - $words = ucwords(str_replace('_', ' ', $this->type)); - - return lcfirst(str_replace(' ', '', $words)); - } - - /** - * Defines the specified integration. - * - * @param \Rougin\Slytherin\Container\ContainerInterface $container - * @param \Rougin\Slytherin\Integration\Configuration $config - * @return \Rougin\Slytherin\Container\ContainerInterface - */ - public function define(ContainerInterface $container, Configuration $config) - { - $this->set($container); - - return $container; - } - - /** - * Sets the component. Has also an option to add it to the container. - * - * @param \Psr\Container\ContainerInterface $container - * @return void - */ - public function set(PsrContainerInterface &$container) + public function getType() { + return $this->type; } } diff --git a/src/Component/Collection.php b/src/Component/Collection.php index 4636868a..ca834dd0 100644 --- a/src/Component/Collection.php +++ b/src/Component/Collection.php @@ -2,14 +2,15 @@ namespace Rougin\Slytherin\Component; -use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Container\VanillaContainer; +use Rougin\Slytherin\Container\Container; +use Rougin\Slytherin\Container\ContainerInterface; use Rougin\Slytherin\Debug\ErrorHandlerInterface; use Rougin\Slytherin\Middleware\DispatcherInterface as Middleware; use Rougin\Slytherin\Routing\DispatcherInterface as Routing; use Rougin\Slytherin\System; +use Rougin\Slytherin\Template\RendererInterface; /** * Component Collection @@ -19,190 +20,242 @@ * @package Slytherin * @author Rougin Gutib */ -class Collection extends VanillaContainer +class Collection implements ContainerInterface { /** - * @var \Psr\Container\ContainerInterface + * @var \Rougin\Slytherin\Container\ContainerInterface */ protected $container; /** - * Gets an instance of the container. - * - * @return \Psr\Container\ContainerInterface + * @var mixed[] */ - public function getContainer() + protected $items = array(); + + public function __construct() { - return (is_a($this->container, System::CONTAINER)) ? $this->container : $this; + $this->container = new Container; } /** - * Sets the container. + * Adds a new instance to the container. + * NOTE: To be removed in v1.0.0. Use "set" instead. * - * @param \Psr\Container\ContainerInterface $container + * @param string $id + * @param object $concrete * @return self */ - public function setContainer(ContainerInterface $container) + public function add($id, $concrete) { - $this->container = $container; + return $this->container->set($id, $concrete); + } - return $this; + /** + * {@inheritdoc} + */ + public function get($id) + { + return $this->container->get($id); } /** - * Gets an instance of the dependency injector. - * NOTE: To be removed in v1.0.0. Use "getContainer" instead. + * Gets an instance of the container. * - * @return \Rougin\Slytherin\IoC\ContainerInterface + * @return \Rougin\Slytherin\Container\ContainerInterface */ - public function getDependencyInjector() + public function getContainer() { - return $this->getContainer(); + return $this->container; } /** - * Sets the dependency injector. - * NOTE: To be removed in v1.0.0. Use "setContainer" instead. + * Gets the debugger. * - * @param \Rougin\Slytherin\IoC\ContainerInterface $injector + * @return \Rougin\Slytherin\Debugger\ErrorHandlerInterface */ - public function setDependencyInjector(ContainerInterface $injector) + public function getDebugger() { - return $this->setContainer($injector); + return $this->get(System::DEBUGGER); + } + + /** + * NOTE: To be removed in v1.0.0. Use "getContainer" instead. + * + * @return \Rougin\Slytherin\Container\ContainerInterface + */ + public function getDependencyInjector() + { + return $this->getContainer(); } /** * Gets the dispatcher. * - * @return \Rougin\Slytherin\Routing\DispatcherInterface + * @return \Rougin\Slytherin\Dispatching\DispatcherInterface */ public function getDispatcher() { - /** @var \Rougin\Slytherin\Routing\DispatcherInterface */ return $this->get(System::DISPATCHER); } /** - * Sets the dispatcher. + * NOTE: To be removed in v1.0.0. Use "getDebugger" instead. * - * @param \Rougin\Slytherin\Routing\DispatcherInterface $dispatcher - * @return self + * @return \Rougin\Slytherin\Debug\ErrorHandlerInterface */ - public function setDispatcher(Routing $dispatcher) + public function getErrorHandler() { - $this->set(System::DISPATCHER, $dispatcher); + return $this->getDebugger(); + } - return $this; + /** + * Gets the HTTP components. + * + * @return mixed + */ + public function getHttp() + { + return array($this->getHttpRequest(), $this->getHttpResponse()); } /** - * Gets the debugger. + * Gets the request. * - * @return \Rougin\Slytherin\Debug\ErrorHandlerInterface|null + * @return \Psr\Http\Message\ServerRequestInterface */ - public function getDebugger() + public function getHttpRequest() { - return $this->getErrorHandler(); + return $this->get(System::REQUEST); } /** - * Sets the debugger. + * Gets the response. * - * @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $debugger - * @return self + * @return \Psr\Http\Message\ResponseInterface */ - public function setDebugger(ErrorHandlerInterface $debugger) + public function getHttpResponse() { - $this->setErrorHandler($debugger); + return $this->get(System::RESPONSE); + } - return $this; + /** + * Gets the middleware. + * + * @return \Rougin\Slytherin\Middleware\DispatcherInterface + */ + public function getMiddleware() + { + return $this->get(System::MIDDLEWARE); } /** - * Gets the error handler. + * Gets the template. * - * @return \Rougin\Slytherin\Debug\ErrorHandlerInterface|null + * @return \Rougin\Slytherin\Template\RendererInterface */ - public function getErrorHandler() + public function getTemplate() { - if (! $this->has(System::ERREPORT)) return null; + return $this->get(System::TEMPLATE); + } - /** @var \Rougin\Slytherin\Debug\ErrorHandlerInterface */ - return $this->get(System::ERREPORT); + /** + * {@inheritdoc} + */ + public function has($id) + { + return $this->container->has($id); } /** - * Sets the error handler. + * {@inheritdoc} + */ + public function set($id, $concrete = null) + { + $this->container->set($id, $concrete); + + return $this; + } + + /** + * Sets the container. * - * @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $errorHandler - * @return self + * @param \Rougin\Slytherin\Container\ContainerInterface $container */ - public function setErrorHandler(ErrorHandlerInterface $errorHandler) + public function setContainer(ContainerInterface $container) { - $this->set(System::ERREPORT, $errorHandler); + $this->container = $container; return $this; } /** - * Gets the HTTP components. + * Sets the debugger. * - * @return mixed + * @param \Rougin\Slytherin\Debugger\ErrorHandlerInterface $debugger */ - public function getHttp() + public function setDebugger(ErrorHandlerInterface $debugger) { - return array($this->getHttpRequest(), $this->getHttpResponse()); + return $this->add(System::DEBUGGER, $debugger); } /** - * Sets the HTTP components. + * NOTE: To be removed in v1.0.0. Use "setContainer" instead. * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Psr\Http\Message\ResponseInterface $response + * @param \Rougin\Slytherin\Container\ContainerInterface $injector * @return self */ - public function setHttp(ServerRequestInterface $request, ResponseInterface $response) + public function setDependencyInjector(ContainerInterface $injector) { - $this->set(System::SERVER_REQUEST, $request); - - $this->set(System::RESPONSE, $response); + return $this->setContainer($injector); + } - return $this; + /** + * Sets the dispatcher. + * + * @param \Rougin\Slytherin\Dispatching\DispatcherInterface $dispatcher + * @return self + */ + public function setDispatcher(Routing $dispatcher) + { + return $this->add(System::DISPATCHER, $dispatcher); } /** - * Gets the HTTP request. + * NOTE: To be removed in v1.0.0. Use "setDebugger" instead. * - * @return \Psr\Http\Message\ServerRequestInterface + * @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $debugger + * @return self */ - public function getHttpRequest() + public function setErrorHandler(ErrorHandlerInterface $debugger) { - /** @var \Psr\Http\Message\ServerRequestInterface */ - return $this->get(System::SERVER_REQUEST); + return $this->setDebugger($debugger); } /** - * Sets the HTTP request. + * Sets the HTTP components. * * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response * @return self */ - public function setHttpRequest(ServerRequestInterface $request) + public function setHttp(ServerRequestInterface $request, ResponseInterface $response) { - $this->set(System::SERVER_REQUEST, $request); + $this->add(System::REQUEST, $request); - return $this; + return $this->add(System::RESPONSE, $response); } /** - * Gets the HTTP response. + * Sets the HTTP request. * - * @return \Psr\Http\Message\ResponseInterface + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return self */ - public function getHttpResponse() + public function setHttpRequest(ServerRequestInterface $request) { - /** @var \Psr\Http\Message\ResponseInterface */ - return $this->get(System::RESPONSE); + $this->set(System::REQUEST, $request); + + return $this; } /** @@ -219,32 +272,24 @@ public function setHttpResponse(ResponseInterface $response) } /** - * TODO: Reimplement Middleware package. - * - * Gets the middleware. + * Sets the middleware. * - * @return \Rougin\Slytherin\Middleware\DispatcherInterface|null + * @param \Rougin\Slytherin\Middleware\DispatcherInterface $middleware + * @return self */ - public function getMiddleware() + public function setMiddleware(Middleware $middleware) { - if (! $this->has(System::MIDDLEWARE)) return null; - - /** @var \Rougin\Slytherin\Middleware\DispatcherInterface */ - return $this->get(System::MIDDLEWARE); + return $this->add(System::MIDDLEWARE, $middleware); } /** - * TODO: Reimplement Middleware package. - * - * Sets the middleware. + * Sets the template. * - * @param \Rougin\Slytherin\Middleware\DispatcherInterface $middleware + * @param \Rougin\Slytherin\Template\RendererInterface $template * @return self */ - public function setMiddleware(Middleware $middleware) + public function setTemplate(RendererInterface $template) { - $this->set(System::MIDDLEWARE, $middleware); - - return $this; + return $this->add(System::TEMPLATE, $template); } } diff --git a/src/Component/Collector.php b/src/Component/Collector.php index 3ba17c9b..4bc48655 100644 --- a/src/Component/Collector.php +++ b/src/Component/Collector.php @@ -2,14 +2,13 @@ namespace Rougin\Slytherin\Component; +use Rougin\Slytherin\Container\Container; use Rougin\Slytherin\Container\ContainerInterface; -use Rougin\Slytherin\Integration\Configuration; /** * Component Collector * - * Collects all components into Collection. - * NOTE: To be removed in v1.0.0. Use "Integration" instead. + * Collects all defined components into a Collection. * * @package Slytherin * @author Rougin Gutib @@ -17,72 +16,117 @@ class Collector { /** - * Collects the specified components. + * @var \Rougin\Slytherin\Component\ComponentInterface[] + */ + protected $items = array(); + + /** + * @param \Rougin\Slytherin\Component\ComponentInterface[]|string[] $items + */ + public function __construct(array $items = array()) + { + foreach ($items as $item) + { + if (is_string($item)) + { + /** @var \Rougin\Slytherin\Component\ComponentInterface */ + $item = new $item; + } + + array_push($this->items, $item); + } + } + + /** + * Generates a Collection instance. * - * @param \Rougin\Slytherin\Container\ContainerInterface $container - * @param string[] $components - * @param array|null $globals + * @param * @return \Rougin\Slytherin\Component\Collection */ - public static function get(ContainerInterface $container, array $components = array(), &$globals = null) + public function make(ContainerInterface $container) { - $configuration = new Configuration; - $collection = new Collection; - foreach ($components as $component) + $collection->setContainer($container); + + // If there is a defined container, set it first ----------- + foreach ($this->items as $item) { - $instance = self::prepare($collection, $component); + if ($item->getType() === 'container') + { + /** @var \Rougin\Slytherin\IoC\ContainerInterface */ + $result = $item->get(); - $container = $instance->define($container, $configuration); + $collection->setDependencyInjector($result); + } } + // --------------------------------------------------------- - $collection->setContainer($container); - - // NOTE: To be removed in v1.0.0. Use Application::container instead. --- - if ($globals) + foreach ($this->items as $item) { - $globals['container'] = $container; + if ($item->getType() === 'dispatcher') + { + /** @var \Rougin\Slytherin\Dispatching\DispatcherInterface */ + $result = $item->get(); + + $collection->setDispatcher($result); + } + + if (in_array($item->getType(), array('debugger', 'error_handler'))) + { + /** @var \Rougin\Slytherin\Debug\DebuggerInterface */ + $result = $item->get(); + + $collection->setErrorHandler($result); + } + + if ($item->getType() === 'http') + { + /** @var array */ + $result = $item->get(); + + /** @var \Psr\Http\Message\ServerRequestInterface */ + $request = $result[0]; + + /** @var \Psr\Http\Message\ResponseInterface */ + $response = $result[1]; + + $collection->setHttp($request, $response); + } + + if ($item->getType() === 'middleware') + { + /** @var \Rougin\Slytherin\Middleware\MiddlewareInterface */ + $result = $item->get(); + + $collection->setMiddleware($result); + } + + if ($item->getType() === 'template') + { + /** @var \Rougin\Slytherin\Template\RendererInterface */ + $result = $item->get(); + + $collection->setTemplate($result); + } } - // ---------------------------------------------------------------------- return $collection; } /** - * Prepares the component and sets it to the collection. - * - * @param \Rougin\Slytherin\Component\Collection &$collection - * @param string $component - * @return \Rougin\Slytherin\Integration\IntegrationInterface + * Collects the specified components. + * + * @param \Rougin\Slytherin\Component\ComponentInterface[]|string[] $components + * @param \Rougin\Slytherin\IoC\ContainerInterface $container + * @return \Rougin\Slytherin\Component\Collection */ - protected static function prepare(Collection &$collection, $component) + public static function get(array $components, ContainerInterface $container = null) { - /** @var class-string $component */ - $instance = new $component; - - /** @var callable */ - $class = array($instance, 'type'); - $type = call_user_func($class); - - if (empty($type)) - { - /** @var \Rougin\Slytherin\Integration\IntegrationInterface */ - return $instance; - } - - /** @var callable */ - $class = array($instance, 'get'); - $args = call_user_func($class); - - if ($type !== 'http') $args = array($args); - - /** @var callable */ - $class = array($collection, 'set' . ucfirst($type)); + $self = new Collector($components); - call_user_func_array($class, $args); + if (! $container) $container = new Container; - /** @var \Rougin\Slytherin\Integration\IntegrationInterface */ - return $instance; + return $self->make($container); } } diff --git a/src/Component/ComponentInterface.php b/src/Component/ComponentInterface.php index 0a346004..558c783a 100644 --- a/src/Component/ComponentInterface.php +++ b/src/Component/ComponentInterface.php @@ -2,25 +2,27 @@ namespace Rougin\Slytherin\Component; -use Psr\Container\ContainerInterface; -use Rougin\Slytherin\Integration\IntegrationInterface; - /** * Component Interface * * An interface for handling components. - * NOTE: To be removed in v1.0.0. Use "Integration" instead. * * @package Slytherin * @author Rougin Gutib */ -interface ComponentInterface extends IntegrationInterface +interface ComponentInterface { /** - * Sets the component. Can also add it to the container. + * Returns an instance from the named class. * - * @param \Psr\Container\ContainerInterface &$container - * @return void + * @return mixed + */ + public function get(); + + /** + * Returns the type of the component. + * + * @return string */ - public function set(ContainerInterface &$container); + public function getType(); } diff --git a/src/Container/AurynContainer.php b/src/Container/AurynContainer.php index e7342b98..cf4dd970 100644 --- a/src/Container/AurynContainer.php +++ b/src/Container/AurynContainer.php @@ -58,7 +58,7 @@ public function __construct($data = null) /** * Adds a new instance to the container. - * NOTE: To be removed in v1.0.0. Use $this->set() instead. + * NOTE: To be removed in v1.0.0. Use "set" instead. * * @param string $id * @param mixed|null $concrete diff --git a/src/Container/Container.php b/src/Container/Container.php index 7a6644d6..f1c9fd49 100644 --- a/src/Container/Container.php +++ b/src/Container/Container.php @@ -49,7 +49,7 @@ public function __construct(array $instances = array(), PsrContainerInterface $c /** * Adds a new instance to the container. - * NOTE: To be removed in v1.0.0. Use $this->set() instead. + * NOTE: To be removed in v1.0.0. Use "set" instead. * * @param string $id * @param object $concrete diff --git a/src/Debug/WhoopsErrorHandler.php b/src/Debug/WhoopsErrorHandler.php index d91c989b..da78781d 100644 --- a/src/Debug/WhoopsErrorHandler.php +++ b/src/Debug/WhoopsErrorHandler.php @@ -66,7 +66,7 @@ public function getEnvironment() /** * Returns a listing of handlers. - * NOTE: To be removed in v1.0.0. Use __call() instead. + * NOTE: To be removed in v1.0.0. Use __call" instead. * * @return \Whoops\Handler\HandlerInterface[] */ @@ -93,7 +93,7 @@ public function display() /** * Sets a handler. - * NOTE: To be removed in v1.0.0. Use __call() instead. + * NOTE: To be removed in v1.0.0. Use __call" instead. * * @param \Whoops\Handler\HandlerInterface|callable $handler * @return void diff --git a/src/Middleware/Dispatcher.php b/src/Middleware/Dispatcher.php index ff578d1b..6e333a1f 100644 --- a/src/Middleware/Dispatcher.php +++ b/src/Middleware/Dispatcher.php @@ -90,7 +90,7 @@ public function setStack($stack) } /** - * NOTE: To be removed in v1.0.0. Use $this->getStack() instead. + * NOTE: To be removed in v1.0.0. Use "getStack" instead. * * @return \Rougin\Slytherin\Middleware\MiddlewareInterface[] */ diff --git a/src/Middleware/StratigilityDispatcher.php b/src/Middleware/StratigilityDispatcher.php index 8b555b16..ca640550 100644 --- a/src/Middleware/StratigilityDispatcher.php +++ b/src/Middleware/StratigilityDispatcher.php @@ -143,6 +143,7 @@ protected function setMiddleware(MiddlewareInterface $item) /** * @param callable $item * @return object + * @codeCoverageIgnore */ protected function setPsrMiddleware($item) { diff --git a/src/Routing/Router.php b/src/Routing/Router.php index c51a2e13..fb9dbfa9 100644 --- a/src/Routing/Router.php +++ b/src/Routing/Router.php @@ -98,7 +98,7 @@ public function add($method, $uri, $handler, $middlewares = array()) /** * Adds a new raw route. - * NOTE: To be removed in v1.0.0. Use $this->add() instead. + * NOTE: To be removed in v1.0.0. Use "add" instead. * * @param string $method * @param string $route @@ -113,7 +113,7 @@ public function addRoute($method, $route, $handler, $middlewares = array()) /** * Merges a listing of parsed routes to current one. - * NOTE: To be removed in v1.0.0. Use $this->merge() instead. + * NOTE: To be removed in v1.0.0. Use "merge" instead. * * @param \Rougin\Slytherin\Routing\RouteInterface[] $routes * @return self @@ -174,7 +174,7 @@ public function get($uri, $handler, $middlewares = array()) /** * Returns a specific route based on the specified HTTP method and URI. - * NOTE: To be removed in v1.0.0. Use $this->retrieve() instead. + * NOTE: To be removed in v1.0.0. Use "retrieve" instead. * * @param string $method * @param string $uri @@ -187,7 +187,7 @@ public function getRoute($method, $uri) /** * Returns a listing of available routes. - * NOTE: To be removed in v1.0.0. Use $this->routes() instead. + * NOTE: To be removed in v1.0.0. Use "routes" instead. * * @return \Rougin\Slytherin\Routing\RouteInterface[] */ @@ -319,7 +319,7 @@ public function restful($uri, $class, $middlewares = array()) /** * Finds a specific route based on the specified HTTP method and URI. - * NOTE: To be removed in v1.0.0. Use $this->find() instead. + * NOTE: To be removed in v1.0.0. Use "find" instead. * * @param string $method * @param string $uri @@ -342,7 +342,7 @@ public function routes() /** * Sets a prefix for the succeeding route endpoints. - * NOTE: To be removed in v1.0.0. Use $this->prefix() instead. + * NOTE: To be removed in v1.0.0. Use "prefix" instead. * * @param string $prefix * @param string $namespace diff --git a/src/System.php b/src/System.php index bdfe4c5a..19df7ae4 100644 --- a/src/System.php +++ b/src/System.php @@ -22,17 +22,19 @@ class System const DISPATCHER = 'Rougin\Slytherin\Routing\DispatcherInterface'; - const ERREPORT = 'Rougin\Slytherin\Ereport\EreportInterface'; + // TODO: Implement Error Handler ------------------------------- + const DEBUGGER = 'Rougin\Slytherin\Debug\ErrorHandlerInterface'; + // ------------------------------------------------------------- const MIDDLEWARE = 'Rougin\Slytherin\Middleware\DispatcherInterface'; - const RENDERER = 'Rougin\Slytherin\Template\RendererInterface'; + const REQUEST = 'Psr\Http\Message\ServerRequestInterface'; - const ROUTER = 'Rougin\Slytherin\Routing\RouterInterface'; + const RESPONSE = 'Psr\Http\Message\ResponseInterface'; - const SERVER_REQUEST = 'Psr\Http\Message\ServerRequestInterface'; + const ROUTER = 'Rougin\Slytherin\Routing\RouterInterface'; - const RESPONSE = 'Psr\Http\Message\ResponseInterface'; + const TEMPLATE = 'Rougin\Slytherin\Template\RendererInterface'; /** * @var \Rougin\Slytherin\Integration\Configuration @@ -153,7 +155,7 @@ public function integrate($integrations, ConfigurationInterface $config = null) public function run() { /** @var \Psr\Http\Message\ServerRequestInterface */ - $request = $this->container->get(self::SERVER_REQUEST); + $request = $this->container->get(self::REQUEST); echo (string) $this->emit($request)->getBody(); } diff --git a/src/System/Handler.php b/src/System/Handler.php index 903dcea3..fd23a2d8 100644 --- a/src/System/Handler.php +++ b/src/System/Handler.php @@ -49,7 +49,7 @@ public function __construct(RouteInterface $route, ContainerInterface $container public function handle(ServerRequestInterface $request) { // Attach the request again in the container to reflect from stack --- - $this->container->set(System::SERVER_REQUEST, $request); + $this->container->set(System::REQUEST, $request); // ------------------------------------------------------------------- $resolver = new Resolver($this->container); diff --git a/src/Template/RendererIntegration.php b/src/Template/RendererIntegration.php index 14bcb6f9..dca2d8ac 100644 --- a/src/Template/RendererIntegration.php +++ b/src/Template/RendererIntegration.php @@ -49,7 +49,7 @@ public function define(ContainerInterface $container, Configuration $config) $renderer = $twig->load($path); } - $container->set(Application::RENDERER, $renderer); + $container->set(Application::TEMPLATE, $renderer); return $container; } diff --git a/tests/Application/ApplicationTest.php b/tests/Application/ApplicationTest.php index 4e200b7c..a5d86f07 100644 --- a/tests/Application/ApplicationTest.php +++ b/tests/Application/ApplicationTest.php @@ -43,19 +43,18 @@ protected function doSetUp() $items = array(); - $items[] = 'Rougin\Slytherin\Fixture\Components\CollectionComponent'; + $items[] = 'Rougin\Slytherin\Fixture\Components\ContainerComponent'; $items[] = 'Rougin\Slytherin\Fixture\Components\DebuggerComponent'; $items[] = 'Rougin\Slytherin\Fixture\Components\DispatcherComponent'; $items[] = 'Rougin\Slytherin\Fixture\Components\HttpComponent'; - $items[] = 'Rougin\Slytherin\Fixture\Components\SingleComponent'; + $items[] = 'Rougin\Slytherin\Fixture\Components\TemplateComponent'; - $container = new Container; - - $globals = $GLOBALS; - - $components = Collector::get($container, $items, $globals); + if (class_exists('Zend\Stratigility\MiddlewarePipe')) + { + $items[] = 'Rougin\Slytherin\Fixture\Components\MiddlewareComponent'; + } - $this->components = $components; + $this->components = Collector::get($items); } /** diff --git a/tests/Application/AurynContainerTest.php b/tests/Application/AurynContainerTest.php index 5d249aee..9c013f7d 100644 --- a/tests/Application/AurynContainerTest.php +++ b/tests/Application/AurynContainerTest.php @@ -33,7 +33,7 @@ protected function doSetUp() $router = $this->router(); $container->share($this->request('GET', '/')); - $container->alias(System::SERVER_REQUEST, 'Rougin\Slytherin\Http\ServerRequest'); + $container->alias(System::REQUEST, 'Rougin\Slytherin\Http\ServerRequest'); $container->share(new Response); $container->alias(System::RESPONSE, 'Rougin\Slytherin\Http\Response'); diff --git a/tests/Application/ContainerInterfaceTest.php b/tests/Application/ContainerInterfaceTest.php index 496fdc9d..defba307 100644 --- a/tests/Application/ContainerInterfaceTest.php +++ b/tests/Application/ContainerInterfaceTest.php @@ -30,7 +30,7 @@ protected function doSetUp() $container->set(System::DISPATCHER, $dispatcher); $request = $this->request('GET', '/'); - $container->set(System::SERVER_REQUEST, $request); + $container->set(System::REQUEST, $request); $response = new Response; $container->set(System::RESPONSE, $response); diff --git a/tests/Component/CollectionTest.php b/tests/Component/CollectionTest.php index 8977118f..3742b42e 100644 --- a/tests/Component/CollectionTest.php +++ b/tests/Component/CollectionTest.php @@ -11,6 +11,8 @@ use Rougin\Slytherin\IoC\Vanilla\Container; use Rougin\Slytherin\Middleware\Interop; use Rougin\Slytherin\Middleware\VanillaMiddleware; +use Rougin\Slytherin\Template\TwigLoader; +use Rougin\Slytherin\Template\TwigRenderer; use Rougin\Slytherin\Testcase; /** @@ -43,11 +45,6 @@ protected function doSetUp() */ public function testSetContainerMethod() { - if (! interface_exists('Psr\Container\ContainerInterface')) - { - $this->markTestSkipped('Container Interop is not installed.'); - } - $expected = new Container; $this->components->setContainer($expected); @@ -57,6 +54,22 @@ public function testSetContainerMethod() $this->assertEquals($expected, $actual); } + /** + * Tests the setDependencyInjector() method. + * + * @return void + */ + public function testSetDependencyInjectorMethod() + { + $expected = new Container; + + $this->components->setDependencyInjector($expected); + + $actual = $this->components->getDependencyInjector(); + + $this->assertEquals($expected, $actual); + } + /** * Tests the setDispatcher() method. * @@ -64,9 +77,7 @@ public function testSetContainerMethod() */ public function testSetDispatcherMethod() { - $router = new Router; - - $expected = new Dispatcher($router); + $expected = new Dispatcher(new Router); $this->components->setDispatcher($expected); @@ -91,6 +102,22 @@ public function testSetDebuggerMethod() $this->assertEquals($expected, $actual); } + /** + * Tests the setErrorHandler() method. + * + * @return void + */ + public function testSetErrorHandlerMethod() + { + $expected = new Debugger; + + $this->components->setErrorHandler($expected); + + $actual = $this->components->getErrorHandler(); + + $this->assertEquals($expected, $actual); + } + /** * Tests the setHttp() method. * @@ -98,11 +125,6 @@ public function testSetDebuggerMethod() */ public function testSetHttpMethod() { - if (! interface_exists('Psr\Http\Message\ResponseInterface')) - { - $this->markTestSkipped('PSR HTTP Message is not installed.'); - } - $server = array(); $server['REQUEST_METHOD'] = 'GET'; $server['REQUEST_URI'] = '/'; @@ -129,11 +151,6 @@ public function testSetHttpMethod() */ public function testSetHttpRequestMethod() { - if (! interface_exists('Psr\Http\Message\ServerRequestInterface')) - { - $this->markTestSkipped('PSR HTTP Message is not installed.'); - } - $server = array(); $server['REQUEST_METHOD'] = 'GET'; $server['REQUEST_URI'] = '/'; @@ -156,11 +173,6 @@ public function testSetHttpRequestMethod() */ public function testSetHttpResponseMethod() { - if (! interface_exists('Psr\Http\Message\ResponseInterface')) - { - $this->markTestSkipped('PSR HTTP Message is not installed.'); - } - $expected = new Response; $this->components->setHttpResponse($expected); @@ -177,11 +189,6 @@ public function testSetHttpResponseMethod() */ public function testSetMiddlewareMethod() { - if (! interface_exists('Psr\Http\Message\ResponseInterface')) - { - $this->markTestSkipped('PSR HTTP Message is not installed.'); - } - if (! Interop::exists()) { $this->markTestSkipped('Interop middleware/s not yet installed'); @@ -197,12 +204,29 @@ public function testSetMiddlewareMethod() } /** - * Tests if get() returns null. + * Tests the setMiddleware() method. * * @return void */ - public function testGetNullComponent() + public function testSetTemplateMethod() { - $this->assertNull($this->components->getDebugger()); + $twig = new TwigLoader; + + if (! $twig->exists()) + { + $this->markTestSkipped('Twig is not installed.'); + } + + $path = realpath(__DIR__ . '/../../Fixture/Templates'); + + $environment = $twig->load($path); + + $expected = new TwigRenderer($environment); + + $this->components->setTemplate($expected); + + $actual = $this->components->getTemplate(); + + $this->assertEquals($expected, $actual); } } diff --git a/tests/Component/Server.php b/tests/Component/Server.php index 9ad30e91..e0ccfc91 100644 --- a/tests/Component/Server.php +++ b/tests/Component/Server.php @@ -2,7 +2,6 @@ use Rougin\Slytherin\Application; use Rougin\Slytherin\Component\Collector; -use Rougin\Slytherin\Container\Container; $root = dirname(dirname(__DIR__)); @@ -16,9 +15,7 @@ $items[] = 'Rougin\Slytherin\Fixture\Components\HttpComponent'; $items[] = 'Rougin\Slytherin\Fixture\Components\SingleComponent'; -$container = new Container; - -$components = Collector::get($container, $items, $globals); +$components = Collector::get($items); $app = new Application($components); diff --git a/tests/Fixture/Components/CollectionComponent.php b/tests/Fixture/Components/ContainerComponent.php similarity index 54% rename from tests/Fixture/Components/CollectionComponent.php rename to tests/Fixture/Components/ContainerComponent.php index 85ae6337..98fb95e3 100644 --- a/tests/Fixture/Components/CollectionComponent.php +++ b/tests/Fixture/Components/ContainerComponent.php @@ -3,18 +3,24 @@ namespace Rougin\Slytherin\Fixture\Components; use Rougin\Slytherin\Component\AbstractComponent; - -use Rougin\Slytherin\Fixture\TestClass; -use Rougin\Slytherin\Fixture\TestClassWithEmptyConstructor; +use Rougin\Slytherin\Container\Container; /** - * Collection Component + * Container Component * * @package Slytherin * @author Rougin Gutib */ -class CollectionComponent extends AbstractComponent +class ContainerComponent extends AbstractComponent { + /** + * Type of the component: + * container, dispatcher, debugger, http, middleware, template + * + * @var string + */ + protected $type = 'container'; + /** * Returns an instance from the named class. * It's used in supporting component types for Slytherin. @@ -23,6 +29,6 @@ class CollectionComponent extends AbstractComponent */ public function get() { - return array(new TestClass, new TestClassWithEmptyConstructor); + return new Container; } } diff --git a/tests/Fixture/Components/DebuggerComponent.php b/tests/Fixture/Components/DebuggerComponent.php index e530c183..cb19cd36 100644 --- a/tests/Fixture/Components/DebuggerComponent.php +++ b/tests/Fixture/Components/DebuggerComponent.php @@ -2,17 +2,20 @@ namespace Rougin\Slytherin\Fixture\Components; +use Rougin\Slytherin\Component\AbstractComponent; +use Rougin\Slytherin\Debug\Vanilla\Debugger; + /** * Debugger Component * * @package Slytherin * @author Rougin Gutib */ -class DebuggerComponent extends \Rougin\Slytherin\Component\AbstractComponent +class DebuggerComponent extends AbstractComponent { /** * Type of the component: - * dispatcher, debugger, http, middleware + * container, dispatcher, debugger, http, middleware, template * * @var string */ @@ -26,7 +29,7 @@ class DebuggerComponent extends \Rougin\Slytherin\Component\AbstractComponent */ public function get() { - $debugger = new \Rougin\Slytherin\Debug\Vanilla\Debugger; + $debugger = new Debugger; $debugger->setEnvironment('development'); diff --git a/tests/Fixture/Components/DispatcherComponent.php b/tests/Fixture/Components/DispatcherComponent.php index 6fedcc60..912b369d 100644 --- a/tests/Fixture/Components/DispatcherComponent.php +++ b/tests/Fixture/Components/DispatcherComponent.php @@ -2,6 +2,7 @@ namespace Rougin\Slytherin\Fixture\Components; +use Rougin\Slytherin\Component\AbstractComponent; use Rougin\Slytherin\Dispatching\Vanilla\Router; use Rougin\Slytherin\Dispatching\Vanilla\Dispatcher; @@ -11,11 +12,11 @@ * @package Slytherin * @author Rougin Gutib */ -class DispatcherComponent extends \Rougin\Slytherin\Component\AbstractComponent +class DispatcherComponent extends AbstractComponent { /** * Type of the component: - * dispatcher, debugger, http, middleware + * container, dispatcher, debugger, http, middleware, template * * @var string */ @@ -29,18 +30,18 @@ class DispatcherComponent extends \Rougin\Slytherin\Component\AbstractComponent */ public function get() { - $routes = array( - array('GET', '/', array('Rougin\Slytherin\Fixture\Classes\NewClass', 'index')), - array('GET', '/optional', array('Rougin\Slytherin\Fixture\Classes\WithOptionalParameter', 'index')), - array('GET', '/parameter', array('Rougin\Slytherin\Fixture\Classes\WithParameter', 'index')), - array('GET', '/hello', array('Rougin\Slytherin\Fixture\Classes\WithResponseInterface', 'index')), - array('GET', '/error', array('Rougin\Slytherin\Fixture\Classes\WithResponseInterface', 'error')), - array('GET', '/middleware', array('Rougin\Slytherin\Fixture\Classes\NewClass', 'index'), 'Rougin\Slytherin\Fixture\Middlewares\LastMiddleware'), - array('PUT', '/hello', array('Rougin\Slytherin\Fixture\Classes\WithPutHttpMethod', 'index')), - array('GET', '/callback', function () { - return 'Hello'; - }), - ); + $routes = array(); + + $last = 'Rougin\Slytherin\Fixture\Middlewares\LastMiddleware'; + + $routes[] = array('GET', '/', array('Rougin\Slytherin\Fixture\Classes\NewClass', 'index')); + $routes[] = array('GET', '/optional', array('Rougin\Slytherin\Fixture\Classes\WithOptionalParameter', 'index')); + $routes[] = array('GET', '/parameter', array('Rougin\Slytherin\Fixture\Classes\WithParameter', 'index')); + $routes[] = array('GET', '/hello', array('Rougin\Slytherin\Fixture\Classes\WithResponseInterface', 'index')); + $routes[] = array('GET', '/error', array('Rougin\Slytherin\Fixture\Classes\WithResponseInterface', 'error')); + $routes[] = array('GET', '/middleware', array('Rougin\Slytherin\Fixture\Classes\NewClass', 'index'), $last); + $routes[] = array('PUT', '/hello', array('Rougin\Slytherin\Fixture\Classes\WithPutHttpMethod', 'index')); + $routes[] = array('GET', '/callback', function () { return 'Hello'; }); return new Dispatcher(new Router($routes)); } diff --git a/tests/Fixture/Components/HttpComponent.php b/tests/Fixture/Components/HttpComponent.php index 6dff80f0..cf3ca909 100644 --- a/tests/Fixture/Components/HttpComponent.php +++ b/tests/Fixture/Components/HttpComponent.php @@ -2,17 +2,19 @@ namespace Rougin\Slytherin\Fixture\Components; +use Rougin\Slytherin\Component\AbstractComponent; + /** * HTTP Component * * @package Slytherin * @author Rougin Gutib */ -class HttpComponent extends \Rougin\Slytherin\Component\AbstractComponent +class HttpComponent extends AbstractComponent { /** * Type of the component: - * dispatcher, debugger, http, middleware + * container, dispatcher, debugger, http, middleware, template * * @var string */ diff --git a/tests/Fixture/Components/MiddlewareComponent.php b/tests/Fixture/Components/MiddlewareComponent.php index cd332faf..ff76d110 100644 --- a/tests/Fixture/Components/MiddlewareComponent.php +++ b/tests/Fixture/Components/MiddlewareComponent.php @@ -2,7 +2,8 @@ namespace Rougin\Slytherin\Fixture\Components; -use Rougin\Slytherin\Middleware\Stratigility\Middleware; +use Rougin\Slytherin\Component\AbstractComponent; +use Rougin\Slytherin\Middleware\Middleware; /** * Middleware Component @@ -10,11 +11,11 @@ * @package Slytherin * @author Rougin Gutib */ -class MiddlewareComponent extends \Rougin\Slytherin\Component\AbstractComponent +class MiddlewareComponent extends AbstractComponent { /** * Type of the component: - * dispatcher, debugger, http, middleware + * container, dispatcher, debugger, http, middleware, template * * @var string */ @@ -28,6 +29,6 @@ class MiddlewareComponent extends \Rougin\Slytherin\Component\AbstractComponent */ public function get() { - return new Middleware(new \Zend\Stratigility\MiddlewarePipe); + return new Middleware; } } diff --git a/tests/Fixture/Components/SingleComponent.php b/tests/Fixture/Components/SingleComponent.php deleted file mode 100644 index d8d0a0b7..00000000 --- a/tests/Fixture/Components/SingleComponent.php +++ /dev/null @@ -1,13 +0,0 @@ - - */ -class SingleComponent extends \Rougin\Slytherin\Component\AbstractComponent -{ -} diff --git a/tests/Fixture/Components/TemplateComponent.php b/tests/Fixture/Components/TemplateComponent.php new file mode 100644 index 00000000..2327bc71 --- /dev/null +++ b/tests/Fixture/Components/TemplateComponent.php @@ -0,0 +1,34 @@ + + */ +class TemplateComponent extends AbstractComponent +{ + /** + * Type of the component: + * container, dispatcher, debugger, http, middleware, template + * + * @var string + */ + protected $type = 'template'; + + /** + * Returns an instance from the named class. + * It's used in supporting component types for Slytherin. + * + * @return mixed + */ + public function get() + { + return new Renderer(array()); + } +} diff --git a/tests/Sample/Config/app.php b/tests/Sample/Config/app.php index bd721d9e..bab4c84b 100644 --- a/tests/Sample/Config/app.php +++ b/tests/Sample/Config/app.php @@ -4,11 +4,10 @@ $packages = array(); -// Testing Packages ----------------------------------------------- +// Testing Packages -------------------------------------------- $packages[] = 'Rougin\Slytherin\Sample\Packages\HttpPackage'; -$packages[] = 'Rougin\Slytherin\Sample\Packages\MiddlewarePackage'; $packages[] = 'Rougin\Slytherin\Sample\Packages\RoutingPackage'; -// ---------------------------------------------------------------- +// ------------------------------------------------------------- $config = array('name' => 'Slytherin'); diff --git a/tests/Sample/SampleTest.php b/tests/Sample/SampleTest.php index d24e4e89..9b44c8a0 100644 --- a/tests/Sample/SampleTest.php +++ b/tests/Sample/SampleTest.php @@ -6,6 +6,7 @@ use Rougin\Slytherin\Sample\Builder; use Rougin\Slytherin\Sample\Handlers\Parsed\Request; use Rougin\Slytherin\Sample\Handlers\Parsed\Response; +use Rougin\Slytherin\Sample\Packages\MiddlewarePackage; use Rougin\Slytherin\Sample\Packages\SamplePackage; use Rougin\Slytherin\Testcase; @@ -166,6 +167,8 @@ public function test_callable_as_the_route_with_params_and_string_only_as_the_ou */ public function test_middleware_changing_the_request_constructor() { + $this->builder->addPackage(new MiddlewarePackage); + $this->builder->addHandler(new Request); $this->builder->setUrl('GET', '/handler/conts'); @@ -182,6 +185,8 @@ public function test_middleware_changing_the_request_constructor() */ public function test_middleware_changing_the_request_parameter() { + $this->builder->addPackage(new MiddlewarePackage); + $this->builder->addHandler(new Request); $this->builder->setUrl('GET', '/handler/param'); @@ -198,6 +203,8 @@ public function test_middleware_changing_the_request_parameter() */ public function test_middleware_changing_the_response_parameter() { + $this->builder->addPackage(new MiddlewarePackage); + $this->builder->addHandler(new Response); $this->builder->setUrl('GET', '/response'); @@ -214,6 +221,8 @@ public function test_middleware_changing_the_response_parameter() */ public function test_callable_middleware_changing_the_response_parameter() { + $this->builder->addPackage(new MiddlewarePackage); + $this->builder->setUrl('GET', '/middleware'); $this->expectOutputString('From callable middleware!'); @@ -228,6 +237,8 @@ public function test_callable_middleware_changing_the_response_parameter() */ public function test_interop_middleware_changing_the_response_parameter() { + $this->builder->addPackage(new MiddlewarePackage); + if (! Interop::exists()) { $this->markTestSkipped('Interop middleware/s not yet installed'); From 67ad74438f7f2042984261d07ab8a0f6db0ff8a0 Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sat, 9 Dec 2023 17:35:07 +0800 Subject: [PATCH 45/46] Improve type hinting in Component --- src/Component/AbstractComponent.php | 2 +- src/Component/Collection.php | 35 ++++++++++++++++++++++------ src/Component/Collector.php | 6 ++--- src/Component/ComponentInterface.php | 2 +- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/Component/AbstractComponent.php b/src/Component/AbstractComponent.php index 60972a72..36b67ae9 100644 --- a/src/Component/AbstractComponent.php +++ b/src/Component/AbstractComponent.php @@ -15,7 +15,7 @@ abstract class AbstractComponent implements ComponentInterface /** * Type of the component: * container, dispatcher, debugger, http, middleware, template - * + * * @var string */ protected $type = ''; diff --git a/src/Component/Collection.php b/src/Component/Collection.php index ca834dd0..1ba81301 100644 --- a/src/Component/Collection.php +++ b/src/Component/Collection.php @@ -47,14 +47,20 @@ public function __construct() */ public function add($id, $concrete) { - return $this->container->set($id, $concrete); + return $this->set($id, $concrete); } /** - * {@inheritdoc} + * Finds an entry of the container by its identifier and returns it. + * + * @param string $id + * @return object + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\Container\ContainerExceptionInterface */ public function get($id) { + /** @var object */ return $this->container->get($id); } @@ -71,10 +77,11 @@ public function getContainer() /** * Gets the debugger. * - * @return \Rougin\Slytherin\Debugger\ErrorHandlerInterface + * @return \Rougin\Slytherin\Debug\ErrorHandlerInterface */ public function getDebugger() { + /** @var \Rougin\Slytherin\Debug\ErrorHandlerInterface */ return $this->get(System::DEBUGGER); } @@ -95,6 +102,7 @@ public function getDependencyInjector() */ public function getDispatcher() { + /** @var \Rougin\Slytherin\Dispatching\DispatcherInterface */ return $this->get(System::DISPATCHER); } @@ -125,6 +133,7 @@ public function getHttp() */ public function getHttpRequest() { + /** @var \Psr\Http\Message\ServerRequestInterface */ return $this->get(System::REQUEST); } @@ -135,6 +144,7 @@ public function getHttpRequest() */ public function getHttpResponse() { + /** @var \Psr\Http\Message\ResponseInterface */ return $this->get(System::RESPONSE); } @@ -145,6 +155,7 @@ public function getHttpResponse() */ public function getMiddleware() { + /** @var \Rougin\Slytherin\Middleware\DispatcherInterface */ return $this->get(System::MIDDLEWARE); } @@ -155,11 +166,15 @@ public function getMiddleware() */ public function getTemplate() { + /** @var \Rougin\Slytherin\Template\RendererInterface */ return $this->get(System::TEMPLATE); } /** - * {@inheritdoc} + * Returns true if the container can return an entry for the given identifier. + * + * @param string $id + * @return boolean */ public function has($id) { @@ -167,7 +182,11 @@ public function has($id) } /** - * {@inheritdoc} + * Sets a new instance to the container. + * + * @param string $id + * @param mixed $concrete + * @return self */ public function set($id, $concrete = null) { @@ -179,7 +198,8 @@ public function set($id, $concrete = null) /** * Sets the container. * - * @param \Rougin\Slytherin\Container\ContainerInterface $container + * @param \Rougin\Slytherin\Container\ContainerInterface $container + * @return self */ public function setContainer(ContainerInterface $container) { @@ -191,7 +211,8 @@ public function setContainer(ContainerInterface $container) /** * Sets the debugger. * - * @param \Rougin\Slytherin\Debugger\ErrorHandlerInterface $debugger + * @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $debugger + * @return self */ public function setDebugger(ErrorHandlerInterface $debugger) { diff --git a/src/Component/Collector.php b/src/Component/Collector.php index 4bc48655..92272b00 100644 --- a/src/Component/Collector.php +++ b/src/Component/Collector.php @@ -40,7 +40,7 @@ public function __construct(array $items = array()) /** * Generates a Collection instance. * - * @param + * @param \Rougin\Slytherin\Container\ContainerInterface $container * @return \Rougin\Slytherin\Component\Collection */ public function make(ContainerInterface $container) @@ -96,7 +96,7 @@ public function make(ContainerInterface $container) if ($item->getType() === 'middleware') { - /** @var \Rougin\Slytherin\Middleware\MiddlewareInterface */ + /** @var \Rougin\Slytherin\Middleware\DispatcherInterface */ $result = $item->get(); $collection->setMiddleware($result); @@ -116,7 +116,7 @@ public function make(ContainerInterface $container) /** * Collects the specified components. - * + * * @param \Rougin\Slytherin\Component\ComponentInterface[]|string[] $components * @param \Rougin\Slytherin\IoC\ContainerInterface $container * @return \Rougin\Slytherin\Component\Collection diff --git a/src/Component/ComponentInterface.php b/src/Component/ComponentInterface.php index 558c783a..2e4a3541 100644 --- a/src/Component/ComponentInterface.php +++ b/src/Component/ComponentInterface.php @@ -21,7 +21,7 @@ public function get(); /** * Returns the type of the component. - * + * * @return string */ public function getType(); From ea17d77ef4b8869071b977253cc5e0f687c8131d Mon Sep 17 00:00:00 2001 From: Rougin Gutib Date: Sat, 9 Dec 2023 17:42:37 +0800 Subject: [PATCH 46/46] Change details in ERRATUM.md, CHANGELOG.md --- ERRATUM.md | 359 +++++++++++++++++++++++++++++++++++++++++++++++++++++ UPGRADE.md | 2 +- 2 files changed, 360 insertions(+), 1 deletion(-) diff --git a/ERRATUM.md b/ERRATUM.md index 33b2eb5c..d6c09472 100644 --- a/ERRATUM.md +++ b/ERRATUM.md @@ -23,6 +23,365 @@ This version introduced a PSR-15 implementation based on `http-interop/http-midd } ``` +## 0.7.0 + +### Handling of `Components` + +One of the features in this version is the enhanced handling of `Components`. Instead of manually defining dependencies to core interfaces of Slytherin (e.g., `Routing`, `Debugger`, `Http`, `Middleware`), it is now possible to create a class around it and add the said class to a `Collector`: + +``` php +// src/Packages/ContainerPackage.php + +namespace Rougin\Nostalgia\Packages; + +use Rougin\Slytherin\Component\AbstractComponent; +use Rougin\Slytherin\IoC\Auryn; +use Rougin\Slytherin\Template\RendererInterface; +use Rougin\Slytherin\Template\Twig; +use Twig_Environment; +use Twig_Loader_Filesystem; + +class ContainerPackage extends AbstractComponent +{ + /** + * Type of the component: + * container, dispatcher, debugger, http, middleware, template + * + * @var string + */ + protected $type = 'container'; + + /** + * Returns an instance from the named class. + * + * @return mixed + */ + public function get() + { + $root = realpath(dirname(dirname(__DIR__))); + + // Initialize the RendererInterface ------------- + $views = (string) realpath($root . '/app/views'); + $loader = new Twig_Loader_Filesystem($views); + $twig = new Twig(new Twig_Environment($loader)); + $renderer = RendererInterface::class; + // ---------------------------------------------- + + // Initialize the DependencyInjectorInterface --- + $auryn = new Auryn(new \Auryn\Injector); + // Create an alias for the RendererInterface --- + $auryn->share($twig); + $auryn->alias($renderer, get_class($twig)); + // --------------------------------------------- + + return $auryn; + } +} +``` + +``` php +// src/Packages/DebuggerPackage.php + +namespace Rougin\Nostalgia\Packages; + +use Rougin\Slytherin\Component\AbstractComponent; +use Rougin\Slytherin\ErrorHandler\Whoops; + +class DebuggerPackage extends AbstractComponent +{ + /** + * Type of the component: + * container, dispatcher, debugger, http, middleware, template + * + * @var string + */ + protected $type = 'debugger'; + + /** + * Returns an instance from the named class. + * + * @return mixed + */ + public function get() + { + return new Whoops(new \Whoops\Run); + } +} +``` + +``` php +// src/Packages/HttpPackage.php + +namespace Rougin\Nostalgia\Packages; + +use Rougin\Slytherin\Component\AbstractComponent; +use Zend\Diactoros\Response; +use Zend\Diactoros\ServerRequestFactory; + +class HttpPackage extends AbstractComponent +{ + /** + * Type of the component: + * container, dispatcher, debugger, http, middleware, template + * + * @var string + */ + protected $type = 'http'; + + /** + * Returns an instance from the named class. + * + * @return mixed + */ + public function get() + { + $request = ServerRequestFactory::fromGlobals(); + + return array($request, new Response); + } +} +``` + +``` php +// src/Packages/MiddlewarePackage.php + +namespace Rougin\Nostalgia\Packages; + +use Rougin\Slytherin\Component\AbstractComponent; +use Rougin\Slytherin\Middleware\StratigilityMiddleware; +use Zend\Stratigility\MiddlewarePipe; + +class MiddlewarePackage extends AbstractComponent +{ + /** + * Type of the component: + * container, dispatcher, debugger, http, middleware, template + * + * @var string + */ + protected $type = 'middleware'; + + /** + * Returns an instance from the named class. + * + * @return mixed + */ + public function get() + { + return new StratigilityMiddleware(new MiddlewarePipe); + } +} +``` + +``` php +namespace Rougin\Nostalgia\Packages; + +use Rougin\Slytherin\Component\AbstractComponent; +use Rougin\Slytherin\Template\Twig; +use Twig_Environment; +use Twig_Loader_Filesystem; + +class TemplatePackage extends AbstractComponent +{ + /** + * Type of the component: + * container, dispatcher, debugger, http, middleware, template + * + * @var string + */ + protected $type = 'template'; + + /** + * Returns an instance from the named class. + * + * @return mixed + */ + public function get() + { + $root = realpath(dirname(dirname(__DIR__))); + + $views = (string) realpath($root . '/app/views'); + + $loader = new Twig_Loader_Filesystem($views); + + return new Twig(new Twig_Environment($loader)); + } +} +``` + +After creating the classes that extends on `AbstractComponent`, update the following code below to `app/web/index.php`: + +``` diff +// app/web/index.php + ++use Rougin\Nostalgia\Packages\ContainerPackage; ++use Rougin\Nostalgia\Packages\DebuggerPackage; ++use Rougin\Nostalgia\Packages\HttpPackage; ++use Rougin\Nostalgia\Packages\MiddlewarePackage; ++use Rougin\Nostalgia\Packages\RoutingPackage; ++use Rougin\Nostalgia\Packages\TemplatePackage; + use Rougin\Slytherin\Application; +-use Rougin\Slytherin\ComponentCollection; +-use Rougin\Slytherin\Dispatching\Dispatcher; +-use Rougin\Slytherin\ErrorHandler\Whoops; +-use Rougin\Slytherin\IoC\Auryn; +-use Rougin\Slytherin\Middleware\StratigilityMiddleware; +-use Rougin\Slytherin\Template\RendererInterface; +-use Rougin\Slytherin\Template\Twig; +-use Zend\Diactoros\Response; +-use Zend\Diactoros\ServerRequestFactory; +-use Zend\Stratigility\MiddlewarePipe; ++use Rougin\Slytherin\Component\Collector; + + $root = dirname(dirname(__DIR__)); + + require $root . '/vendor/autoload.php'; + +-$component = new ComponentCollection; +- +-// Initialize the RendererInterface ------------- +-$views = (string) realpath($root . '/app/views'); +-$loader = new Twig_Loader_Filesystem($views); +-$twig = new Twig(new Twig_Environment($loader)); +-$renderer = RendererInterface::class; +-// ---------------------------------------------- ++$components = array(); + + // Initialize the DependencyInjectorInterface --- +-$auryn = new Auryn(new \Auryn\Injector); +-// Create an alias for the RendererInterface --- +-$auryn->share($twig); +-$auryn->alias($renderer, get_class($twig)); +-// --------------------------------------------- +-$component->setDependencyInjector($auryn); ++$components[] = new ContainerPackage; + // ---------------------------------------------- + + // Initialize the ErrorHandlerInterface --- +-$whoops = new Whoops(new \Whoops\Run); +-$component->setErrorHandler($whoops); ++$components[] = new DebuggerPackage; + // ---------------------------------------- + + // Initialize the ServerRequestInterface and ResponseInterface --- +-$request = ServerRequestFactory::fromGlobals(); +-$response = new Response; +-$component->setHttp($request, $response); ++$components[] = new HttpPackage; + // --------------------------------------------------------------- + + // Initialize the routing dispatcher interface --- +-$router = require "$root/app/config/routes.php"; +-$dispatcher = new Dispatcher($router, $response); +-$component->setDispatcher($dispatcher); ++$components[] = new RoutingPackage; + // ----------------------------------------------- + + // Initialize the middleware ------------------- +-$pipe = new MiddlewarePipe; +-$middleware = new StratigilityMiddleware($pipe); +-$component->setMiddleware($middleware); ++$components[] = new MiddlewarePackage; + // --------------------------------------------- + ++// Initialize the template renderer ------------ ++$components[] = new TemplatePackage; ++// --------------------------------------------- + + // Initialize then run the Application --- +-$app = new Application($component); ++$collection = Collector::get($components); + +-$app->run(); ++(new Application($collection))->run(); + // --------------------------------------- +``` + +## 0.5.0 + +### Middlewares + +Middlewares in concept are a layer of actions or callables that are wrapped around a piece of core logic in an application. To add middlewares in Slytherin, kindly install `Stratigility` first for handling the middlewares: + +``` diff + { + "require": + { + "rougin/slytherin": "~0.5.0", + "filp/whoops": "~1.0", + "nikic/fast-route": "~1.0", + "rdlowrey/auryn": "~1.0", + "twig/twig": "~1.0", +- "zendframework/zend-diactoros": "~1.0" ++ "zendframework/zend-diactoros": "~1.0", ++ "zendframework/zend-stratigility": "~1.0" + } +``` + +``` bash +$ composer update +``` + +After installing the said package, update the code below to support the handling of middlewares: + +``` php +// app/web/index.php + +// ... + +// Initialize the middleware ------------------- +$pipe = new MiddlewarePipe; +$middleware = new StratigilityMiddleware($pipe); +$component->setMiddleware($middleware); +// --------------------------------------------- + +$app = new Application($component); + +// ... +``` + +``` php +// app/config/routes.php + +$router = new Rougin\Slytherin\Dispatching\Router; + +// ... + +// Add the middlewares to a specified route --------------- +$items = array('Rougin\Nostalgia\Handlers\Hello'); + +$router->addRoute('GET', '/hello', function () {}, $items); +// -------------------------------------------------------- + +// ... +``` + +``` php +// src/Handlers/Hello.php + +namespace Rougin\Nostalgia\Handlers; + +/** + * This is a sample middleware + */ +class Hello +{ + /** + * Creating middlewares should follow this __invoke method. + */ + public function __invoke($request, $response, $next = null) + { + $response = $next($request, $response); + + $response->getBody()->write('Hello from middleware'); + + return $response; + } +} +``` + +**NOTE**: Due to the nature of middleware and as a new concept, integrating middlewares to existing routes is not yet supported. + ## 0.4.0 In this version, the `patricklouys/http` has been removed in favor for PSR-07 (`psr/http-message`). With this, kindly add a package that is compliant to PSR-07 (e.g., `zendframework/zend-diactoros`) in the `composer.json`: diff --git a/UPGRADE.md b/UPGRADE.md index 2eac32a1..c5fd9ff8 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,4 +1,4 @@ -# Slytherin Upgrade Guide +# Upgrade Guide Below are the guides when upgrading from specified versions due to backward compatibility breaks: