diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae1abaed..0b54345a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,25 +41,29 @@ 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 7.1 if: ${{ matrix.php-versions == '7.1' }} - run: composer require zendframework/zend-stratigility:~2.0 --dev + 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:~2.0 --dev + 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:~2.0 --dev + 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:~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 zendframework/zend-stratigility --dev - name: Run test suite run: vendor/bin/phpunit --coverage-clover=coverage.clover diff --git a/CHANGELOG.md b/CHANGELOG.md index 549854a5..86a08eef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,16 @@ 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`) +- `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` - Conformed all application logic to `RouteInterface` - `UnexpectedValueException` to `BadMethodCallException` in `DispatcherInterface` +- Conformed `Middleware` to the official `PSR-15` package (`psr/http-server-middleware`) +- `Application` class to `System` ### Fixed - Type hinting of all classes using `PHPStan` (up to `level 9`) @@ -21,10 +26,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 - -### Removed -- `__call` methods in `Router`, use the defined methods instead (e.g., `get()`, `post()`, etc.) +- 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 @@ -92,7 +95,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.2...v0.9.0) - 2017-07-08 **NOTE**: This release may break your application if upgrading from `v0.8.0` release. @@ -145,7 +148,25 @@ 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.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. + +### 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.2...v0.8.0) - 2016-09-08 ### Added - Implementation for [Phroute](https://github.com/mrjgreen/phroute) package @@ -159,7 +180,26 @@ 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.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. + +### 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 - HTTP method spoofing @@ -174,7 +214,14 @@ 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.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 - Parameter for adding default data and file extension in `Template\TwigRenderer` @@ -183,7 +230,18 @@ 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.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 - `Middleware` component @@ -195,6 +253,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 @@ -239,7 +310,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. @@ -253,6 +324,17 @@ 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 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 generating `index.php` from `Installer` + ## [0.2.1](https://github.com/rougin/slytherin/compare/v0.2.0...v0.2.1) - 2015-09-30 ### Added diff --git a/ERRATUM.md b/ERRATUM.md new file mode 100644 index 00000000..d6c09472 --- /dev/null +++ b/ERRATUM.md @@ -0,0 +1,427 @@ +# Erratum + +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.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 + { + "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 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 + { + "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..c5fd9ff8 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,301 @@ +# Upgrade Guide + +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: + +``` 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`. + +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 an 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 Rougin\Nostalgia\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 diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..bc1bfd76 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,8 @@ +parameters: + level: 9 + paths: + - src + excludePaths: + analyse: + - src/Middleware/Handlers + - src/Middleware/Interop.php \ No newline at end of file diff --git a/src/Application.php b/src/Application.php index 212b1165..e85d2b47 100644 --- a/src/Application.php +++ b/src/Application.php @@ -2,170 +2,14 @@ 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/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/Component/AbstractComponent.php b/src/Component/AbstractComponent.php index 62b5659e..36b67ae9 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,8 +13,8 @@ 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 */ @@ -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 a2d9360b..1ba81301 100644 --- a/src/Component/Collection.php +++ b/src/Component/Collection.php @@ -2,192 +2,281 @@ 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 MiddlewareDispatcher; -use Rougin\Slytherin\Routing\DispatcherInterface as RouteDispatcher; +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 * - * Contains all the required components for Slytherin. * NOTE: To be removed in v1.0.0. Use "Integration" instead. * * @package Slytherin * @author Rougin Gutib */ -class Collection extends VanillaContainer +class Collection implements ContainerInterface { /** - * @var \Psr\Container\ContainerInterface + * @var \Rougin\Slytherin\Container\ContainerInterface */ protected $container; + /** + * @var mixed[] + */ + protected $items = array(); + + public function __construct() + { + $this->container = new Container; + } + + /** + * Adds a new instance to the container. + * NOTE: To be removed in v1.0.0. Use "set" instead. + * + * @param string $id + * @param object $concrete + * @return self + */ + public function add($id, $concrete) + { + return $this->set($id, $concrete); + } + + /** + * 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); + } + /** * Gets an instance of the container. * - * @return \Psr\Container\ContainerInterface + * @return \Rougin\Slytherin\Container\ContainerInterface */ public function getContainer() { - $interface = 'Psr\Container\ContainerInterface'; - - return (is_a($this->container, $interface)) ? $this->container : $this; + return $this->container; } /** - * Sets the container. + * Gets the debugger. * - * @param \Psr\Container\ContainerInterface $container - * @return self + * @return \Rougin\Slytherin\Debug\ErrorHandlerInterface */ - public function setContainer(ContainerInterface $container) + public function getDebugger() { - $this->container = $container; + /** @var \Rougin\Slytherin\Debug\ErrorHandlerInterface */ + return $this->get(System::DEBUGGER); + } - return $this; + /** + * 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('Rougin\Slytherin\Routing\DispatcherInterface'); + /** @var \Rougin\Slytherin\Dispatching\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(RouteDispatcher $dispatcher) + public function getErrorHandler() { - $this->set('Rougin\Slytherin\Routing\DispatcherInterface', $dispatcher); - - return $this; + return $this->getDebugger(); } /** - * Gets the debugger. + * Gets the HTTP components. * - * @return \Rougin\Slytherin\Debug\ErrorHandlerInterface|null + * @return mixed */ - public function getDebugger() + public function getHttp() { - return $this->getErrorHandler(); + return array($this->getHttpRequest(), $this->getHttpResponse()); } /** - * Sets the debugger. + * Gets the request. * - * @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $debugger - * @return self + * @return \Psr\Http\Message\ServerRequestInterface */ - public function setDebugger(ErrorHandlerInterface $debugger) + public function getHttpRequest() { - $this->setErrorHandler($debugger); + /** @var \Psr\Http\Message\ServerRequestInterface */ + return $this->get(System::REQUEST); + } - return $this; + /** + * Gets the response. + * + * @return \Psr\Http\Message\ResponseInterface + */ + public function getHttpResponse() + { + /** @var \Psr\Http\Message\ResponseInterface */ + return $this->get(System::RESPONSE); } /** - * Gets the error handler. + * Gets the middleware. * - * @return \Rougin\Slytherin\Debug\ErrorHandlerInterface|null + * @return \Rougin\Slytherin\Middleware\DispatcherInterface */ - public function getErrorHandler() + public function getMiddleware() { - $interface = 'Rougin\Slytherin\Debug\ErrorHandlerInterface'; + /** @var \Rougin\Slytherin\Middleware\DispatcherInterface */ + return $this->get(System::MIDDLEWARE); + } - if (! $this->getContainer()->has($interface)) return null; + /** + * Gets the template. + * + * @return \Rougin\Slytherin\Template\RendererInterface + */ + public function getTemplate() + { + /** @var \Rougin\Slytherin\Template\RendererInterface */ + return $this->get(System::TEMPLATE); + } - /** @var \Rougin\Slytherin\Debug\ErrorHandlerInterface */ - return $this->getContainer()->get((string) $interface); + /** + * Returns true if the container can return an entry for the given identifier. + * + * @param string $id + * @return boolean + */ + public function has($id) + { + return $this->container->has($id); } /** - * Sets the error handler. + * Sets a new instance to the container. * - * @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $errorHandler + * @param string $id + * @param mixed $concrete * @return self */ - public function setErrorHandler(\Rougin\Slytherin\Debug\ErrorHandlerInterface $errorHandler) + public function set($id, $concrete = null) { - $this->set('Rougin\Slytherin\Debug\ErrorHandlerInterface', $errorHandler); + $this->container->set($id, $concrete); return $this; } /** - * Gets the HTTP components. + * Sets the container. * - * @return mixed + * @param \Rougin\Slytherin\Container\ContainerInterface $container + * @return self */ - public function getHttp() + public function setContainer(ContainerInterface $container) { - $request = $this->get('Psr\Http\Message\ServerRequestInterface'); - $response = $this->get('Psr\Http\Message\ResponseInterface'); + $this->container = $container; - return array($request, $response); + return $this; } /** - * Sets the HTTP components. + * Sets the debugger. * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Psr\Http\Message\ResponseInterface $response + * @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $debugger * @return self */ - public function setHttp(ServerRequestInterface $request, ResponseInterface $response) + public function setDebugger(ErrorHandlerInterface $debugger) { - $this->set('Psr\Http\Message\ServerRequestInterface', $request); + return $this->add(System::DEBUGGER, $debugger); + } - $this->set('Psr\Http\Message\ResponseInterface', $response); + /** + * NOTE: To be removed in v1.0.0. Use "setContainer" instead. + * + * @param \Rougin\Slytherin\Container\ContainerInterface $injector + * @return self + */ + public function setDependencyInjector(ContainerInterface $injector) + { + 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('Psr\Http\Message\ServerRequestInterface'); + 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('Psr\Http\Message\ServerRequestInterface', $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('Psr\Http\Message\ResponseInterface'); + $this->set(System::REQUEST, $request); + + return $this; } /** @@ -198,36 +287,30 @@ public function getHttpResponse() */ public function setHttpResponse(ResponseInterface $response) { - $this->set('Psr\Http\Message\ResponseInterface', $response); + $this->set(System::RESPONSE, $response); return $this; } /** - * 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) { - $interface = 'Rougin\Slytherin\Middleware\DispatcherInterface'; - - if (! $this->getContainer()->has($interface)) return null; - - /** @var \Rougin\Slytherin\Middleware\DispatcherInterface */ - return $this->getContainer()->get((string) $interface); + return $this->add(System::MIDDLEWARE, $middleware); } /** - * Sets the middleware. + * Sets the template. * - * @param \Rougin\Slytherin\Middleware\DispatcherInterface $middleware + * @param \Rougin\Slytherin\Template\RendererInterface $template * @return self */ - public function setMiddleware(MiddlewareDispatcher $middleware) + public function setTemplate(RendererInterface $template) { - $this->set('Rougin\Slytherin\Middleware\DispatcherInterface', $middleware); - - return $this; + return $this->add(System::TEMPLATE, $template); } } diff --git a/src/Component/Collector.php b/src/Component/Collector.php index 3ba17c9b..92272b00 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 * @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\DispatcherInterface */ + $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. + * Collects the specified components. * - * @param \Rougin\Slytherin\Component\Collection &$collection - * @param string $component - * @return \Rougin\Slytherin\Integration\IntegrationInterface + * @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..2e4a3541 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. + * + * @return mixed + */ + public function get(); + + /** + * Returns the type of the component. * - * @param \Psr\Container\ContainerInterface &$container - * @return void + * @return string */ - public function set(ContainerInterface &$container); + public function getType(); } 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/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 b93e80c3..f1c9fd49 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 @@ -48,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 @@ -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/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/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/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/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..678855be --- /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..1dd4c6d8 --- /dev/null +++ b/src/IoC/ContainerInterface.php @@ -0,0 +1,17 @@ + + */ +interface ContainerInterface extends Slytherin +{ +} 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/Callback.php b/src/Middleware/Callback.php new file mode 100644 index 00000000..d0e3587b --- /dev/null +++ b/src/Middleware/Callback.php @@ -0,0 +1,89 @@ + + */ +class Callback 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 \Rougin\Slytherin\Middleware\HandlerInterface $handler + * @return \Psr\Http\Message\ResponseInterface + */ + public function process(ServerRequestInterface $request, HandlerInterface $handler) + { + $middleware = $this->middleware; + + if (is_string($middleware)) + { + /** @var callable */ + $middleware = new $middleware; + } + + if ($this->isSinglePass($middleware)) + { + return $middleware($request, $handler); + } + + $response = $this->response; + + $fn = function ($request) use ($handler) + { + return $handler->handle($request); + }; + + return $middleware($request, $response, $fn); + } + + /** + * @param mixed $item + * @return boolean + */ + protected function isSinglePass($item) + { + if ($item instanceof \Closure) + { + $object = new \ReflectionFunction($item); + } + else + { + /** @var object|string $item */ + $object = new \ReflectionMethod($item, '__invoke'); + } + + return count($object->getParameters()) === 2; + } +} diff --git a/src/Middleware/Delegate.php b/src/Middleware/Delegate.php index 17321474..53092c8d 100644 --- a/src/Middleware/Delegate.php +++ b/src/Middleware/Delegate.php @@ -2,64 +2,13 @@ namespace Rougin\Slytherin\Middleware; -use Psr\Http\Message\ServerRequestInterface; -use Rougin\Slytherin\Http\Response; - /** - * 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 105c25a6..6e333a1f 100644 --- a/src/Middleware/Dispatcher.php +++ b/src/Middleware/Dispatcher.php @@ -2,257 +2,145 @@ 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; /** - * Dispatcher - * - * A simple implementation of a middleware dispatcher. - * * @package Slytherin * @author Rougin Gutib - * @author Rasmus Schultz */ class Dispatcher implements DispatcherInterface { - const SINGLE_PASS = false; - - const DOUBLE_PASS = true; - - /** - * @var \Psr\Http\Message\ResponseInterface - */ - protected $response; - /** - * @var array + * @var \Rougin\Slytherin\Middleware\MiddlewareInterface[] */ protected $stack = array(); /** - * Initializes the dispatcher instance. - * - * @param array $stack - * @param \Psr\Http\Message\ResponseInterface|null $response + * @var \Psr\Http\Message\ResponseInterface|null */ - public function __construct(array $stack = array(), ResponseInterface $response = null) - { - $this->response = $response ?: new Response; - - $this->stack = $stack; - } + protected $response = null; /** - * 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 + * @param mixed[] $stack */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $stack = array()) + public function __construct($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)); + if ($stack) $this->setStack($stack); } /** - * Returns the listing of middlewares included. - * NOTE: To be removed in v1.0.0. Use $this->stack() instead. - * - * @return array + * @return \Rougin\Slytherin\Middleware\MiddlewareInterface[] */ public function getStack() { - return $this->stack(); + return $this->stack; } /** - * 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\Middleware\HandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { - $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; + $stack = (array) $this->getStack(); - /** @var \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface $middleware */ - $this->stack[$index] = $this->transform($middleware); - } - - $resolved = $this->resolve(0); - - array_pop($this->stack); - - $this->stack = $original; + $handler = new Handler($stack, $handler); - return $resolved->process($request); + return $handler->handle($request); } /** - * Adds a new middleware or a list of middlewares in the stack. - * - * @param array|\Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface|string $middleware + * @param mixed $middleware * @return self */ public function push($middleware) { - if (is_array($middleware)) + if (! is_array($middleware)) { - $this->stack = array_merge($this->stack, $middleware); + $item = $this->transform($middleware); + + array_push($this->stack, $item); return $this; } - array_push($this->stack, $middleware); + foreach ($middleware as $item) $this->push($item); return $this; } /** - * Returns the listing of middlewares included. - * - * @return array + * @param mixed[] $stack + * @return self */ - public function stack() + public function setStack($stack) { - return $this->stack; - } + $result = array(); - /** - * 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')) + foreach ($stack as $item) { - $object = new \ReflectionFunction($middleware); - - return count($object->getParameters()) === 2; + $result[] = $this->transform($item); } - $class = (string) get_class($middleware); - - $object = new \ReflectionMethod($class, '__invoke'); + $this->stack = $result; - return count($object->getParameters()) === 2; + return $this; } /** - * Returns the middleware as a single pass callable. + * NOTE: To be removed in v1.0.0. Use "getStack" instead. * - * @param \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface|string $middleware - * @param \Psr\Http\Message\ResponseInterface $response - * @return callable + * @return \Rougin\Slytherin\Middleware\MiddlewareInterface[] */ - protected function callback($middleware, ResponseInterface $response) + public function stack() { - 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; + return $this->getStack(); } /** - * Resolves the whole stack through its index. - * - * @param integer $index - * @return \Interop\Http\ServerMiddleware\DelegateInterface + * @param mixed $middleware + * @return \Rougin\Slytherin\Middleware\MiddlewareInterface */ - protected function resolve($index) + protected function transform($middleware) { - $stack = $this->stack; - - if (! isset($this->stack[$index])) + if ($middleware instanceof MiddlewareInterface) { - return new Delegate(null); + return $middleware; } - /** @var \Interop\Http\ServerMiddleware\MiddlewareInterface */ - $item = $stack[(integer) $index]; - - $next = $this->resolve($index + 1); + // Set empty response for double pass middlewares --- + if (! $this->response) + { + $this->response = new Response; + } + // -------------------------------------------------- - $fn = function ($request) use ($item, $next) + if ($this->isCallable($middleware)) { - return $item->process($request, $next); - }; + /** @var callable $middleware */ + return new Callback($middleware, $this->response); + } - return new Delegate($fn); + return new Wrapper($middleware); } /** - * Transforms the specified middleware into a PSR-15 middleware. - * - * @param \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface $middleware - * @param boolean $wrappable - * @return \Interop\Http\ServerMiddleware\MiddlewareInterface + * @param mixed $item + * @return boolean */ - protected function transform($middleware, $wrappable = true) + protected function isCallable($item) { - $response = null; - - if (is_a($middleware, Application::MIDDLEWARE)) return $middleware; - - $approach = $this->approach($middleware); + /** @var object|string $item */ + $method = method_exists($item, '__invoke'); - $singlePass = $approach === self::SINGLE_PASS; + $callable = is_callable($item); - if ($singlePass) $response = $this->response; + $object = is_object($item); - $wrapper = new CallableMiddlewareWrapper($middleware, $response); + $closure = (! $object) || $item instanceof \Closure; - /** @var \Interop\Http\ServerMiddleware\MiddlewareInterface */ - return $wrappable ? $wrapper : $middleware; + return ($method || $callable) && $closure; } } diff --git a/src/Middleware/DispatcherInterface.php b/src/Middleware/DispatcherInterface.php index bb3fcc58..5f46435a 100644 --- a/src/Middleware/DispatcherInterface.php +++ b/src/Middleware/DispatcherInterface.php @@ -2,9 +2,6 @@ namespace Rougin\Slytherin\Middleware; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; - /** * Dispatcher Interface * @@ -16,28 +13,19 @@ interface DispatcherInterface extends MiddlewareInterface { /** - * 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 + * @return \Rougin\Slytherin\Middleware\MiddlewareInterface[] */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $stack = array()); + public function getStack(); /** - * Adds a new middleware or a list of middlewares in the stack. - * - * @param array|\Interop\Http\ServerMiddleware\MiddlewareInterface|callable|string $middleware + * @param mixed $middleware * @return self */ public function push($middleware); /** - * Returns the listing of middlewares included. - * - * @return array + * @param mixed[] $stack + * @return self */ - public function stack(); + public function setStack($stack); } diff --git a/src/Middleware/Doublepass.php b/src/Middleware/Doublepass.php new file mode 100644 index 00000000..3da8d6b3 --- /dev/null +++ b/src/Middleware/Doublepass.php @@ -0,0 +1,53 @@ + + * @codeCoverageIgnore + */ +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/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/Handler.php b/src/Middleware/Handler.php new file mode 100644 index 00000000..22878d80 --- /dev/null +++ b/src/Middleware/Handler.php @@ -0,0 +1,78 @@ + + * @codeCoverageIgnore + */ +class Handler implements HandlerInterface +{ + /** + * @var \Rougin\Slytherin\Middleware\HandlerInterface + */ + protected $default; + + /** + * @var integer + */ + protected $index = 0; + + /** + * @var \Rougin\Slytherin\Middleware\MiddlewareInterface[] + */ + protected $stack; + + /** + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface[] $stack + * @param \Rougin\Slytherin\Middleware\HandlerInterface $default + */ + public function __construct(array $stack, HandlerInterface $default) + { + $this->default = $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 + */ + public function handle(ServerRequestInterface $request) + { + if (! isset($this->stack[$this->index])) + { + return $this->default->handle($request); + } + + $item = $this->stack[(int) $this->index]; + + $next = $this->next(); + + return $item->process($request, $next); + } + + /** + * @return \Rougin\Slytherin\Middleware\HandlerInterface + */ + protected function next() + { + $next = clone $this; + + $next->index++; + + return $next; + } +} diff --git a/src/Middleware/HandlerInterface.php b/src/Middleware/HandlerInterface.php index 1ca45bc8..2db08a75 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 Psr\Http\Message\ServerRequestInterface; /** * Handler Interface @@ -12,6 +12,11 @@ * @package Slytherin * @author Rougin Gutib */ -interface HandlerInterface extends DelegateInterface +interface HandlerInterface { + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request); } diff --git a/src/Middleware/Handlers/Handler030.php b/src/Middleware/Handlers/Handler030.php new file mode 100644 index 00000000..693dc8ad --- /dev/null +++ b/src/Middleware/Handlers/Handler030.php @@ -0,0 +1,47 @@ + + * @codeCoverageIgnore + */ +class Handler030 implements DelegateInterface +{ + /** + * @var \Rougin\Slytherin\Middleware\HandlerInterface + */ + protected $handler; + + /** + * @param \Rougin\Slytherin\Middleware\HandlerInterface $handler + */ + 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 + */ + public function process(RequestInterface $request) + { + return $this->handler->handle($request); + } +} diff --git a/src/Middleware/Handlers/Handler041.php b/src/Middleware/Handlers/Handler041.php new file mode 100644 index 00000000..7874237c --- /dev/null +++ b/src/Middleware/Handlers/Handler041.php @@ -0,0 +1,46 @@ + + * @codeCoverageIgnore + */ +class Handler041 implements DelegateInterface +{ + /** + * @var \Rougin\Slytherin\Middleware\HandlerInterface + */ + protected $handler; + + /** + * @param \Rougin\Slytherin\Middleware\HandlerInterface $handler + */ + 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 + */ + public function process(ServerRequestInterface $request) + { + return $this->handler->handle($request); + } +} diff --git a/src/Middleware/Handlers/Handler050.php b/src/Middleware/Handlers/Handler050.php new file mode 100644 index 00000000..5f333c20 --- /dev/null +++ b/src/Middleware/Handlers/Handler050.php @@ -0,0 +1,46 @@ + + * @codeCoverageIgnore + */ +class Handler050 implements RequestHandlerInterface +{ + /** + * @var \Rougin\Slytherin\Middleware\HandlerInterface + */ + protected $handler; + + /** + * @param \Rougin\Slytherin\Middleware\HandlerInterface $handler + */ + 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 + */ + public function handle(ServerRequestInterface $request) + { + return $this->handler->handle($request); + } +} diff --git a/src/Middleware/Handlers/Handler100.php b/src/Middleware/Handlers/Handler100.php new file mode 100644 index 00000000..bff87f02 --- /dev/null +++ b/src/Middleware/Handlers/Handler100.php @@ -0,0 +1,47 @@ + + * @codeCoverageIgnore + */ +class Handler100 implements RequestHandlerInterface +{ + /** + * @var \Rougin\Slytherin\Middleware\HandlerInterface + */ + protected $handler; + + /** + * @param \Rougin\Slytherin\Middleware\HandlerInterface $handler + */ + 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 + */ + public function handle(ServerRequestInterface $request): ResponseInterface + { + return $this->handler->handle($request); + } +} diff --git a/src/Middleware/Interop.php b/src/Middleware/Interop.php new file mode 100644 index 00000000..ebf4190b --- /dev/null +++ b/src/Middleware/Interop.php @@ -0,0 +1,140 @@ + + * @codeCoverageIgnore + */ +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 + */ + 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) + { + $version = Version::get(); + + $method = 'handle'; + + if ($version === '0.3.0' || $version === '0.4.1') + { + $method = 'process'; + } + + $class = array($this->handler, $method); + + return call_user_func($class, $request); + } + + /** + * @return boolean + */ + public static function exists() + { + return interface_exists(self::VERSION_0_3_0) + || interface_exists(self::VERSION_0_4_1) + || interface_exists(self::VERSION_0_5_0); + } + + /** + * @param mixed $handler + * @param string|null $version + * @return mixed + */ + public static function getHandler($handler, $version = null) + { + switch ($version) + { + case '0.3.0': + return new Handler030($handler); + + case '0.4.1': + return new Handler041($handler); + + case '0.5.0': + return new Handler050($handler); + + case '1.0.0': + return new Handler100($handler); + } + + if (self::hasVersion($handler, self::VERSION_0_3_0)) + { + return new Handler030($handler); + } + + if (self::hasVersion($handler, self::VERSION_0_4_1)) + { + return new Handler041($handler); + } + + if (self::hasVersion($handler, self::VERSION_0_5_0)) + { + return new Handler050($handler); + } + + if (self::hasVersion($handler, self::VERSION_1_0_0)) + { + return new Handler100($handler); + } + + return $handler; + } + + /** + * @param mixed $handler + * @param string $version + * @return boolean + */ + public static function hasVersion($handler, $class) + { + return interface_exists($class) || is_a($handler, $class); + } + + /** + * @return boolean + */ + public static function psrExists() + { + return interface_exists(self::VERSION_1_0_0); + } +} diff --git a/src/Middleware/MiddlewareIntegration.php b/src/Middleware/MiddlewareIntegration.php index 3721ffda..07e1f24b 100644 --- a/src/Middleware/MiddlewareIntegration.php +++ b/src/Middleware/MiddlewareIntegration.php @@ -2,11 +2,10 @@ 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 Rougin\Slytherin\System; use Zend\Stratigility\MiddlewarePipe; /** @@ -33,38 +32,14 @@ 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); + $dispatch = new Dispatcher($stack); $empty = $this->preferred === null; @@ -76,9 +51,9 @@ protected function dispatcher(ResponseInterface $response, $stack) { $pipe = new MiddlewarePipe; - $dispatcher = new StratigilityDispatcher($pipe, $stack, $response); + $dispatch = new StratigilityDispatcher($pipe); } - return $dispatcher; + return $container->set($middleware, $dispatch); } } 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 8a2e0022..ca640550 100644 --- a/src/Middleware/StratigilityDispatcher.php +++ b/src/Middleware/StratigilityDispatcher.php @@ -2,10 +2,13 @@ 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\Middleware\Doublepass; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\Interop; +use Rougin\Slytherin\Middleware\MiddlewareInterface; use Zend\Stratigility\MiddlewarePipe; /** @@ -22,53 +25,133 @@ 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; + public function __construct(MiddlewarePipe $pipe, $stack = array()) + { + parent::__construct($stack); + + $this->zend = $pipe; + } /** - * @var array + * @return boolean */ - protected $stack = array(); + public function hasFactory() + { + return class_exists('Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory'); + } /** - * Initializes the dispatcher instance. - * - * @param \Zend\Stratigility\MiddlewarePipe $pipeline - * @param array $stack - * @param \Psr\Http\Message\ResponseInterface|null $response + * @return boolean */ - public function __construct(MiddlewarePipe $pipeline, array $stack = array(), ResponseInterface $response = null) + public function hasPsr() { - $this->pipeline = $pipeline; - - $this->response = $response ?: new Response; - - $this->stack = $stack; + return class_exists('Zend\Stratigility\Middleware\CallableMiddlewareDecorator'); } /** - * 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\Middleware\HandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) + public function process(ServerRequestInterface $request, HandlerInterface $handler) { - $wrap = class_exists('Zend\Stratigility\Middleware\ErrorHandler'); + $response = new Response; + + $this->setFactory($response); - foreach ($this->stack as $middleware) + foreach ($this->getStack() as $item) { - if (is_string($middleware)) $middleware = new $middleware; + $item = $this->setMiddleware($item); + + if (! $this->hasFactory() && $this->hasPsr()) + { + $item = $this->setPsrMiddleware($item); + } + + $this->zend->pipe($item); + } + + // Force version check to 1.0.0 if using v3.0 --- + $version = $this->hasPsr() ? '1.0.0' : null; + // ---------------------------------------------- + + $next = Interop::getHandler($handler, $version); + + $zend = $this->zend; + + if ($this->hasPsr()) + { + /** @phpstan-ignore-next-line */ + return $zend->process($request, $next); + } + + /** @phpstan-ignore-next-line */ + return $zend($request, $response, $next); + } + + /** + * @param \Psr\Http\Message\ResponseInterface $response + * @return void + * @codeCoverageIgnore + */ + 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'); - /** @var \Closure|\Interop\Http\ServerMiddleware\MiddlewareInterface $middleware */ - $this->pipeline->pipe($this->transform($middleware, $wrap)); + call_user_func_array($class, array($factory)); + } + + /** + * @param \Rougin\Slytherin\Middleware\MiddlewareInterface $item + * @return callable + */ + protected function setMiddleware(MiddlewareInterface $item) + { + if ($this->hasPsr()) + { + return function ($request, $handler) use ($item) + { + return $item->process($request, new Interop($handler)); + }; } - return $this->pipeline->__invoke($request, $this->response, $delegate); + return function ($request, $response, $next) use ($item) + { + /** @var callable $next */ + $handle = new Doublepass($next, $response); + + return $item->process($request, $handle); + }; + } + + /** + * @param callable $item + * @return object + * @codeCoverageIgnore + */ + protected function setPsrMiddleware($item) + { + /** @var class-string */ + $psr = 'Zend\Stratigility\Middleware\CallableMiddlewareDecorator'; + + $class = new \ReflectionClass($psr); + + return $class->newInstance($item); } } 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/Middleware/Version.php b/src/Middleware/Version.php new file mode 100644 index 00000000..5775f4d8 --- /dev/null +++ b/src/Middleware/Version.php @@ -0,0 +1,46 @@ + + */ +class Version +{ + const VERSION_0_3_0 = 'Interop\Http\Middleware\ServerMiddlewareInterface'; + + const VERSION_0_4_0 = 'Interop\Http\ServerMiddleware\MiddlewareInterface'; + + const VERSION_0_5_0 = 'Interop\Http\Server\MiddlewareInterface'; + + const VERSION_1_0_0 = 'Psr\Http\Server\MiddlewareInterface'; + + /** + * Returns the current version installed. + * + * @return string|null + * @codeCoverageIgnore + */ + public static function get() + { + $hasPsr = interface_exists(self::VERSION_1_0_0); + + if (! $hasPsr && interface_exists(self::VERSION_0_3_0)) + { + return '0.3.0'; + } + + if (! $hasPsr && interface_exists(self::VERSION_0_4_0)) + { + return '0.4.1'; + } + + if (! $hasPsr && interface_exists(self::VERSION_0_5_0)) + { + return '0.5.0'; + } + + return $hasPsr ? '1.0.0' : null; + } +} diff --git a/src/Middleware/Wrapper.php b/src/Middleware/Wrapper.php new file mode 100644 index 00000000..66f4afcb --- /dev/null +++ b/src/Middleware/Wrapper.php @@ -0,0 +1,48 @@ + + */ +class Wrapper implements MiddlewareInterface +{ + /** + * @var mixed + */ + protected $middleware; + + /** + * Initializes the middleware instance. + * + * @param mixed $middleware + */ + public function __construct($middleware) + { + $this->middleware = $middleware; + } + + /** + * Processes an incoming server request and return a response. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Rougin\Slytherin\Middleware\HandlerInterface $handler + * @return \Psr\Http\Message\ResponseInterface + */ + public function process(ServerRequestInterface $request, HandlerInterface $handler) + { + $middleware = $this->middleware; + + if (is_string($middleware)) $middleware = new $middleware; + + $next = Interop::getHandler($handler); + + /** @phpstan-ignore-next-line */ + return $middleware->process($request, $next); + } +} diff --git a/src/Routing/FastRouteRouter.php b/src/Routing/FastRouteRouter.php index fb73f8a8..9119010a 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 * @@ -27,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 499c8130..f8a7aa58 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 * @@ -25,7 +24,7 @@ class PhrouteRouter extends Router /** * Initializes the router instance. * - * @param array> $routes + * @param array> $routes */ public function __construct(array $routes = array()) { @@ -37,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/Route.php b/src/Routing/Route.php index c7902e30..c3fe37b3 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[]|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/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/Routing/Router.php b/src/Routing/Router.php index 478ca833..fb9dbfa9 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()) @@ -98,12 +98,12 @@ 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 * @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()) @@ -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 @@ -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()) @@ -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[] */ @@ -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()) @@ -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/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/src/System.php b/src/System.php new file mode 100644 index 00000000..19df7ae4 --- /dev/null +++ b/src/System.php @@ -0,0 +1,193 @@ + + */ +class System +{ + const CONTAINER = 'Psr\Container\ContainerInterface'; + + const DISPATCHER = 'Rougin\Slytherin\Routing\DispatcherInterface'; + + // TODO: Implement Error Handler ------------------------------- + const DEBUGGER = 'Rougin\Slytherin\Debug\ErrorHandlerInterface'; + // ------------------------------------------------------------- + + const MIDDLEWARE = 'Rougin\Slytherin\Middleware\DispatcherInterface'; + + const REQUEST = 'Psr\Http\Message\ServerRequestInterface'; + + const RESPONSE = 'Psr\Http\Message\ResponseInterface'; + + const ROUTER = 'Rougin\Slytherin\Routing\RouterInterface'; + + const TEMPLATE = 'Rougin\Slytherin\Template\RendererInterface'; + + /** + * @var \Rougin\Slytherin\Integration\Configuration + */ + protected $config; + + /** + * @var \Rougin\Slytherin\Container\ContainerInterface + */ + protected $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; + + $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 mixed + */ + 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\Middleware\DispatcherInterface */ + $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::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/System/Endofline.php b/src/System/Endofline.php new file mode 100644 index 00000000..7af3de8e --- /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/src/Application/FinalCallback.php b/src/System/Handler.php similarity index 71% rename from src/Application/FinalCallback.php rename to src/System/Handler.php index 52bd7fc6..fd23a2d8 100644 --- a/src/Application/FinalCallback.php +++ b/src/System/Handler.php @@ -1,28 +1,24 @@ */ -class FinalCallback +class Handler implements HandlerInterface { - const REQUEST = 'Psr\Http\Message\ServerRequestInterface'; - - const RESPONSE = 'Psr\Http\Message\ResponseInterface'; - /** - * @var \Rougin\Slytherin\Container\Container + * @var \Rougin\Slytherin\Container\ContainerInterface */ protected $container; @@ -32,16 +28,16 @@ class FinalCallback protected $route; /** - * Sets up the callback handler. + * Initializes the system handler. * - * @param \Rougin\Slytherin\Routing\RouteInterface $route - * @param \Rougin\Slytherin\Container\Container $container + * @param \Rougin\Slytherin\Routing\RouteInterface $route + * @param \Rougin\Slytherin\Container\ContainerInterface $container */ - public function __construct(RouteInterface $route, Container $container) + public function __construct(RouteInterface $route, ContainerInterface $container) { - $this->container = $container; - $this->route = $route; + + $this->container = $container; } /** @@ -50,17 +46,19 @@ public function __construct(RouteInterface $route, Container $container) * @param \Psr\Http\Message\ServerRequestInterface $request * @return \Psr\Http\Message\ResponseInterface */ - public function __invoke(ServerRequestInterface $request) + public function handle(ServerRequestInterface $request) { // Attach the request again in the container to reflect from stack --- - $this->container->set(self::REQUEST, $request); + $this->container->set(System::REQUEST, $request); // ------------------------------------------------------------------- + $resolver = new Resolver($this->container); + $handler = $this->route->getHandler(); if (is_array($handler) && is_string($handler[0])) { - $handler[0] = $this->container->resolve($handler[0], $request); + $handler[0] = $resolver->resolve($handler[0], $request); /** @var object|string */ $objectOrMethod = $handler[0]; @@ -85,30 +83,7 @@ public function __invoke(ServerRequestInterface $request) $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; + return $this->setResponse($handler); } /** @@ -119,16 +94,18 @@ protected function finalize($function) */ protected function setParams(\ReflectionFunctionAbstract $reflector) { + $resolver = new Resolver($this->container); + $params = $this->route->getParams(); if (empty($params)) { - return $this->container->arguments($reflector, $params); + return $resolver->getArguments($reflector, $params); } $items = $reflector->getParameters(); - $values = $this->container->arguments($reflector, $params); + $values = $resolver->getArguments($reflector, $params); $result = array(); @@ -141,4 +118,25 @@ protected function setParams(\ReflectionFunctionAbstract $reflector) 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)) + { + $response->getBody()->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..ebea36b4 --- /dev/null +++ b/src/System/Resolver.php @@ -0,0 +1,137 @@ + + */ +class Resolver +{ + /** + * @var \Psr\Container\ContainerInterface + */ + protected $container; + + /** + * @var \Psr\Container\ContainerInterface + */ + protected $extra; + + /** + * @param \Psr\Container\ContainerInterface $container + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + + $this->extra = new ReflectionContainer; + } + + /** + * 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) + { + // Backward compatibility for ReflectionParameter --- + $param = new Parameter($parameter); + // -------------------------------------------------- + + $argument = null; $name = $param->getName(); + + if ($this->container->has($name)) + { + $argument = $this->container->get($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; + } +} 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/src/Template/Twig.php b/src/Template/Twig.php index f5e6a7ea..0a257e1f 100644 --- a/src/Template/Twig.php +++ b/src/Template/Twig.php @@ -3,17 +3,13 @@ namespace Rougin\Slytherin\Template; /** - * Twig Renderer + * 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 + * 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 */ 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/src/Template/TwigRenderer.php b/src/Template/TwigRenderer.php index 34998889..a177e215 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 = array(), $extension = 'html') { - return $this->twig->render($template, $data); + return $this->twig->render("$template.$extension", $data); } } diff --git a/tests/Application/ApplicationTest.php b/tests/Application/ApplicationTest.php index 5ff24c69..a5d86f07 100644 --- a/tests/Application/ApplicationTest.php +++ b/tests/Application/ApplicationTest.php @@ -2,13 +2,22 @@ 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; + /** * Application Test * * @package Slytherin * @author Rougin Gutib */ -class ApplicationTest extends \Rougin\Slytherin\Testcase +class ApplicationTest extends Testcase { /** * @var \Rougin\Slytherin\ComponentCollection @@ -22,33 +31,30 @@ 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.'); } - $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', - ); - - if (class_exists('Zend\Stratigility\MiddlewarePipe')) { - $components[] = 'Rougin\Slytherin\Fixture\Components\MiddlewareComponent'; - } - - $container = new \Rougin\Slytherin\IoC\Vanilla\Container; + $items = array(); - $globals = $GLOBALS; + $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\TemplateComponent'; - $components = \Rougin\Slytherin\Component\Collector::get($container, $components, $globals); + if (class_exists('Zend\Stratigility\MiddlewarePipe')) + { + $items[] = 'Rougin\Slytherin\Fixture\Components\MiddlewareComponent'; + } - $this->components = $components; + $this->components = Collector::get($items); } /** @@ -137,7 +143,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.'); } @@ -147,9 +154,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); @@ -170,33 +177,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); - - $integrations = array('Rougin\Slytherin\Http\HttpIntegration'); + $config->set('app.views', (string) $root); - $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'; - } + $items = array('Rougin\Slytherin\Http\HttpIntegration'); - $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(); } /** @@ -211,11 +214,12 @@ 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); - switch ($httpMethod) { + switch ($httpMethod) + { case 'GET': $request = $request->withQueryParams($data); @@ -230,6 +234,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/Application/ApplicationTestCases.php b/tests/Application/ApplicationTestCases.php index e2229f7c..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'; @@ -206,7 +202,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')) @@ -270,19 +266,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 = Application::container(); - - 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; } @@ -293,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/AurynContainerTest.php b/tests/Application/AurynContainerTest.php index 4b2cb5e1..9c013f7d 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\Middleware\Dispatcher as Middleware; +use Rougin\Slytherin\Routing\Dispatcher; +use Rougin\Slytherin\System; + /** * Auryn Container Test * @@ -17,24 +23,27 @@ 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 \Rougin\Slytherin\Container\AurynContainer; + $container = new 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::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'); - } + $container->share(new Middleware); + $container->alias(System::MIDDLEWARE, 'Rougin\Slytherin\Middleware\Dispatcher'); - $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..defba307 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\Middleware\Dispatcher as Middleware; +use Rougin\Slytherin\Routing\Dispatcher; +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::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 Middleware; + $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..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,39 +24,18 @@ 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'; + $config = new Configuration; - $middlewares = array('Rougin\Slytherin\Fixture\Middlewares\EmptyMiddleware'); + $router = $this->router(); - $config->set('app.middlewares', $middlewares); - } + $config->set('app.router', $router); - $app = new \Rougin\Slytherin\Application; - - $app->integrate('Rougin\Slytherin\Template\RendererIntegration'); - $app->integrate('Rougin\Slytherin\Integration\ConfigurationIntegration'); + $app = new Application; $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/Component/CollectionTest.php b/tests/Component/CollectionTest.php index 956ed045..3742b42e 100644 --- a/tests/Component/CollectionTest.php +++ b/tests/Component/CollectionTest.php @@ -2,13 +2,26 @@ 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\Template\TwigLoader; +use Rougin\Slytherin\Template\TwigRenderer; +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 +35,7 @@ class CollectionTest extends \Rougin\Slytherin\Testcase */ protected function doSetUp() { - $this->components = new \Rougin\Slytherin\Component\Collection; + $this->components = new Collection; } /** @@ -32,16 +45,29 @@ protected function doSetUp() */ public function testSetContainerMethod() { - if (! interface_exists('Psr\Container\ContainerInterface')) - { - $this->markTestSkipped('Container Interop is not installed.'); - } + $expected = new Container; - $container = new \Rougin\Slytherin\IoC\Vanilla\Container; + $this->components->setContainer($expected); - $this->components->setContainer($container); + $actual = $this->components->getContainer(); - $this->assertEquals($container, $this->components->getContainer()); + $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); } /** @@ -51,13 +77,13 @@ public function testSetContainerMethod() */ public function testSetDispatcherMethod() { - $router = new \Rougin\Slytherin\Dispatching\Vanilla\Router; + $expected = new Dispatcher(new Router); - $dispatcher = new \Rougin\Slytherin\Dispatching\Vanilla\Dispatcher($router); + $this->components->setDispatcher($expected); - $this->components->setDispatcher($dispatcher); + $actual = $this->components->getDispatcher(); - $this->assertEquals($dispatcher, $this->components->getDispatcher()); + $this->assertEquals($expected, $actual); } /** @@ -67,11 +93,29 @@ public function testSetDispatcherMethod() */ public function testSetDebuggerMethod() { - $debugger = new \Rougin\Slytherin\Debug\Vanilla\Debugger; + $expected = new Debugger; + + $this->components->setDebugger($expected); + + $actual = $this->components->getDebugger(); + + $this->assertEquals($expected, $actual); + } + + /** + * Tests the setErrorHandler() method. + * + * @return void + */ + public function testSetErrorHandlerMethod() + { + $expected = new Debugger; - $this->components->setDebugger($debugger); + $this->components->setErrorHandler($expected); - $this->assertEquals($debugger, $this->components->getDebugger()); + $actual = $this->components->getErrorHandler(); + + $this->assertEquals($expected, $actual); } /** @@ -81,24 +125,23 @@ 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'] = '/'; + $server['SERVER_NAME'] = 'localhost'; + $server['SERVER_PORT'] = '8000'; - $server = array( - 'SERVER_NAME' => 'localhost', - 'SERVER_PORT' => '8000', - 'REQUEST_URI' => '/', - 'REQUEST_METHOD' => 'GET', - ); + $request = new ServerRequest($server); - $response = new \Rougin\Slytherin\Http\Response; - $request = new \Rougin\Slytherin\Http\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); } /** @@ -108,23 +151,19 @@ 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'] = '/'; + $server['SERVER_NAME'] = 'localhost'; + $server['SERVER_PORT'] = '8000'; - $server = array( - 'SERVER_NAME' => 'localhost', - 'SERVER_PORT' => '8000', - 'REQUEST_URI' => '/', - 'REQUEST_METHOD' => 'GET', - ); + $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); } /** @@ -134,16 +173,13 @@ public function testSetHttpRequestMethod() */ public function testSetHttpResponseMethod() { - if (! interface_exists('Psr\Http\Message\ResponseInterface')) - { - $this->markTestSkipped('PSR HTTP Message is not installed.'); - } + $expected = new Response; - $response = new \Rougin\Slytherin\Http\Response; + $this->components->setHttpResponse($expected); - $this->components->setHttpResponse($response); + $actual = $this->components->getHttpResponse(); - $this->assertEquals($response, $this->components->getHttpResponse()); + $this->assertEquals($expected, $actual); } /** @@ -153,28 +189,44 @@ public function testSetHttpResponseMethod() */ public function testSetMiddlewareMethod() { - $response = 'Psr\Http\Message\ResponseInterface'; - - interface_exists($response) || $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); } /** - * 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 new file mode 100644 index 00000000..e0ccfc91 --- /dev/null +++ b/tests/Component/Server.php @@ -0,0 +1,22 @@ +run(); 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/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/Fixture/Middlewares/BodyParametersMiddleware.php b/tests/Fixture/Middlewares/BodyParametersMiddleware.php index 8c0637c0..35281a6c 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\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\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..dbd1c157 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\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\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 4eabfe72..4a9b34d0 100644 --- a/tests/Fixture/Middlewares/EmptyMiddleware.php +++ b/tests/Fixture/Middlewares/EmptyMiddleware.php @@ -4,23 +4,22 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Rougin\Slytherin\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\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, HandlerInterface $handler) + { + return $handler->handle($request); + } } 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/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 f62264e4..24a13683 100644 --- a/tests/Fixture/Middlewares/LastMiddleware.php +++ b/tests/Fixture/Middlewares/LastMiddleware.php @@ -6,26 +6,14 @@ use Psr\Http\Message\ServerRequestInterface; /** - * Last Middleware - * * @package Slytherin * @author Rougin Gutib */ 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() == '') { - $response->getBody()->write('Loaded with middleware'); - } else { - $response->getBody()->write(' Last!'); - } + $response->getBody()->write(' Last!'); 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/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 86521606..f6232552 100644 --- a/tests/Middleware/DispatcherTestCases.php +++ b/tests/Middleware/DispatcherTestCases.php @@ -3,6 +3,10 @@ namespace Rougin\Slytherin\Middleware; use Rougin\Slytherin\Http\ServerRequest; +use Rougin\Slytherin\Middleware\Interop; +use Rougin\Slytherin\Middleware\Wrapper; +use Rougin\Slytherin\System\Endofline; +use Rougin\Slytherin\Testcase; /** * Dispatcher Test Cases @@ -10,10 +14,10 @@ * @package Slytherin * @author Rougin Gutib */ -class DispatcherTestCases extends \Rougin\Slytherin\Testcase +class DispatcherTestCases extends Testcase { /** - * @var \Rougin\Slytherin\Middleware\DispatcherInterface + * @var \Rougin\Slytherin\Middleware\Dispatch */ protected $dispatcher; @@ -24,11 +28,14 @@ class DispatcherTestCases extends \Rougin\Slytherin\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; @@ -44,31 +51,35 @@ 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'; + $class = 'Rougin\Slytherin\Middleware\StratigilityDispatcher'; - $message .= ' does not accept single pass middlewares'; + if (is_a($this->dispatcher, $class)) + { + /** @var \Rougin\Slytherin\Middleware\StratigilityDispatcher */ + $zend = $this->dispatcher; - $this->markTestSkipped((string) $message); + if (! $zend->hasPsr() && ! $zend->hasFactory()) + { + $this->markTestSkipped('Current Stratigility version does not support single pass callbacks'); + } } $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); } /** @@ -78,23 +89,27 @@ public function testProcessMethodWithSinglePassCallback() */ public function testProcessMethodWithDelagateInterfaceCallback() { - $stratigility = 'Rougin\Slytherin\Middleware\StratigilityDispatcher'; - - $wrapper = 'Zend\Stratigility\Middleware\CallableMiddlewareWrapper'; + $class = 'Rougin\Slytherin\Middleware\StratigilityDispatcher'; - if (is_a($this->dispatcher, $stratigility) && ! class_exists($wrapper)) { - $message = 'Stratigility\'s current version'; + if (is_a($this->dispatcher, $class)) + { + /** @var \Rougin\Slytherin\Middleware\StratigilityDispatcher */ + $zend = $this->dispatcher; - $message .= (string) ' does not accept delegates'; - - $this->markTestSkipped((string) $message); + if (! $zend->hasPsr() && ! $zend->hasFactory()) + { + $this->markTestSkipped('Current Stratigility version does not support single pass callbacks'); + } } - $this->dispatcher->push(function ($request, $delegate) { - $response = $delegate->process($request); + $fn = function ($request, $next) + { + $response = $next($request); return $response->withHeader('Content-Type', 'application/json'); - }); + }; + + $this->dispatcher->push($fn); $expected = array('application/json'); @@ -110,16 +125,9 @@ 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'; - - $message .= ' does not accept PSR-15 middlewares'; - - $this->markTestSkipped((string) $message); + if (! Interop::exists()) + { + $this->markTestSkipped('Interop middleware/s not yet installed'); } $interop = 'Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'; @@ -140,11 +148,16 @@ public function testProcessMethodWithString() */ public function testPushMethodWithArray() { - $expected = array('Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'); + if (! Interop::exists()) + { + $this->markTestSkipped('Interop middleware/s not yet installed'); + } + + $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(); @@ -158,15 +171,16 @@ public function testPushMethodWithArray() */ public function testStackMethod() { - $this->dispatcher->push('Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'); - - $this->dispatcher->push('Rougin\Slytherin\Middleware\FinalResponse'); + if (! Interop::exists()) + { + $this->markTestSkipped('Interop middleware/s not yet installed'); + } - $expected = (integer) 2; + $this->dispatcher->push('Rougin\Slytherin\Fixture\Middlewares\InteropMiddleware'); - $result = $this->dispatcher->stack(); + $actual = $this->dispatcher->stack(); - $this->assertCount($expected, $result); + $this->assertCount(1, $actual); } /** @@ -186,6 +200,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); } } 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(); +// --------------------------------------- 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/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/Handlers/Cors.php b/tests/Sample/Handlers/Cors.php index 7d33bf79..3fc189fe 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\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\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..734fe9e7 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\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\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..dd012eb8 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\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\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..ea3352a3 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\Middleware\HandlerInterface; +use Rougin\Slytherin\Middleware\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 10ffa628..2c7cb123 100644 --- a/tests/Sample/Router.php +++ b/tests/Sample/Router.php @@ -26,6 +26,17 @@ public function routes($parsed = false) $this->get('without-slash', 'Hello@string'); + $fn = function ($request, $next) + { + $response = $next($request); + + $response->getBody()->write('From callable middleware!'); + + return $response; + }; + + $this->get('middleware', 'Hello@response', $fn); + $this->get('/', 'Home@index'); $this->get('/callable', function () @@ -49,6 +60,10 @@ public function routes($parsed = false) $this->get('/handler/param', 'Hello@param'); + $interop = 'Rougin\Slytherin\Sample\Handlers\Interop'; + + $this->get('interop', 'Hello@response', $interop); + 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..9b44c8a0 100644 --- a/tests/Sample/SampleTest.php +++ b/tests/Sample/SampleTest.php @@ -2,9 +2,11 @@ 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; +use Rougin\Slytherin\Sample\Packages\MiddlewarePackage; use Rougin\Slytherin\Sample\Packages\SamplePackage; use Rougin\Slytherin\Testcase; @@ -165,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'); @@ -181,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'); @@ -197,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'); @@ -206,6 +214,43 @@ 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->addPackage(new MiddlewarePackage); + + $this->builder->setUrl('GET', '/middleware'); + + $this->expectOutputString('From callable middleware!'); + + $this->builder->make()->run(); + } + + /** + * @runInSeparateProcess + * + * @return void + */ + 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'); + } + + $this->builder->setUrl('GET', '/interop'); + + $this->expectOutputString('From interop!'); + + $this->builder->make()->run(); + } + /** * @runInSeparateProcess * 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); }