diff --git a/.travis.yml b/.travis.yml index b4bb4ba..019dbb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: php php: - - 7.0 - 7.1 + - 7.2 before_script: - composer install --no-interaction diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aa7119..68837c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +10.0 +==== + +- Most code has been moved to thecodingmachine/splash-router +- Only classes specific to Mouf or embedding some display logic stay in this package + 7.0 === diff --git a/README.md b/README.md index d8d4b9e..16f7f3a 100644 --- a/README.md +++ b/README.md @@ -6,121 +6,7 @@ [![Build Status](https://travis-ci.org/thecodingmachine/mvc.splash-common.svg?branch=8.0)](https://travis-ci.org/thecodingmachine/mvc.splash-common) [![Coverage Status](https://coveralls.io/repos/thecodingmachine/mvc.splash-common/badge.svg?branch=8.0&service=github)](https://coveralls.io/github/thecodingmachine/mvc.splash-common?branch=8.0) -Splash: a highly configurable PSR-7 router -========================================== +Splash: a highly configurable PSR-15 router +=========================================== -What is Splash? ---------------- - -Splash is a PHP router. It takes an HTTP request and dispatches it to the appropriate controller. - -- Splash is [PSR-7 compatible](http://www.php-fig.org/psr/psr-7/) -- It acts as a [PSR-7 middleware](https://akrabat.com/writing-psr-7-middleware/) -- It is based on **controllers** and **annotations** (routes are declared as annotations in the controllers) -- It is heavily optimized, relying on an underlying [PSR-6 cache](http://www.php-fig.org/psr/psr-6/) -- It is a **pure** router. It is not a full-featured MVC framework. No views management, no model, only routing! -- It promotes best practices. Controllers must be declared in a [container-interop compatible container](https://github.com/container-interop/container-interop/). -- It is extensible. -- It integrates with a high number of tools: - - With [Mouf](http://mouf-php.com): this provides a friendly UI to generate controllers - - With [Drupal (through Druplash, a module that adds a Splash-compatible MVC framework)](http://mouf-php.com/packages/mouf/integration.drupal.druplash), - - With [Wordpress (through Moufpress, a plugin that adds a Splash-compatible MVC framework)](http://mouf-php.com/packages/mouf/integration.wordpress.moufpress), - - With [Joomla (through Moufla, a plugin that adds a Splash-compatible MVC framework)](http://mouf-php.com/packages/mouf/integration.wordpress.moufpress), - - With [Magento (through Moufgento, a plugin that adds a Splash-compatible MVC framework)](http://mouf-php.com/packages/mouf/integration.magento.moufgento), -- And it supports emoji routes (mydomain.com/😍) - - -Clean controllers ------------------ - -Want to get a feeling of Splash? Here is a typical controller: - -```php -use Mouf\Mvc\Splash\Annotations\Get; -use Mouf\Mvc\Splash\Annotations\URL; - -class MyController -{ - /** - * @URL("/my/path") - * @Get - */ - public function index(ServerRequestInterface $request) - { - return new JsonResponse(["Hello" => "World!"]); - } -} -``` - -Ok, so far, things should be fairly obvious to anyone used to PSR-7. The important parts are: - -- Routing is done using the **@URL** annotation. When a method has the **@URL** annotation, we call it an *action*. -- **Controllers are clean**. They don't extend any "Splash" object (so are reusable in any other PSR-7 compatible MVC framework) -- Actions can optionally have a **@Get**, **@Post**, **@Put**, **@Delete** annotation to restrict the response to some HTTP method. -- Splash analyzes the action signature. If it finds a type-hinted `ServerRequestInterface` parameter, it will fill it the PSR-7 request object. -- Actions must return an object implementing the PSR-7 `ResponseInterface`. - - -Even better ------------ - -But Splash can provide much more than this. - -Here is a more advanced routing scenario: - -```php -use Mouf\Mvc\Splash\Annotations\Post; -use Mouf\Mvc\Splash\Annotations\URL; -use Psr\Http\Message\UploadedFileInterface; - -class MyController -{ - /** - * @URL("/user/{id}") - * @Post - */ - public function index($id, $firstName, $lastName, UploadedFileInterface $logo) - { - return //...; - } -} -``` - -Look at the signature: `public function index($id, $firstName, $lastName, UploadedFileInterface $logo)`. - -- `$id` will be fetched from the URL (`@URL("/user/{id}")`) -- `$firstName` and `$lastName` will be automatically fetched from the GET/POST parameters -- finally, `$logo` will contain the uploaded file from `$_FILES['logo']` - -See the magic? **Just by looking at your method signature, you know what parameters your route is expecting.** The method signature is self-documenting the route! - -Even better, Splash is highly extensible. You can add your own plugins to automatically fill some parameters of the request (we call those `ParameterFetchers`). -You could for instance write a parameter fetcher to automatically load Doctrine entities: - -```php - /** - * Lo and behold: you can extend Splash to automatically fill the function parameters with objects of your liking - * - * @URL("/product/{product}") - * @Post - */ - public function index(My\Entities\Product $product) - { - return //...; - } - -``` - -Best practices --------------- - -You might wonder: "*How will Splash instantiate your controller*?" Well, Splash will not instantiate your controller. -Instantiating services and containers is the role of the dependency injection container. Splash connects to any *container-interop* compatible container and will fetch your controllers from the container. - -This means that you **must** declare your controller in the container you are using. This is actually a *good thing* as this encourages you to not use the container as a service locator. - - -High performance ----------------- - -For best performance, Splash is caching the list of routes it detects. Unlike what can be seen in most micro-frameworks where the application slows down as the number of routes increases, in Splash, **performance stays constant as the number of routes increases**. +[Splash documentation is available at https://thecodingmachine.github.io/splash-router/](https://thecodingmachine.github.io/splash-router/) \ No newline at end of file diff --git a/composer.json b/composer.json index 1c140c6..2036e95 100644 --- a/composer.json +++ b/composer.json @@ -21,35 +21,24 @@ ], "require" : { "php" : ">=7.0", - "psr/cache": "^1.0", - "cache/void-adapter": "^0.3.0", - "doctrine/annotations": "^1.2", + "thecodingmachine/splash-router": "^10", + "mouf/mouf": "^2.0", "mouf/html.htmlelement" : "^2.0", "mouf/utils.action.common-action" : "~1.0", - "mouf/utils.common.url-interface" : "~1.0", "mouf/html.renderer.twig-extensions": "~1.0", "mouf/utils.common.conditioninterface": "~2.0", - "mouf/utils.cache.cache-interface": "~2.0", - "zendframework/zend-diactoros": "~1.0", - "mouf/html.template.templateinterface": "^2.1", - "container-interop/service-provider": "^0.4", - "thecodingmachine/common-factories": "^0.4", - "thecodingmachine/middleware-list-universal-module": "~1.0", - "http-interop/http-middleware": "^0.4" + "mouf/html.template.templateinterface": "^2.1" }, "require-dev": { "phpunit/phpunit": "^5.0", "satooshi/php-coveralls": "^1.0", "mouf/picotainer": "~1.0", "mnapoli/simplex": "^0.3", - "cache/array-adapter": "^0.4.0" - }, - "conflict": { - "zendframework/zend-stratigility": "<2.0" + "cache/array-adapter": "^0.4.0", + "phpstan/phpstan": "^0.10.1" }, "autoload" : { "psr-4" : { - "Mouf\\Annotations\\" : "src/Mouf/Annotations", "Mouf\\Mvc\\Splash\\" : "src/Mouf/Mvc/Splash" } }, @@ -58,64 +47,14 @@ "Mouf\\Mvc\\Splash\\" : "tests/Mouf/Mvc/Splash" } }, + "scripts": { + "phpstan": "phpstan analyse src/Mouf -c phpstan.neon --level=0 --no-progress -vvv" + }, "minimum-stability": "dev", "prefer-stable": true, "extra" : { "mouf" : { "logo" : "doc/images/logo.png", - "doc" : [{ - "title" : "Installing Splash", - "url" : "doc/install/index.md", - "children": [ - { - "title" : "Standalone mode", - "url" : "doc/install/standalone.md" - }, - { - "title" : "Mouf integration", - "url" : "doc/install/mouf.md" - }, - { - "title" : "Service provider", - "url" : "doc/service-provider.md" - } - ] - }, - { - "title" : "Writing controllers", - "url" : "doc/writing_controllers_manually.md" - }, - { - "title" : "Integrations", - "children" : [ - { - "title" : "Mouf", - "children" : [ - { - "title" : "Controller creation wizard", - "url" : "doc/mouf/writing_controllers.md" - }, - { - "title" : "Advanced: configuring routing", - "url" : "doc/url_routing.md" - } - ] - } - ] - }, - { - "title" : "Managing URL parameters", - "url" : "doc/url_parameters.md" - }, - { - "title" : "Writing your own filters", - "url" : "doc/filters.md" - }, - { - "title" : "Migrating from older version", - "url" : "doc/migrating.md" - } - ], "section": { "name": "MVC", "description": "All the tools you need to route requests and display pages", diff --git a/doc/filters.md b/doc/filters.md deleted file mode 100644 index e9a274e..0000000 --- a/doc/filters.md +++ /dev/null @@ -1,91 +0,0 @@ -Filters -======= - -Using filters -------------- - -Splash supports the notion of _"Filter"_. A filter is a piece of code that can wrap an action. - -There could be many reason why you want to run a filter: - - - Check that a user is logged before starting an action - - Check that the action is performed on an SSL channel - - Provide caching - - Initialize some frameworks, ... - -In Splash, filters are **annotations**. - -Below are sample filters: - -```php -/** - * @URL("/test") - * @Logged - * @RequireHttps("yes") - */ -function deleteUser($password) { ... } -``` - -This sample provides 2 filters: - - - *@Logged* is used by Splash to check that the user is logged. If not, the user is sent to the login page. - - *@RequireHttps* is used by Splash to make sure the action is run on an HTTPS channel. If not, an error message is displayed. - -Note: the *@Logged* filter is not part of the base distribution of Splash. Indeed, Splash is in no way an authentication framework. -However, if you are using the _mouf/security.userservice_ package, you might want to add the _mouf/security.userservice-splash_ package -that provides this usefull *@Logged* annotation. - -Please note the @RequireHttps annotation accepts one parameter. This parameter can be: - -- By passing @RequireHttps("yes"), an Exception is thrown if the action is called in HTTP. -- By passing @RequireHttps("no"), no test is performed. -- By passing @RequireHttps("redirect"), the call is redirected to HTTPS. This does only work with GET requests. - - -There is a third default filter worth mentioning: - -The *@RedirectToHttp* filter will bring the user back to HTTP if the user is in HTTPS. The port can be specified in parameter if needed. The filter -works only with GET requests. If another type of request is performed (POST), an exception will be thrown. - -Developing your own filters ---------------------------- - -You can of course develop your own filters. -Each filter is represented by a class. - -To create a filter: - - - The filters must be a valid [Doctrine annotation](http://doctrine-orm.readthedocs.io/projects/doctrine-common/en/latest/reference/annotations.html). - - The filter must implement a `__invoke` method whose signature is: - ```php - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next, ContainerInterface $container); - ``` - -Note that a filter is very similar to a PSR-7 middleware, except the `__invoke` method is additionally passed a container (useful to bind the annotation to a given service). - -Here is the typical filter layout: - -```php -/** - * A filter is an annotation, therefore, it MUST have the @Annotation annotation. - * - * @Annotation - */ -class SampleFilter -{ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next, ContainerInterface $container) - { - // Do some stuff before the action is called - // ... - - // Then call the action: - $response = $next($request, $response); - - // Then do some stuff after the action is called - // ... - - // Finally, return the response - return $response; - } -} -``` diff --git a/doc/images/activate_splash.png b/doc/images/activate_splash.png deleted file mode 100644 index 6b9d640..0000000 Binary files a/doc/images/activate_splash.png and /dev/null differ diff --git a/doc/images/configure_splash.jpg b/doc/images/configure_splash.jpg deleted file mode 100644 index 00ff108..0000000 Binary files a/doc/images/configure_splash.jpg and /dev/null differ diff --git a/doc/images/create_instance.jpg b/doc/images/create_instance.jpg deleted file mode 100644 index 6185d38..0000000 Binary files a/doc/images/create_instance.jpg and /dev/null differ diff --git a/doc/images/create_instance.png b/doc/images/create_instance.png deleted file mode 100644 index f251f8c..0000000 Binary files a/doc/images/create_instance.png and /dev/null differ diff --git a/doc/images/create_instance_splash.jpg b/doc/images/create_instance_splash.jpg deleted file mode 100644 index d11f700..0000000 Binary files a/doc/images/create_instance_splash.jpg and /dev/null differ diff --git a/doc/images/images.ppt b/doc/images/images.ppt deleted file mode 100644 index e22d523..0000000 Binary files a/doc/images/images.ppt and /dev/null differ diff --git a/doc/images/install_splash.png b/doc/images/install_splash.png deleted file mode 100644 index 67d1e7a..0000000 Binary files a/doc/images/install_splash.png and /dev/null differ diff --git a/doc/images/install_splash_2.png b/doc/images/install_splash_2.png deleted file mode 100644 index a6285ea..0000000 Binary files a/doc/images/install_splash_2.png and /dev/null differ diff --git a/doc/images/install_splash_3.png b/doc/images/install_splash_3.png deleted file mode 100644 index 9fb7cdb..0000000 Binary files a/doc/images/install_splash_3.png and /dev/null differ diff --git a/doc/images/logo.png b/doc/images/logo.png deleted file mode 100644 index a8f99a3..0000000 Binary files a/doc/images/logo.png and /dev/null differ diff --git a/doc/images/register_controller_file.jpg b/doc/images/register_controller_file.jpg deleted file mode 100644 index d19e40d..0000000 Binary files a/doc/images/register_controller_file.jpg and /dev/null differ diff --git a/doc/images/splash_instance.png b/doc/images/splash_instance.png deleted file mode 100644 index de5242b..0000000 Binary files a/doc/images/splash_instance.png and /dev/null differ diff --git a/doc/images/splash_menu.png b/doc/images/splash_menu.png deleted file mode 100644 index 5f2d812..0000000 Binary files a/doc/images/splash_menu.png and /dev/null differ diff --git a/doc/images/splash_menu_section.png b/doc/images/splash_menu_section.png deleted file mode 100644 index 457cb5e..0000000 Binary files a/doc/images/splash_menu_section.png and /dev/null differ diff --git a/doc/images/template_structure.png b/doc/images/template_structure.png deleted file mode 100644 index 7b9954a..0000000 Binary files a/doc/images/template_structure.png and /dev/null differ diff --git a/doc/images/wizard.png b/doc/images/wizard.png deleted file mode 100644 index 6dc4d68..0000000 Binary files a/doc/images/wizard.png and /dev/null differ diff --git a/doc/images/wizard_menu.png b/doc/images/wizard_menu.png deleted file mode 100644 index d65d28c..0000000 Binary files a/doc/images/wizard_menu.png and /dev/null differ diff --git a/doc/install/index.md b/doc/install/index.md deleted file mode 100644 index 7aa6cf0..0000000 --- a/doc/install/index.md +++ /dev/null @@ -1,9 +0,0 @@ -Installing Splash -================= - -Splash can integrate in many different ways with your application. Therefore, installation will highly vary depending on the kind of install you are looking to perform. - -Choose the install you want to work on: - -- [Installing Splash in a stand-alone / microframework environment](standalone.md) -- [Installing Splash in a Mouf environment](mouf.md) diff --git a/doc/install/mouf.md b/doc/install/mouf.md deleted file mode 100644 index 69c9e46..0000000 --- a/doc/install/mouf.md +++ /dev/null @@ -1,82 +0,0 @@ -Installing Splash 8 in Mouf -=========================== - -Dependencies ------------- - -Splash 8 comes as a *Composer* package. If you are using Mouf, there is a special package that eases Splash installation in Mouf. - -The first step is therefore to [install Mouf](http://www.mouf-php.com/). - -Once Mouf is installed, you can process to the Splash installation. - -Requirements ------------- - -For Splash to work, you will need an Apache server, with the *rewrite_module* enabled. - -Install Splash --------------- - -Edit your *composer.json* file, and add a dependency on *mouf/mvc.splash*. - -A minimal *composer.json* file might look like this: - -```json -{ - "require": { - "mouf/mouf": "~2.0.0", - "mouf/mvc.splash": "~8.0" - }, - "autoload": { - "psr-4": { - "Test\\": "src/Test/" - } - }, - "minimum-stability": "dev", - "prefer-stable": true -} -``` - -As explained above, Splash integrates easily with Mouf framework (before version 8, Splash was heavily tied to Mouf, but since version 8, Mouf is only one possible integration amongst many others). Mouf allows you (among other things) to visually "build" your project's dependencies and instances. - -To install the dependency, run - - php composer.phar install - -This *composer.json* file assumes that you will put your code in the "src" directory, and that you will use the "Test" namespace and respect the PSR-0 naming scheme. -Be sure to create those directories (src/Test) before running the install process. -If you do not understand what "namespace" or "PSR-4" means, *stop right now*, and head over the [autoloading section of Composer](http://getcomposer.org/doc/01-basic-usage.md#autoloading) and the [PSR-0 documentation](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md). - -At this point, the Splash packages should be downloaded and installed (and Mouf should be set up). Start the Mouf admin interface at http://localhost/{yourproject}/vendor/mouf/mouf -There is an install process to run, so just run it. - -![Splash install screenshot](../images/install_splash.png) -![Splash install screenshot](../images/install_splash_2.png) - -The Splash install process will: - - - Create a number of instances in the container. Especially, it will set up a Stratigility pipe, with Splash middleware included and fully configured. - - Create an Apache .htaccess file that will route the requests to Splash - - Create a default `RootController` class to handle the requests to the root of your web application - - Create a default HTML view for the RootController - -The install process does its best to use your namespace for the RootController, and it asks you where the files should go: -![Splash install screenshot](../images/install_splash_3.png) - - -Configure Apache redirection ----------------------------- - -When Splash is installed, a MVC menu appears in Mouf. - -![Splash menu](../images/splash_menu.png) - -The *Configure Apache redirection* menu helps you configuring what files should be handled by Splash and what files should be ignored. -By default, resource files (images, js, css...) are ignored. - -Purging the cache ------------------ - -Splash comes with a caching system used to speed it up. -When you modify a route (for instance, when you create a new controller), please be sure to purge the cache (using MVC/Splash/Purge URLs cache or the big "Purge PSR-6 cache" button) to take your modifications into account. diff --git a/doc/install/standalone.md b/doc/install/standalone.md deleted file mode 100644 index 2f09d7f..0000000 --- a/doc/install/standalone.md +++ /dev/null @@ -1,143 +0,0 @@ -Installing Splash 8 in standalone / microframework mode -======================================================= - -There are [many ways to install Splash](index.md). In this article, we will show you how to get started with a standalone install (no framework). - -In order to work, Splash needs: - -Requirements ------------- - -- A PSR-6 cache -- A dependency injection container -- A PSR-7 middleware pipe we can tap into - -All these components are up to you. In this article, we will show you how to get started with: - -- [Stash (for a PSR-6 caching library)](http://www.stashphp.com/) -- [Simplex (for a container)](https://github.com/mnapoli/simplex/). Note: Simplex is a Pimple 3 fork that adds compatibility with container-interop and with container-interop/service-providers. This will be hugely useful to speed up container configuration. -- [Stratigility (for PSR-7 middleware piping)](https://github.com/zendframework/zend-stratigility) - -Of course, your mileage may vary and you can really use any compatible library here. - -Install -------- - -Here is a typical `composer.json` file to load all dependencies. - -**composer.json** -```json -{ - "autoload": { - "psr-4": {"Test\\": "src/Test"} - }, - "require": { - "mouf/mvc.splash-common": "~8.2", - "mnapoli/simplex": "~0.2.1", - "zendframework/zend-diactoros": "^1.3", - "zendframework/zend-stratigility": "^2.0", - "tedivm/stash": "~0.14.0", - "thecodingmachine/doctrine-annotations-universal-module": "~1.0", - "thecodingmachine/psr-6-doctrine-bridge-universal-module": "~1.0", - "thecodingmachine/stash-universal-module": "~1.0", - "thecodingmachine/stratigility-harmony": "~2.0" - }, - "minimum-stability": "dev", - "prefer-stable": true -} -``` - -Note: the *thecodingmachine/xxx-universal-module* packages are service providers that we will use to quickly set up the environment. -Note: the *thecodingmachine/whoops-middleware-universal-module* package is optional. It helps debugging by displaying a nice error screen. - -Run `composer install`. - -Now, we need to set up the entry point. - -**public/index.php** -```php -register(new DoctrineAnnotationsServiceProvider()); -$container->register(new DoctrineCacheBridgeServiceProvider()); -$container->register(new MiddlewareListServiceProvider()); -$container->register(new StashServiceProvider()); -$container->register(new StratigilityServiceProvider()); -// Note: You should remove the whoops service provider in production. -$container->register(new WhoopsMiddlewareServiceProvider()); - -// The Splash service provider will automatically register Splash in the Stratigility middleware pipe. -$container->register(new SplashServiceProvider()); - -// Let's register a controller in the container: -$container->set('rootController', function() { - return new \Test\RootController(); -}); - -// The 'thecodingmachine.splash.controllers' entry must contain an array of controller instances. -$container->set('thecodingmachine.splash.controllers', [ - 'rootController' -]); - -// We assume the 'BASE' environment variable contains the base URL of the application (see .htaccess below) -$container->set('root_url', getenv('BASE')); - -// Let's get the PSR-7 server, and let's bootstrap it. -$diactorosServer = $container->get(\Zend\Diactoros\Server::class); -$diactorosServer->listen(new \Zend\Stratigility\NoopFinalHandler()); -``` - -The important part here is: - -- Controllers are all declared in the container: - ```php - $container->set('rootController', function() { - return new \Test\RootController(); - }); - ``` - Note: if you are using an autowiring container, the container will automatically create the container for you. -- The `'thecodingmachine.splash.controllers'` container entry must contain an array listing the name of all controllers. - -You can review []all available configuration parameters in the Splash service provider documentation](../service-provider.md). - -If you are using Apache, you can use the `.htaccess` file below to automatically route. - -**public/.htaccess** -```php - - RewriteEngine On - - # .htaccess RewriteBase related tips courtesy of Symfony 2's skeleton app. - - # Determine the RewriteBase automatically and set it as environment variable. - # If you are using Apache aliases to do mass virtual hosting or installed the - # project in a subdirectory, the base path will be prepended to allow proper - # resolution of the base directory and to redirect to the correct URI. It will - # work in environments without path prefix as well, providing a safe, one-size - # fits all solution. But as you do not need it in this case, you can comment - # the following 2 lines to eliminate the overhead. - RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ - RewriteRule ^(.*) - [E=BASE:%1] - - # If the requested filename exists, simply serve it. - # We only want to let Apache serve files and not directories. - RewriteCond %{REQUEST_FILENAME} -f - RewriteRule .*(^index.php) - [L] - - # Rewrite all other queries to the front controller. - RewriteRule .? %{ENV:BASE}/index.php [L] - -``` diff --git a/doc/migrating.md b/doc/migrating.md deleted file mode 100644 index e664006..0000000 --- a/doc/migrating.md +++ /dev/null @@ -1,58 +0,0 @@ -Migrating from Splash 7 to Splash 8 ------------------------------------ - -In order to upgrade from Splash 7 to Splash 8, you need to perform the following steps: - -- Update "mouf/mvc.splash" version to "~8.0" in your `composer.json` file. -- Run `php composer.phar update` -- Remove `extends Controller` in each controller. Starting from Splash 8, controllers do not extend any class. -- Update all `@URL` annotations. In Splash 8, annotations are handled by the Doctrine annotations library. Therefore: - - You must add a `use Mouf\Mvc\Splash\Annotations\URL;` in each controller. - - You must rewrite annotations to the Doctrine format: - ``` - @URL my/path => @URL("my/path") - ``` -- Update all `@Action` annotations. - - You must add a `use Mouf\Mvc\Splash\Annotations\Action;` in each controller. -- Assuming you are using Mouf (this is a safe assumption since Splash 7 is highly tied to Mouf), run the Splash installer again. - - Connect to Mouf UI (http://localhost/[yourproject]/vendor/mouf/mouf) - - Click on *Project > Installation tasks* - - There are 2 install tasks for "mouf/mvc.splash". Locate those in the table. - - Click on the **Reinstall** button for both tasks. - -You are done. Enjoy the new features! - -Migrating from Splash 5 to Splash 7 ------------------------------------ - -In order to upgrade from Splash 5 to Splash 7, you need to perform the following steps: - -- Update "mouf/mvc.splash" version to "~7.0" in your `composer.json` file. -- Run `php composer.phar update` -- Connect to Mouf UI (http://localhost/[yourproject]/vendor/mouf/mouf) -- Click on *Project > Installation tasks* -- There are 2 install tasks for "mouf/mvc.splash". Locate those in the table. -- Click on the **Reinstall** button for both tasks. -- In your controllers, stop using the `Request` and `Response` classes and start using PSR-7's `ServerRequestInterface` and `ResponseInterface` - -You are done. Enjoy the new features! - -Hey! What about Splash 6? -Mmmm... there was never a stable Splash 6. Splash 6 is a release targeting Mouf 2.1 that is not released as we write these lines. - -Migrating from Splash 4 to Splash 5 ------------------------------------ - -In order to upgrade from Splash 4 to Splash 5, you need to perform the following steps: - -- Update "mouf/mvc.splash" version to "~5.0" in your `composer.json` file. -- Run `php composer.phar update` -- Connect to Mouf UI (http://localhost/[yourproject]/vendor/mouf/mouf) -- Click on *Instances > View declared instances* -- Look for the "splash" instance. -- Click on it, then click on the "Delete" button -- Click on *Project > Installation tasks* -- There are 2 install tasks for "mouf/mvc.splash". Locate those in the table. -- Click on the **Reinstall** button for both tasks. - -You are done. Enjoy [the new features](http://mouf-php.com/stackphp-support-added-to-splash)! diff --git a/doc/mouf/url_routing.md b/doc/mouf/url_routing.md deleted file mode 100644 index 87ca306..0000000 --- a/doc/mouf/url_routing.md +++ /dev/null @@ -1,39 +0,0 @@ -URL routing -=========== - -Since version 7.0, Splash implements a routing system based on [Stratigility](https://github.com/zendframework/zend-stratigility). - -The basics ----------- - -Splash has a "middleware stack". Splash will give the Request to the first middleware that will handle it. -Eventually, the first middleware will forward it to the next one. The second middleware will maybe -forward the request to the third middleware, and so on... - -**Note:** Some middlewares can, in fact, be considered as filters. - -Splash's default middleware implementation -------------------------------------------- -Here is the Splash instance view after installation : - -![Default splash instance view](../images/splash_instance.png) - -If you look closer at the stack, here is what you will find out : - -```php -> PhpVarsCheckRouter // Checks if max_input_vars and max_post_size have not been exceeded -> SplashDefaultRouter // Main router (will find a matching Route (controller / actions), and return the HTML -> NotFoundRouter // No router has been able to handle the Request, return a 404 response -> ExceptionRouter // Surround the router stack with a try/catch statement, and handle Exceptions display -``` - -The `PhpVarsCheckRouter` should be placed at the beginning of the stack, and at least before the "effective" routers -The `NotFoundRouter` should always be the last but one. -The `Exception` router should always be the last one - -Adding your own router into the stack -------------------------------------- - -To do so, you just need to create an instance of a middleware. Check the [Stratigility](https://github.com/zendframework/zend-stratigility) documentation. - -There are many existing PSR-7 middlewares that you can use and add to the pipeline. diff --git a/doc/mouf/writing_controllers.md b/doc/mouf/writing_controllers.md deleted file mode 100755 index 4da6631..0000000 --- a/doc/mouf/writing_controllers.md +++ /dev/null @@ -1,150 +0,0 @@ -Writing controllers -=================== - -What is a controller? ---------------------- - -In Splash, a controller is a class that contains a number of _Actions_. -_Actions_ are methods that can be directly accessed from the browser. -What binds a URL to an action is called a _route_. - -Controllers can be created very easily, using Splash user interface. Check it out! - - - -Creating a controller using Splash's wizard -------------------------------------------- - -For your application to print some HTML when you call a URL, you need 4 things: - -- A **controller** class: this is the class that will contain the actions declaration -- **Actions** methods: these are the methods that will be called when a URL is called -- A controller **instance**, declared in Mouf: you must create one instance of your class in Mouf (usually, only one instance) -- **Views**: these are files that contain the HTML to be outputed. We keep the HTML out of the controller to make the code - more readable and to separate concerns between the controller (in charge of the logic) and the view (in charge of the rendering) - -The Splash create-a-controller wizard will create those 4 things for you. - -Let's start. Go in the **MVC** > **Splash** > **Create a new controller** menu. - -![Create a controller menu](../images/wizard_menu.png) - -You will be displayed a page you can use to create your controller. - -![Create a controller menu](../images/wizard.png) - -Use this page to configure your controller! You can add as many actions as you want, you can even -decide what parameters must be passed to those actions. -You shall also decide what instances should be injected by default in your controller (you can of -course later change the class code and add more properties to the controller). - -By default, you can inject in your controller: - -- a logger -- an HTML template -- the content block of the HTML template -- the [TDBM DAO factory](http://mouf-php.com/packages/mouf/database.tdbm/index.md) (if you are using TDBM) - - -The @URL annotation -------------------- - -Have a look at the actions that have been declared: - -```php - -``` - -The *@URL* annotation points to the web path the action is bound to. - -The action takes 2 parameters: var1 and var2. This means that the page needs both parameters passed -either in GET or POST. - -The @Get / @Post annotations ----------------------------- - -We might decide that an action should always be called via GET, or via POST (or PUT or DELETE if you want to provide REST services). -Splash makes that very easy to handle. You can just add a @Get or @Post annotation (or @Put or @Delete). Here is a sample: - -```php - -``` - -Do you see the @URL annotation? The {id} part is a placeholder that will be replaced by any value found in the URL. -So for instance, if you access http://[server]/[appname]/user/42/view, the $id parameter will be filled with "42". - diff --git a/doc/service-provider.md b/doc/service-provider.md deleted file mode 100644 index d1fcebb..0000000 --- a/doc/service-provider.md +++ /dev/null @@ -1,71 +0,0 @@ -# Universal service provider - -Splash provides a service-provider to be easily integrated in any [container-interop/service-provider](https://github.com/container-interop/service-provider) compatible framework/container. - -If you need a complete working sample, check the [Splash standalone installation guide](install/standalone.md). - -## Registering the service provider - -You need to register the [`Mouf\Mvc\Splash\DI\SplashServiceProvider`](src/Mouf/Mvc/Splash/DI/SplashServiceProvider.php) into your container. - -If your container supports thecodingmachine/discovery integration, you have nothing to do. Otherwise, refer to your framework or container's documentation to learn how to register *service providers*. - -## Introduction - -This service provider will provide a default Splash router. - -It requires an instance of Doctrine's annotation reader to be available. - -Note: you can get a service provider providing a Doctrine annotation reader using the following packages: - -``` -composer require thecodingmachine/doctrine-annotations-universal-module -``` - -It will use a PSR-6 cache if the cache is available. -Note: you can get a service provider providing a working PSR-6 cache using the following packages: - -``` -composer require thecodingmachine/stash-universal-module -``` - -This will install Stash and its related service-provider. - -## Expected values / services - -This *service provider* expects the following configuration / services to be available: - -| Name | Compulsory | Description | -|-----------------|------------|----------------------------------------| -| `thecodingmachine.splash.controllers` | *yes* | A list of controllers entry identifiers in the container (it is an array of strings, each string is the entry of a controller) | -| `Doctrine\Common\Annotations\Reader` | *yes* | An instance of Doctrine's annotation reader. | -| `CacheItemPoolInterface::class` | *no* | The PSR-6 cache pool used to cache the routes | -| `LoggerInterface::class` | *no* | An optional PSR-3 logger | -| `thecodingmachine.splash.mode` | *no* | The mode Splash runs into. Can be one of `SplashUtils::MODE_STRICT` or `SplashUtils::MODE_WEAK`. Defaults to `SplashUtils::MODE_STRICT`. | -| `thecodingmachine.splash.debug` | *no* | If true, Splash will display an error with the 'echoed' output in strict mode. Defaults to true. | -| `thecodingmachine.splash.root_url` (or `root_url`) | *no* | The base URL of the application. Defaults to '/'. | - - -## Provided services - -This *service provider* provides the following services: - -| Service name | Description | -|-----------------------------|--------------------------------------| -| `SplashDefaultRouter::class` | The Splash router | -| `thecodingmachine.splash.route-providers` | A list of "route providers" for Splash (an array of `UrlProviderInterface`). Each route provider is in charge of feeding routes to Splash. By default, this array contains an instance of the `ControllerRegistry` that scans routes of the controllers. | -| `ControllerRegistry::class` | Instance of `ControllerRegistry`. | -| `ParameterFetcherRegistry::class` | Registry class referencing all parameter fetchers | -| `thecodingmachine.splash.parameter-fetchers` | Array of `ParameterFetcher` objects | -| `SplashRequestFetcher::class` | An instance of `SplashRequestFetcher` (to autofill the attributes type-hinted on the `ServerRequestInterface`) | -| `SplashRequestParameterFetcher::class` | An instance of `SplashRequestParameterFetcher` (to autofill attributes from the request) | - - - -## Extended services - -This *service provider* registers the `SplashDefaultRouter::class` in the `MiddlewareListServiceProvider::MIDDLEWARES_QUEUE`. - -| Service name | Description | -|-----------------------------|--------------------------------------| -| `MiddlewareListServiceProvider::MIDDLEWARES_QUEUE` | Adds the Splash middleware to this queue (to be used by external routers) | diff --git a/doc/url_parameters.md b/doc/url_parameters.md deleted file mode 100644 index 7788be6..0000000 --- a/doc/url_parameters.md +++ /dev/null @@ -1,127 +0,0 @@ -Managing URL parameters -======================= - -Default behaviour ------------------ - -By default, when an action is created, each parameter of the function should be passed in the URL. Here is a sample: - -```php -/** - * My action with 2 compulsory parameters. - * - * @URL("/test") - * @param string $var1 - * @param string $var2 - */ -public function my_action($var1, $var2) { ... } -``` - -In this action, both parameters are compulsory. If one of the parameters is not passed by the user, an error message is displayed ("HTTP 400 Bad request" return code). -Hopefully, you can get optional parameters using parameters default values: - -```php -/** - * My action with 1 compulsory parameter and one optional. - * - * @URL("/test") - * @param string $var1 - * @param string $var2 - */ -public function my_action($var1, $var2 = 42) { ... } -``` - -In this sample, if the user does not pass the "var2" parameter in the URL, it will be equal to 42. -The URL might be: `http://[server-url]/[webapp-path]/test?var1=param1` - - -Injecting PSR-7 Request object as a parameter ---------------------------------------------- - -Splash 7+ has native support for PSR-7 `RequestInterface` and for `ServerRequestInterface` objects. - -This means that Splash will automatically inject a `ServerRequest` object into your action if your action expects a `RequestInterface` or a `ServerRequestInterface` object: - -```php -use Psr\Http\Message\ServerRequestInterface; -... - -/** - * My action with the request object filled - * - * @URL("/test") - * @param ServerRequestInterface $request - */ -public function my_action(ServerRequestInterface $request) { - $param = $request->getQueryParams()['param']; - ... -} -``` - -Note: you should use the `ServerRequest` object instead of accessing directly `$_FILES`, `$_SERVER`, `$_COOKIES`, or HTTP headers. - - -Advanced behaviour ------------------- - -You are a power user? Splash has a number of extension points including one that let's you define how parameters are injected in the action. - -You can add your own plugins to automatically fill some parameters of the request (we call those `ParameterFetchers`). - -You could write a parameter fetcher to: - -- automatically plug your ORM to Splash (by injecting entities directly into the action) -- or inject a session -- or inject services (à la Angular) -- or really... whatever you might imagine! - -### Writing a parameter fetcher - -To write a parameter fetcher, you need to implement the `ParameterFetcher` interface: - -```php -/** - * Classes implementing this interface can create parameter fetchers that will fill parameters of actions. - */ -interface ParameterFetcher -{ - /** - * Returns whether this fetcher factory can handle the parameter passed in parameter for the url $url. - * - * @param ReflectionParameter $reflectionParameter - * @param string $url - * - * @return bool - */ - public function canHandle(ReflectionParameter $reflectionParameter, string $url = null) : bool; - - /** - * Returns some data needed by this fetcher to fetch data from the request. - * This data MUST be serializable (and will be serialized). This function will be called only once - * and data cached. You can perform expensive computation in this function. - * - * @param ReflectionParameter $reflectionParameter - * @param string|null $url - * - * @return mixed - */ - public function getFetcherData(ReflectionParameter $reflectionParameter, string $url = null); - - /** - * Returns the value to be injected in this parameter. - * - * @param mixed $data The data generated by "getFetcherData" - * @param SplashRequestContext $context - * - * @return mixed - */ - public function fetchValue($data, SplashRequestContext $context); -} -``` - -Notice how there are 3 methods: - -- `canHandle` is called by Splash on each parameter. The parameter fetcher must return `true` is it can handle this parameter, or `false` if it cannot. -- If `canHandle` returns `true`, `getFetcherData` is called. This method can return any **serializable** data that will be used to analyze the parameter. This method is called only once per parameter. The result is put in cache. You can perform expensive computation in this function. -- `fetchValue` is where the real work happens. For each request and each parameter, the `fetchValue` method is called. It must return the value that is injected into the parameter. The `fetchValue` method receives the serialized data that was computed by the `getFetcherData` method along the request, bundled in the `SplashRequestContext $context` object. - diff --git a/doc/writing_controllers_manually.md b/doc/writing_controllers_manually.md deleted file mode 100755 index 820e146..0000000 --- a/doc/writing_controllers_manually.md +++ /dev/null @@ -1,329 +0,0 @@ -Writing controllers -=================== - -In this document, we will see how to create a controller. -You will also learn more about what makes a controller. - -Note: if you are using *Mouf*, Splash comes with a [controller creation wizard (a nice user interface)](writing_controllers.md). - -What is a controller? ---------------------- - -In Splash, a controller is a class that contains a number of _Actions_. -_Actions_ are methods that can be directly accessed from the browser. - -There are several ways to declare a method to be an action. The most common ways are: - - The *@URL* annotation - - The *@Action* annotation - - -The @URL annotation -------------------- - -This is the preferred way of declaring an action: - -```php -"; - $str .= ""; - $str .= "var1 value is ".htmlentities($var1)." and var2 value is ".htmlentities($var2); - $str .= ""; - return new HtmlResponse($str); - } -} -``` - -Note: this class must be auto-loadable by Composer. Be sure to put the class in the correct repository according to your *composer.json* `autoload` section. - - -The *@URL* annotation points to the web path the action is bound to. - -The action takes 2 parameters: `$var1` and `$var2`. This means that the page needs both parameters passed -either in GET or POST. - -In order to test this, we must first create an instance of the controller in your container. -How you do this really depends on the container you are using. See the [installation section for more information](install/index.md). - -Finally, we must also register this controller in Splash. How this is done essentially depends on your Splash installation. - - - if you are using Mouf, you have nothing to do. Splash will automatically detect your controllers. - - if you are using Splash service provider, you need to put in your container an entry named 'thecodingmachine.splash.controllers' that is an array of controller identifiers. - The way to do this depends on the container you are using, but might be something similar to: - - ```php - $container->set('thecodingmachine.splash.controllers', [ - 'myController', - 'myOtherController', - ]); - ``` - -Now, let's test our code. -By browsing to `http://localhost/{my_app}/path/to/my/action?var1=42&var2=24`, we should see the message displayed! - -*Troubleshooting:* If for some reason, your controller is not detected, you can try to purge your cache. - -Done? Then let's move on! - -The @Get / @Post annotations ----------------------------- - -We might decide that an action should always be called via GET, or via POST (or PUT or DELETE if you want to provide REST services). -Splash makes that very easy to handle. You can just add a @Get or @Post annotation (or @Put or @Delete). Here is a sample: - -```php - -``` - -Do you see the @URL annotation? The {id} part is a placeholder that will be replaced by any value found in the URL. -So for instance, if you access http://[server]/[appname]/user/42/view, the $id parameter will be filled with "42". - -Returning / outputting values ------------------------------ - -As you probably already guessed, you must return a [PSR-7 Response object](http://www.php-fig.org/psr/psr-7/#3-3-psr-http-message-responseinterface). -Splash comes bundled with [Zend Diactoros](https://github.com/zendframework/zend-diactoros) - -Therefore, you can write things like: - -```php - 'text/html')); - } - - /** - * Returning a JSON response - * - * @URL("/myjsonurl") - */ - public function testJson() { - return new JsonResponse({ "status" => "ok", "message" => "Hello world!" }); - } -} -?> -``` - -Typically, in Mouf, you will want to output a template object. You can easily output templates (or any object -implementing the [`HtmlElementInterface`](http://mouf-php.com/packages/mouf/html.htmlelement/README.md) using the `HtmlResponse` object: - -```php -template); - } -} -``` - -Uploading files ---------------- - -Uploaded files are also directly available from the signature of the method: - -**HTML:** -```html - -``` - -**PHP**: -```php -moveTo(__DIR__.'/uploads/logo.png'); - ... - } -} -``` - -The `$logo` object injected implements the [PSR-7 `UploadedFileInterface`](http://www.php-fig.org/psr/psr-7/#3-6-psr-http-message-uploadedfileinterface) - -The @Action annotation ----------------------- - -The @Action parameter can replace the @URL parameter. -You simply put a @Action annotation in your method. The URLs to access a @Action method are always: - - http://[server-url]/[webapp-path]/[controller-instance-name]/[action-name] - -Here is a sample: - -```php - -``` - -The *my_action* method is a Splash action. You know this because there is a @Action annotation in the PHPDoc comment of the method. - -Now, we can access the example page using this URL: - - http://[server-url]/[webapp-path]/my_controller/my_action?var1=42&var2=toto - -Default actions ---------------- - -Sometimes, when using @Action annotations, we might want to have a URL that is a bit shorter than /my_webapp/my_controller/my_action. -Splash supports a special method called "index". If no action is provided in the URL, the index method will be called instead. - -```php - -``` - -The test page can be accessed using the URL: - - http://[server-url]/[webapp-path]/my_controller/. - -[Wanna learn more? Have a look at the Mouf controller cration wizard](mouf/writing_controllers.md) diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..a40d2df --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +#parameters: +# ignoreErrors: +# - "#Instantiated class WeakRef not found.#" +#includes: + # - vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon \ No newline at end of file diff --git a/src/Mouf/Mvc/Splash/Annotations/Action.php b/src/Mouf/Mvc/Splash/Annotations/Action.php deleted file mode 100644 index eb2e68d..0000000 --- a/src/Mouf/Mvc/Splash/Annotations/Action.php +++ /dev/null @@ -1,12 +0,0 @@ -title = $values['value']; - } - - /** - * @return string - */ - public function getTitle() - { - return $this->title; - } -} diff --git a/src/Mouf/Mvc/Splash/Annotations/URL.php b/src/Mouf/Mvc/Splash/Annotations/URL.php deleted file mode 100644 index f006900..0000000 --- a/src/Mouf/Mvc/Splash/Annotations/URL.php +++ /dev/null @@ -1,31 +0,0 @@ -url = $values['value']; - } - - /** - * @return string - */ - public function getUrl() - { - return $this->url; - } -} diff --git a/src/Mouf/Mvc/Splash/Controllers/Controller.php b/src/Mouf/Mvc/Splash/Controllers/Controller.php index 465b0d2..8ebd122 100644 --- a/src/Mouf/Mvc/Splash/Controllers/Controller.php +++ b/src/Mouf/Mvc/Splash/Controllers/Controller.php @@ -12,7 +12,7 @@ abstract class Controller implements Scopable /** * Inludes the file (useful to load a view inside the Controllers scope). * - * @param unknown_type $file + * @param string $file */ public function loadFile($file) { diff --git a/src/Mouf/Mvc/Splash/Controllers/Http400HandlerInterface.php b/src/Mouf/Mvc/Splash/Controllers/Http400HandlerInterface.php deleted file mode 100644 index ebd50c4..0000000 --- a/src/Mouf/Mvc/Splash/Controllers/Http400HandlerInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - [self::class, 'createDefaultRouter'], - 'thecodingmachine.splash.route-providers' => [self::class, 'createRouteProviders'], - ControllerRegistry::class => [self::class, 'createControllerRegistry'], - ControllerAnalyzer::class => [self::class, 'createControllerAnalyzer'], - ParameterFetcherRegistry::class => [self::class, 'createParameterFetcherRegistry'], - 'thecodingmachine.splash.parameter-fetchers' => [self::class, 'createParameterFetchers'], - SplashRequestFetcher::class => [self::class, 'createSplashRequestFetcher'], - SplashRequestParameterFetcher::class => [self::class, 'createSplashRequestParameterFetcher'], - 'thecodingmachine.splash.mode' => new Parameter(SplashUtils::MODE_STRICT), - 'thecodingmachine.splash.controllers' => new Parameter([]) - ]; - } - - public function getExtensions() - { - return [ - MiddlewareListServiceProvider::MIDDLEWARES_QUEUE => [self::class, 'updatePriorityQueue'], - ]; - } - - public static function createDefaultRouter(ContainerInterface $container) : SplashDefaultRouter - { - if ($container->has(CacheItemPoolInterface::class)) { - $cache = $container->get(CacheItemPoolInterface::class); - } else { - $cache = null; - } - - if ($container->has(LoggerInterface::class)) { - $logger = $container->get(LoggerInterface::class); - } else { - $logger = null; - } - - $routeProviders = $container->get('thecodingmachine.splash.route-providers'); - - $router = new SplashDefaultRouter($container, $routeProviders, $container->get(ParameterFetcherRegistry::class), $cache, $logger, SplashUtils::MODE_STRICT, true, self::getRootUrl($container)); - - return $router; - } - - private static function getRootUrl(ContainerInterface $container) - { - if ($container->has('thecodingmachine.splash.root_url')) { - return $container->get('thecodingmachine.splash.root_url'); - } elseif ($container->has('root_url')) { - return $container->get('root_url'); - } else { - return '/'; - } - } - - public static function createRouteProviders(ContainerInterface $container) : array - { - return [ - $container->get(ControllerRegistry::class), - ]; - } - - public static function createControllerRegistry(ContainerInterface $container) : ControllerRegistry - { - return new ControllerRegistry($container->get(ControllerAnalyzer::class), - $container->get('thecodingmachine.splash.controllers')); - } - - public static function createControllerAnalyzer(ContainerInterface $container) : ControllerAnalyzer - { - return new ControllerAnalyzer($container, $container->get(ParameterFetcherRegistry::class), - $container->get(Reader::class)); - } - - public static function createParameterFetcherRegistry(ContainerInterface $container) : ParameterFetcherRegistry - { - return new ParameterFetcherRegistry($container->get('thecodingmachine.splash.parameter-fetchers')); - } - - public static function createParameterFetchers(ContainerInterface $container) : array - { - return [ - $container->get(SplashRequestFetcher::class), - $container->get(SplashRequestParameterFetcher::class), - ]; - } - - public static function createSplashRequestFetcher() : SplashRequestFetcher - { - return new SplashRequestFetcher(); - } - - public static function createSplashRequestParameterFetcher() : SplashRequestParameterFetcher - { - return new SplashRequestParameterFetcher(); - } - - public static function updatePriorityQueue(ContainerInterface $container, \SplPriorityQueue $priorityQueue) : \SplPriorityQueue - { - $priorityQueue->insert($container->get(SplashDefaultRouter::class), MiddlewareOrder::ROUTER); - return $priorityQueue; - } -} diff --git a/src/Mouf/Mvc/Splash/Exception/BadRequestException.php b/src/Mouf/Mvc/Splash/Exception/BadRequestException.php deleted file mode 100644 index f37c760..0000000 --- a/src/Mouf/Mvc/Splash/Exception/BadRequestException.php +++ /dev/null @@ -1,11 +0,0 @@ -url = $url; - - return $exception; - } - - /** - * @return string - */ - public function getUrl() : string - { - return $this->url; - } -} diff --git a/src/Mouf/Mvc/Splash/Exception/SplashMissingParameterException.php b/src/Mouf/Mvc/Splash/Exception/SplashMissingParameterException.php deleted file mode 100644 index f47681b..0000000 --- a/src/Mouf/Mvc/Splash/Exception/SplashMissingParameterException.php +++ /dev/null @@ -1,21 +0,0 @@ -key = $key; - - return $exception; - } -} diff --git a/src/Mouf/Mvc/Splash/Filters/RedirectToHttpAnnotation.php b/src/Mouf/Mvc/Splash/Filters/RedirectToHttpAnnotation.php deleted file mode 100644 index 8319e01..0000000 --- a/src/Mouf/Mvc/Splash/Filters/RedirectToHttpAnnotation.php +++ /dev/null @@ -1,45 +0,0 @@ -port = $values['port'] ?? 80; - } - - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next, ContainerInterface $container) - { - $uri = $request->getUri(); - $scheme = $uri->getScheme(); - if ($scheme === 'https') { - if ($request->getMethod() !== 'GET') { - throw new SplashException('Only GET HTTP methods can be redirected to HTTP'); - } - $uri = $uri->withScheme('http')->withPort($this->port); - - return new RedirectResponse($uri); - } - - return $next($request, $response); - } -} diff --git a/src/Mouf/Mvc/Splash/Filters/RequireHttpsAnnotation.php b/src/Mouf/Mvc/Splash/Filters/RequireHttpsAnnotation.php deleted file mode 100644 index b967ad5..0000000 --- a/src/Mouf/Mvc/Splash/Filters/RequireHttpsAnnotation.php +++ /dev/null @@ -1,53 +0,0 @@ -value = 'force'; - } elseif (strpos($value, 'no') !== false) { - $this->value = 'no'; - } elseif (strpos($value, 'redirect') !== false) { - $this->value = 'redirect'; - } - - if ($this->value === null) { - throw new SplashException('You need to specify a value (either "force", "no" or "redirect") to the @RequireHttpsAnnotation.'); - } - } - - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next, ContainerInterface $container) - { - $uri = $request->getUri(); - $scheme = $uri->getScheme(); - if ($scheme === 'http') { - if ($request->getMethod() !== 'GET') { - throw new SplashException('Only GET HTTP methods can be redirected to HTTPS'); - } - $uri = $uri->withScheme('https'); - - return new RedirectResponse($uri); - } - - return $next($request, $response); - } -} diff --git a/src/Mouf/Mvc/Splash/HtmlResponse.php b/src/Mouf/Mvc/Splash/HtmlResponse.php index 279a665..ead937d 100644 --- a/src/Mouf/Mvc/Splash/HtmlResponse.php +++ b/src/Mouf/Mvc/Splash/HtmlResponse.php @@ -3,11 +3,12 @@ namespace Mouf\Mvc\Splash; use Mouf\Html\HtmlElement\HtmlElementInterface; +use Psr\Http\Message\StreamInterface; use Zend\Diactoros\Response; use Zend\Diactoros\Stream; /** - * This class is a PSR-6 response that takes in parameter a HtmlElementInterface element and will render it. + * This class is a PSR-7 response that takes in parameter a HtmlElementInterface element and will render it. * * @author David Négrier */ diff --git a/src/Mouf/Mvc/Splash/Routers/CacheRouter.php b/src/Mouf/Mvc/Splash/Routers/CacheRouter.php deleted file mode 100644 index 664a994..0000000 --- a/src/Mouf/Mvc/Splash/Routers/CacheRouter.php +++ /dev/null @@ -1,113 +0,0 @@ -cache = $cache; - $this->cacheCondition = $cacheCondition; - $this->log = $log; - } - - /** - * Process an incoming server request and return a response, optionally delegating - * to the next middleware component to create the response. - * - * @param ServerRequestInterface $request - * @param DelegateInterface $delegate - * - * @return ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - $requestMethod = $request->getMethod(); - $key = str_replace(['\\', '/', ':', '*', '?', '"', '<', '>', '|'], '_', $request->getUri()->getPath().'?'.$request->getUri()->getQuery()); - - if ($this->cacheCondition->isOk() && $requestMethod == 'GET') { - $cacheResponse = $this->cache->get($key); - if ($cacheResponse) { - $this->log->debug("Cache HIT on $key"); - - return $cacheResponse; - } else { - $this->log->debug("Cache MISS on key $key"); - $response = $delegate->process($request); - - $noCache = false; - if ($response->hasHeader('Mouf-Cache-Control') && $response->getHeader('Mouf-Cache-Control')[0] == 'no-cache') { - $noCache = true; - } - - if ($noCache) { - $this->log->debug("Mouf NO CACHE header found, not storing '$key'"); - } else { - $ttl = null; - - // TODO: continue here! - // Use PSR-7 response to analyze maxage and expires... - // ...or... use a completely different HTTP cache implementation!!! - // There must be one around for PSR-7! - - $maxAge = $response->getMaxAge(); - $expires = $response->getExpires(); - if ($maxAge) { - $this->log->debug("MaxAge specified : $maxAge"); - $ttl = $maxAge; - } elseif ($expires) { - $this->log->debug("Expires specified : $expires"); - $ttl = date_diff($expires, new \DateTime())->s; - } - - if ($ttl) { - $this->log->debug("TTL is : $ttl"); - } - - // Make sure the response is serializable - $serializableResponse = new Response(); - $serializableResponse->headers = $response->headers; - - ob_start(); - $response->sendContent(); - $content = ob_get_clean(); - - $serializableResponse->setContent($content); - - $this->cache->set($key, $serializableResponse, $ttl); - $this->log->debug("Cache STORED on key $key"); - $response = $serializableResponse; - } - - return $response; - } - } else { - $this->log->debug("No cache for $key"); - - return $this->fallBackRouter->handle($request, $type, $catch); - } - } -} diff --git a/src/Mouf/Mvc/Splash/Routers/ExceptionRouter.php b/src/Mouf/Mvc/Splash/Routers/ExceptionRouter.php index 45bebc2..2b0f630 100644 --- a/src/Mouf/Mvc/Splash/Routers/ExceptionRouter.php +++ b/src/Mouf/Mvc/Splash/Routers/ExceptionRouter.php @@ -2,14 +2,14 @@ namespace Mouf\Mvc\Splash\Routers; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; -use Mouf\Mvc\Splash\Controllers\Http500HandlerInterface; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\LoggerInterface; -use Mouf\Mvc\Splash\Services\SplashUtils; +use TheCodingMachine\Splash\Services\SplashUtils; +use TheCodingMachine\Splash\Controllers\Http500HandlerInterface; /** * This router transforms exceptions into HTTP 500 pages, based on the configured error controller. @@ -77,14 +77,14 @@ function () use ($t, $request) { * to the next middleware component to create the response. * * @param Request $request - * @param DelegateInterface $delegate + * @param RequestHandlerInterface $delegate * * @return Response */ - public function process(Request $request, DelegateInterface $delegate) + public function process(Request $request, RequestHandlerInterface $delegate): ResponseInterface { try { - return $delegate->process($request); + return $delegate->handle($request); } catch (\Throwable $t) { return $this->handleException($t, $request); } diff --git a/src/Mouf/Mvc/Splash/Routers/NotFoundRouter.php b/src/Mouf/Mvc/Splash/Routers/NotFoundRouter.php index 680695a..d6552ff 100644 --- a/src/Mouf/Mvc/Splash/Routers/NotFoundRouter.php +++ b/src/Mouf/Mvc/Splash/Routers/NotFoundRouter.php @@ -2,13 +2,14 @@ namespace Mouf\Mvc\Splash\Routers; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; -use Mouf\Mvc\Splash\Controllers\Http404HandlerInterface; +use TheCodingMachine\Splash\Controllers\Http404HandlerInterface; use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\LoggerInterface; -use Mouf\Mvc\Splash\Services\SplashUtils; +use TheCodingMachine\Splash\Services\SplashUtils; /** * This router always returns a 404 page, based on the configured page not found controller. @@ -41,11 +42,11 @@ public function __construct(Http404HandlerInterface $pageNotFoundController, Log * to the next middleware component to create the response. * * @param Request $request - * @param DelegateInterface $delegate * - * @return Response + * @param RequestHandlerInterface $handler + * @return ResponseInterface */ - public function process(Request $request, DelegateInterface $delegate) + public function process(Request $request, RequestHandlerInterface $handler): ResponseInterface { if ($this->log) { $this->log->info('404 - Page not found on URL: '.$request->getUri()->getPath()); diff --git a/src/Mouf/Mvc/Splash/Routers/PhpVarsCheckRouter.php b/src/Mouf/Mvc/Splash/Routers/PhpVarsCheckRouter.php index 2006f0b..46092d3 100644 --- a/src/Mouf/Mvc/Splash/Routers/PhpVarsCheckRouter.php +++ b/src/Mouf/Mvc/Splash/Routers/PhpVarsCheckRouter.php @@ -2,11 +2,12 @@ namespace Mouf\Mvc\Splash\Routers; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface; -use Mouf\Mvc\Splash\Utils\SplashException; +use TheCodingMachine\Splash\Utils\SplashException; use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\LoggerInterface; /** @@ -19,12 +20,6 @@ */ class PhpVarsCheckRouter implements MiddlewareInterface { - /** - * The logger used by Splash. - * - * @var LoggerInterface - */ - private $log; /** * A simple counter to check requests' length (GET, POST, REQUEST). @@ -33,16 +28,6 @@ class PhpVarsCheckRouter implements MiddlewareInterface */ private $count; - /** - * @Important - * - * @param LoggerInterface $log The logger used by Splash - */ - public function __construct(LoggerInterface $log = null) - { - $this->log = $log; - } - /** * Get the min in 2 values if there exist. * @@ -78,7 +63,7 @@ private static function iniGetBytes($val) $val = trim(ini_get($val)); if ($val != '') { $last = strtolower( - $val{strlen($val) - 1} + $val{strlen($val) - 1} ); } else { $last = ''; @@ -113,12 +98,12 @@ private function countRecursive($item, $key) * to the next middleware component to create the response. * * @param Request $request - * @param DelegateInterface $delegate * - * @return Response - * @throws \Mouf\Mvc\Splash\Utils\SplashException + * @param RequestHandlerInterface $handler + * @return ResponseInterface + * @throws SplashException */ - public function process(Request $request, DelegateInterface $delegate) + public function process(Request $request, RequestHandlerInterface $handler): ResponseInterface { // Check if there is a limit of input number in php // Throw exception if the limit is reached @@ -127,10 +112,7 @@ public function process(Request $request, DelegateInterface $delegate) if ($maxGet !== null) { $this->count = 0; array_walk_recursive($_GET, array($this, 'countRecursive')); - if ($this->count >= $maxGet) { - if ($this->log !== null) { - $this->log->error('Max input vars reaches for get parameters ({maxGet}). Check your variable max_input_vars in php.ini or suhosin module suhosin.get.max_vars.', ['maxGet' => $maxGet]); - } + if ($this->count === $maxGet) { throw new SplashException('Max input vars reaches for get parameters ('.$maxGet.'). Check your variable max_input_vars in php.ini or suhosin module suhosin.get.max_vars.'); } } @@ -140,10 +122,7 @@ public function process(Request $request, DelegateInterface $delegate) if ($maxPost !== null) { $this->count = 0; array_walk_recursive($_POST, array($this, 'countRecursive')); - if ($this->count >= $maxPost) { - if ($this->log !== null) { - $this->log->error('Max input vars reaches for post parameters ({maxPost}). Check your variable max_input_vars in php.ini or suhosin module suhosin.post.max_vars.', ['maxPost' => $maxPost]); - } + if ($this->count === $maxPost) { throw new SplashException('Max input vars reaches for post parameters ('.$maxPost.'). Check your variable max_input_vars in php.ini or suhosin module suhosin.post.max_vars.'); } } @@ -153,10 +132,7 @@ public function process(Request $request, DelegateInterface $delegate) if ($maxRequest !== null) { $this->count = 0; array_walk_recursive($_REQUEST, array($this, 'countRecursive')); - if ($this->count >= $maxRequest) { - if ($this->log !== null) { - $this->log->error('Max input vars reaches for request parameters ({maxRequest}). Check your variable max_input_vars in php.ini or suhosin module suhosin.request.max_vars.', ['maxRequest' => $maxRequest]); - } + if ($this->count === $maxRequest) { throw new SplashException('Max input vars reaches for request parameters ('.$maxRequest.'). Check your variable max_input_vars in php.ini or suhosin module suhosin.request.max_vars.'); } } @@ -164,9 +140,6 @@ public function process(Request $request, DelegateInterface $delegate) if (isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) == 'post' && empty($_POST) && empty($_FILES)) { $maxPostSize = self::iniGetBytes('post_max_size'); if ($_SERVER['CONTENT_LENGTH'] > $maxPostSize) { - if ($this->log !== null) { - $this->log->error('Max post size exceeded! Got {length} bytes, but limit is {maxPostSize} bytes. Edit post_max_size setting in your php.ini.', ['length' => $_SERVER['CONTENT_LENGTH'], 'maxPostSize' => $maxPostSize]); - } throw new SplashException( sprintf('Max post size exceeded! Got %s bytes, but limit is %s bytes. Edit post_max_size setting in your php.ini.', $_SERVER['CONTENT_LENGTH'], @@ -177,6 +150,6 @@ public function process(Request $request, DelegateInterface $delegate) } //If no Exception has been thrown, call next router - return $delegate->process($request); + return $handler->handle($request); } } diff --git a/src/Mouf/Mvc/Splash/Routers/SplashDefaultRouter.php b/src/Mouf/Mvc/Splash/Routers/SplashDefaultRouter.php deleted file mode 100644 index b5b9870..0000000 --- a/src/Mouf/Mvc/Splash/Routers/SplashDefaultRouter.php +++ /dev/null @@ -1,425 +0,0 @@ -container = $container; - $this->routeProviders = $routeProviders; - $this->parameterFetcherRegistry = $parameterFetcherRegistry; - $this->cachePool = $cachePool === null ? new VoidCachePool() : $cachePool; - $this->log = $log === null ? new NullLogger() : $log; - $this->mode = $mode; - $this->debug = $debug; - $this->rootUrl = rtrim($rootUrl, '/').'/'; - } - - /** - * @param Http400HandlerInterface $http400Handler - */ - public function setHttp400Handler($http400Handler) - { - $this->http400Handler = $http400Handler; - - return $this; - } - - /** - * @param Http404HandlerInterface $http404Handler - */ - public function setHttp404Handler($http404Handler) - { - $this->http404Handler = $http404Handler; - - return $this; - } - - /** - * @param Http500HandlerInterface $http500Handler - */ - public function setHttp500Handler($http500Handler) - { - $this->http500Handler = $http500Handler; - - return $this; - } - - /** - * Process an incoming request and/or response. - * - * Accepts a server-side request and a response instance, and does - * something with them. - * - * If the response is not complete and/or further processing would not - * interfere with the work done in the middleware, or if the middleware - * wants to delegate to another process, it can use the `$out` callable - * if present. - * - * If the middleware does not return a value, execution of the current - * request is considered complete, and the response instance provided will - * be considered the response to return. - * - * Alternately, the middleware may return a response instance. - * - * Often, middleware will `return $out();`, with the assumption that a - * later middleware will return a response. - * - * @param ServerRequestInterface $request - * @param ResponseInterface $response - * @param null|callable $out - * - * @return null|ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $out = null) - { - try { - return $this->route($request, $response, $out); - } catch (BadRequestException $e) { - if ($this->http400Handler !== null) { - return $this->http400Handler->badRequest($e, $request); - } else { - throw $e; - } - } catch (PageNotFoundException $e) { - if ($this->http404Handler !== null) { - return $this->http404Handler->pageNotFound($request); - } else { - throw $e; - } - } catch (\Throwable $t) { - if ($this->http500Handler !== null) { - return $this->http500Handler->serverError($t, $request); - } else { - throw $t; - } - } - } - - /** - * @param ServerRequestInterface $request - * @param ResponseInterface $response - * @param callable|null $out - * - * @return ResponseInterface - */ - private function route(ServerRequestInterface $request, ResponseInterface $response, callable $out = null, $retry = false) : ResponseInterface - { - $this->purgeExpiredRoutes(); - - $urlNodesCacheItem = $this->cachePool->getItem('splashUrlNodes'); - if (!$urlNodesCacheItem->isHit()) { - // No value in cache, let's get the URL nodes - $urlsList = $this->getSplashActionsList(); - $urlNodes = $this->generateUrlNode($urlsList); - $urlNodesCacheItem->set($urlNodes); - $this->cachePool->save($urlNodesCacheItem); - } else { - $urlNodes = $urlNodesCacheItem->get(); - } - - /* @var $urlNodes SplashUrlNode */ - - $request_path = $request->getUri()->getPath(); - - $pos = strpos($request_path, $this->rootUrl); - if ($pos === false) { - throw new SplashException('Error: the prefix of the web application "'.$this->rootUrl.'" was not found in the URL. The application must be misconfigured. Check the ROOT_URL parameter in your config.php file at the root of your project. It should have the same value as the RewriteBase parameter in your .htaccess file. Requested URL : "'.$request_path.'"'); - } - - $tailing_url = substr($request_path, $pos + strlen($this->rootUrl)); - $tailing_url = urldecode($tailing_url); - $splashRoute = $urlNodes->walk($tailing_url, $request); - - if ($splashRoute === null) { - // No route found. Let's try variants with or without trailing / if we are in a GET. - if ($request->getMethod() === 'GET') { - // If there is a trailing /, let's remove it and retry - if (strrpos($tailing_url, '/') === strlen($tailing_url) - 1) { - $url = substr($tailing_url, 0, -1); - $splashRoute = $urlNodes->walk($url, $request); - } else { - $url = $tailing_url.'/'; - $splashRoute = $urlNodes->walk($url, $request); - } - - if ($splashRoute !== null) { - // If a route does match, let's make a redirect. - return new RedirectResponse($this->rootUrl.$url); - } - } - - if ($this->debug === false || $retry === true) { - // No route found, let's pass control to the next middleware. - if ($out !== null) { - return $out($request, $response); - } else { - $this->log->debug('Found no route for URL {url}.', [ - 'url' => $request_path, - ]); - throw PageNotFoundException::create($tailing_url); - } - } else { - // We have a 404, but we are in debug mode and have not retried yet... - // Let's purge the cache and retry! - $this->purgeUrlsCache(); - - return $this->route($request, $response, $out, true); - } - } - - // Is the route still valid according to the cache? - if (!$splashRoute->isCacheValid()) { - // The route is invalid! Let's purge the cache and retry! - $this->purgeUrlsCache(); - - return $this($request, $response, $out); - } - - $controller = $this->container->get($splashRoute->getControllerInstanceName()); - $action = $splashRoute->getMethodName(); - - $this->log->debug('Routing URL {url} to controller instance {controller} and action {action}', [ - 'url' => $request_path, - 'controller' => $splashRoute->getControllerInstanceName(), - 'action' => $action, - ]); - - $filters = $splashRoute->getFilters(); - - $middlewareCaller = function (ServerRequestInterface $request, ResponseInterface $response) use ($controller, $action, $splashRoute) { - // Let's recreate a new context object (because request can be modified by the filters) - $context = new SplashRequestContext($request); - $context->setUrlParameters($splashRoute->getFilledParameters()); - // Let's pass everything to the controller: - $args = $this->parameterFetcherRegistry->toArguments($context, $splashRoute->getParameters()); - - try { - $response = SplashUtils::buildControllerResponse( - function () use ($controller, $action, $args) { - return $controller->$action(...$args); - }, - $this->mode, - $this->debug - ); - } catch (SplashException $e) { - throw new SplashException($e->getMessage(). ' (in '.$splashRoute->getControllerInstanceName().'->'.$splashRoute->getMethodName().')', $e->getCode(), $e); - } - return $response; - }; - - // Apply filters - for ($i = count($filters) - 1; $i >= 0; --$i) { - $filter = $filters[$i]; - $middlewareCaller = function (ServerRequestInterface $request, ResponseInterface $response) use ($middlewareCaller, $filter) { - return $filter($request, $response, $middlewareCaller, $this->container); - }; - } - - $response = $middlewareCaller($request, $response); - - return $response; - } - - /** - * Purges the cache if one of the url providers tells us to. - */ - private function purgeExpiredRoutes() - { - $expireTag = ''; - foreach ($this->routeProviders as $routeProvider) { - /* @var $routeProvider UrlProviderInterface */ - $expireTag .= $routeProvider->getExpirationTag(); - } - - $value = md5($expireTag); - - $urlNodesCacheItem = $this->cachePool->getItem('splashExpireTag'); - - if ($urlNodesCacheItem->isHit() && $urlNodesCacheItem->get() === $value) { - return; - } - - $this->purgeUrlsCache(); - - $urlNodesCacheItem->set($value); - $this->cachePool->save($urlNodesCacheItem); - } - - /** - * Returns the list of all SplashActions. - * This call is LONG and should be cached. - * - * @return array - */ - public function getSplashActionsList() - { - $urls = array(); - - foreach ($this->routeProviders as $routeProvider) { - /* @var $routeProvider UrlProviderInterface */ - $tmpUrlList = $routeProvider->getUrlsList(null); - $urls = array_merge($urls, $tmpUrlList); - } - - return $urls; - } - - /** - * Generates the URLNodes from the list of URLS. - * URLNodes are a very efficient way to know whether we can access our page or not. - * - * @param array $urlsList - * - * @return SplashUrlNode - */ - private function generateUrlNode($urlsList) - { - $urlNode = new SplashUrlNode(); - foreach ($urlsList as $splashAction) { - $urlNode->registerCallback($splashAction); - } - - return $urlNode; - } - - /** - * Purges the urls cache. - */ - public function purgeUrlsCache() - { - $this->cachePool->deleteItem('splashUrlNodes'); - } - - /** - * Process an incoming server request and return a response, optionally delegating - * to the next middleware component to create the response. - * - * @param ServerRequestInterface $request - * @param DelegateInterface $delegate - * - * @return ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - // create a dummy response to keep compatibility with old middlewares. - $response = new Response(); - - return $this($request, $response, function($request) use ($delegate) { - return $delegate->process($request); - }); - } -} diff --git a/src/Mouf/Mvc/Splash/Services/ControllerAnalyzer.php b/src/Mouf/Mvc/Splash/Services/ControllerAnalyzer.php deleted file mode 100644 index 45eb400..0000000 --- a/src/Mouf/Mvc/Splash/Services/ControllerAnalyzer.php +++ /dev/null @@ -1,196 +0,0 @@ -container = $container; - $this->parameterFetcherRegistry = $parameterFetcherRegistry; - $this->annotationReader = $annotationReader; - } - - /** - * Returns is a given class name is a controller or not (whether it contains \@Action or \@URL annotations). - * - * @param string $className - * - * @return bool - */ - public function isController(string $className) : bool - { - $refClass = new \ReflectionClass($className); - - - foreach ($refClass->getMethods() as $refMethod) { - $actionAnnotation = $this->annotationReader->getMethodAnnotation($refMethod, Action::class); - if ($actionAnnotation) { - return true; - } - $urlAnnotation = $this->annotationReader->getMethodAnnotation($refMethod, URL::class); - if ($urlAnnotation) { - return true; - } - } - - return false; - } - - /** - * Returns an array of SplashRoute for the controller passed in parameter. - * - * @param object $controller - * - * @return SplashRoute[] - * - * @throws \Mouf\Mvc\Splash\Utils\SplashException - */ - public function analyzeController(string $controllerInstanceName) : array - { - // Let's analyze the controller and get all the @Action annotations: - $urlsList = array(); - - $controller = $this->container->get($controllerInstanceName); - - $refClass = new \ReflectionClass($controller); - - foreach ($refClass->getMethods() as $refMethod) { - $title = null; - // Now, let's check the "Title" annotation (note: we do not support multiple title annotations for the same method) - $titleAnnotation = $this->annotationReader->getMethodAnnotation($refMethod, Title::class); - if ($titleAnnotation !== null) { - /* @var $titleAnnotation TitleAnnotation */ - $title = $titleAnnotation->getTitle(); - } - - // First, let's check the "Action" annotation - $actionAnnotation = $this->annotationReader->getMethodAnnotation($refMethod, Action::class); - if ($actionAnnotation !== null) { - $methodName = $refMethod->getName(); - if ($methodName === 'index') { - $url = $controllerInstanceName.'/'; - } else { - $url = $controllerInstanceName.'/'.$methodName; - } - $parameters = $this->parameterFetcherRegistry->mapParameters($refMethod); - $filters = FilterUtils::getFilters($refMethod, $this->annotationReader); - $urlsList[] = new SplashRoute($url, $controllerInstanceName, $refMethod->getName(), $title, $refMethod->getDocComment(), $this->getSupportedHttpMethods($refMethod), $parameters, $filters, $refClass->getFileName()); - } - - // Now, let's check the "URL" annotation (note: we support multiple URL annotations for the same method) - $annotations = $this->annotationReader->getMethodAnnotations($refMethod); - - foreach ($annotations as $annotation) { - if (!$annotation instanceof URL) { - continue; - } - - /* @var $annotation URL */ - $url = $annotation->getUrl(); - - // Get public properties if they exist in the URL - if (preg_match_all('/[^{]*{\$this->([^\/]*)}[^{]*/', $url, $output)) { - foreach ($output[1] as $param) { - $value = $this->readPrivateProperty($controller, $param); - $url = str_replace('{$this->'.$param.'}', $value, $url); - } - } - - $url = ltrim($url, '/'); - $parameters = $this->parameterFetcherRegistry->mapParameters($refMethod, $url); - $filters = FilterUtils::getFilters($refMethod, $this->annotationReader); - $urlsList[] = new SplashRoute($url, $controllerInstanceName, $refMethod->getName(), $title, $refMethod->getDocComment(), $this->getSupportedHttpMethods($refMethod), $parameters, $filters, $refClass->getFileName()); - } - } - - return $urlsList; - } - - /** - * Reads a private property value. - * Credit to Ocramius: https://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/. - * - * @param object $object - * @param string $property - * - * @return mixed - */ - private function readPrivateProperty($object, string $property) - { - $reflectionClass = new \ReflectionClass($object); - $reflectionProperty = null; - do { - if ($reflectionClass->hasProperty($property)) { - $reflectionProperty = $reflectionClass->getProperty($property); - } - $reflectionClass = $reflectionClass->getParentClass(); - } while ($reflectionClass); - - if ($reflectionProperty === null) { - throw new \InvalidArgumentException("Unable to find property '".$property.'" in object of class '.get_class($object).". Please check your @URL annotation."); - } - - $reflectionProperty->setAccessible(true); - return $reflectionProperty->getValue($object); - } - - /** - * Returns the supported HTTP methods on this function, based on the annotations (\@Get, \@Post, etc...). - * - * @param ReflectionMethod $refMethod - * - * @return array - */ - private function getSupportedHttpMethods(ReflectionMethod $refMethod) : array - { - $methods = array(); - - if ($this->annotationReader->getMethodAnnotation($refMethod, Get::class)) { - $methods[] = 'GET'; - } - if ($this->annotationReader->getMethodAnnotation($refMethod, Post::class)) { - $methods[] = 'POST'; - } - if ($this->annotationReader->getMethodAnnotation($refMethod, Put::class)) { - $methods[] = 'PUT'; - } - if ($this->annotationReader->getMethodAnnotation($refMethod, Delete::class)) { - $methods[] = 'DELETE'; - } - - return $methods; - } -} diff --git a/src/Mouf/Mvc/Splash/Services/ControllerDetector.php b/src/Mouf/Mvc/Splash/Services/ControllerDetector.php deleted file mode 100644 index 62a509a..0000000 --- a/src/Mouf/Mvc/Splash/Services/ControllerDetector.php +++ /dev/null @@ -1,29 +0,0 @@ -controllerAnalyzer = $controllerAnalyzer; - $controllersArr = array_values($controllers); - $this->controllers = array_combine($controllersArr, $controllersArr); - $this->controllerDetector = $controllerDetector; - } - - /** - * Adds a container to the registry (by its instance name). - * Note: any object that has a \@Action or \@URL annotation is a controller. - * - * @param string $controller - * - * @return ControllerRegistry - */ - public function addController(string $controller) : ControllerRegistry - { - $this->controllers[$controller] = $controller; - - return $this; - } - - /** - * Returns the list of URLs that can be accessed, and the function/method that should be called when the URL is called. - * - * @param string $instanceName The identifier for this object in the container. - * - * @return SplashRoute[] - * - * @throws \Mouf\Mvc\Splash\Utils\SplashException - */ - public function getUrlsList($instanceName) - { - // FIXME: $instanceName is no more needed! o_O - $urlsList = []; - - if ($this->controllerDetector) { - $detectedControllers = array_values($this->controllerDetector->getControllerIdentifiers($this->controllerAnalyzer)); - $detectedControllers = array_combine($detectedControllers, $detectedControllers); - - $controllers = $this->controllers + $detectedControllers; - } else { - $controllers = $this->controllers; - } - - foreach ($controllers as $controllerInstanceName) { - $routes = $this->controllerAnalyzer->analyzeController($controllerInstanceName); - - $urlsList = array_merge($urlsList, $routes); - } - - return $urlsList; - } - - /** - * Returns a unique tag representing the list of SplashRoutes returned. - * If the tag changes, the cache is flushed by Splash. - * - * Important! This must be quick to compute. - * - * @return mixed - */ - public function getExpirationTag() : string - { - // An approximate, quick-to-compute rule that will force renewing the cache if a controller is added are a parameter is fetched. - return implode('-/-', $this->controllers).($this->controllerDetector !== null ? $this->controllerDetector->getExpirationTag() : ''); - } -} diff --git a/src/Mouf/Mvc/Splash/Services/FilterUtils.php b/src/Mouf/Mvc/Splash/Services/FilterUtils.php deleted file mode 100644 index 441b0b7..0000000 --- a/src/Mouf/Mvc/Splash/Services/FilterUtils.php +++ /dev/null @@ -1,63 +0,0 @@ -getDeclaringClass(); - - $parentsArray = array(); - $parentClass = $refClass; - while ($parentClass !== false) { - $parentsArray[] = $parentClass; - $parentClass = $parentClass->getParentClass(); - } - - // Start with the most parent class and goes to the target class: - for ($i = count($parentsArray) - 1; $i >= 0; --$i) { - $class = $parentsArray[$i]; - /* @var $class ReflectionClass */ - $annotations = $reader->getClassAnnotations($class); - - foreach ($annotations as $annotation) { - if (is_callable($annotation)) { - $filterArray[] = $annotation; - } - } - } - - // Continue with the method (and eventually override class parameters) - $annotations = $reader->getMethodAnnotations($refMethod); - - foreach ($annotations as $annotation) { - if (is_callable($annotation)) { - $filterArray[] = $annotation; - } - } - - return $filterArray; - } -} diff --git a/src/Mouf/Mvc/Splash/Services/MoufExplorerUrlProvider.php b/src/Mouf/Mvc/Splash/Services/MoufExplorerUrlProvider.php index 3b23bf9..c66f462 100644 --- a/src/Mouf/Mvc/Splash/Services/MoufExplorerUrlProvider.php +++ b/src/Mouf/Mvc/Splash/Services/MoufExplorerUrlProvider.php @@ -3,6 +3,8 @@ namespace Mouf\Mvc\Splash\Services; use Mouf\MoufManager; +use TheCodingMachine\Splash\Services\UrlProviderInterface; +use TheCodingMachine\Splash\Services\SplashRoute; /** * This class scans the Mouf container in order to find all UrlProviderInterface instances. @@ -20,7 +22,7 @@ class MoufExplorerUrlProvider implements UrlProviderInterface public function getUrlsList($instanceName) { $moufManager = MoufManager::getMoufManager(); - $instanceNames = $moufManager->findInstances('Mouf\\Mvc\\Splash\\Services\\UrlProviderInterface'); + $instanceNames = $moufManager->findInstances(UrlProviderInterface::class); $urls = array(); diff --git a/src/Mouf/Mvc/Splash/Services/ParameterFetcher.php b/src/Mouf/Mvc/Splash/Services/ParameterFetcher.php deleted file mode 100644 index 485391c..0000000 --- a/src/Mouf/Mvc/Splash/Services/ParameterFetcher.php +++ /dev/null @@ -1,43 +0,0 @@ -parameterFetchers = $parameterFetchers; - } - - /** - * Builds a registry with the default fetchers. - * - * @return ParameterFetcherRegistry - */ - public static function buildDefaultControllerRegistry() : ParameterFetcherRegistry - { - return new self([ - new SplashRequestFetcher(), - new SplashRequestParameterFetcher(), - ]); - } - - /** - * Adds a parameter fetcher. It will be executed at the top of the list (first). - * - * @param ParameterFetcher $parameterFetcher - * - * @return ParameterFetcherRegistry - */ - public function registerParameterFetcher(ParameterFetcher $parameterFetcher) : ParameterFetcherRegistry - { - array_unshift($this->parameterFetchers, $parameterFetcher); - - return $this; - } - - /** - * Analyses the method and returns an array of SplashRequestParameterFetcher. - * Note: the return from this method is meant to be cached. - * - * @param ReflectionMethod $refMethod - * @param string $url - * - * @return array[] An array representing serializable fetchers. Each fetcher is represented as an array with 2 keys: "fetcherId" (an ID for the fetcher) and "data" (data required by the fetcher) - * - * @throws SplashException - */ - public function mapParameters(ReflectionMethod $refMethod, string $url = null) : array - { - $parameters = $refMethod->getParameters(); - - $values = []; - - foreach ($parameters as $parameter) { - $found = false; - foreach ($this->parameterFetchers as $id => $fetcher) { - if ($fetcher->canHandle($parameter)) { - $data = $fetcher->getFetcherData($parameter, $url); - $values[] = ['fetcherId' => $id, 'data' => $data]; - $found = true; - break; - } - } - if (!$found) { - throw new SplashException('Unable to handle parameter $'.$parameter->getName().' in '.$parameter->getDeclaringClass()->getName().'::'.$parameter->getDeclaringFunction()->getName()); - } - } - - return $values; - } - - /** - * Maps data returned by mapParameters to real arguments to be passed to the action. - * - * @param SplashRequestContext $context - * @param array $parametersMap - * - * @return array - */ - public function toArguments(SplashRequestContext $context, array $parametersMap) : array - { - $arguments = []; - foreach ($parametersMap as $parameter) { - $fetcherid = $parameter['fetcherId']; - $data = $parameter['data']; - $arguments[] = $this->parameterFetchers[$fetcherid]->fetchValue($data, $context); - } - - return $arguments; - } -} diff --git a/src/Mouf/Mvc/Splash/Services/SplashCreateControllerService.php b/src/Mouf/Mvc/Splash/Services/SplashCreateControllerService.php index 616cc36..64c0271 100644 --- a/src/Mouf/Mvc/Splash/Services/SplashCreateControllerService.php +++ b/src/Mouf/Mvc/Splash/Services/SplashCreateControllerService.php @@ -5,7 +5,7 @@ use Mouf\Mvc\Splash\Controllers\Controller; use Mouf\Html\Template\TemplateInterface; use Mouf\Html\HtmlElement\HtmlBlock; -use Mouf\Mvc\Splash\Utils\SplashException; +use TheCodingMachine\Splash\Utils\SplashException; use Psr\Log\LoggerInterface; use Mouf\MoufManager; use Mouf\MoufCache; @@ -117,11 +117,11 @@ public function generate(MoufManager $moufManager, $controllerName, $instanceNam ?> namespace ; -use Mouf\Mvc\Splash\Annotations\Get; -use Mouf\Mvc\Splash\Annotations\Post; -use Mouf\Mvc\Splash\Annotations\Put; -use Mouf\Mvc\Splash\Annotations\Delete; -use Mouf\Mvc\Splash\Annotations\URL; +use TheCodingMachine\Splash\Annotations\Get; +use TheCodingMachine\Splash\Annotations\Post; +use TheCodingMachine\Splash\Annotations\Put; +use TheCodingMachine\Splash\Annotations\Delete; +use TheCodingMachine\Splash\Annotations\URL; use Mouf\Html\Template\TemplateInterface; @@ -134,9 +134,9 @@ public function generate(MoufManager $moufManager, $controllerName, $instanceNam ?> use Psr\Log\LoggerInterface; +?> +use Psr\Http\Message\ResponseInterface; use getVariable('tdbmDefaultDaoNamespace').'\\Generated\\'.$moufManager->getVariable('tdbmDefaultDaoFactoryName') ?>; @@ -345,7 +345,7 @@ public function () { + ?>): ResponseInterface { // TODO: write content of action here diff --git a/src/Mouf/Mvc/Splash/Services/SplashCreateControllerServiceException.php b/src/Mouf/Mvc/Splash/Services/SplashCreateControllerServiceException.php index 04ba028..56ac116 100644 --- a/src/Mouf/Mvc/Splash/Services/SplashCreateControllerServiceException.php +++ b/src/Mouf/Mvc/Splash/Services/SplashCreateControllerServiceException.php @@ -2,7 +2,7 @@ namespace Mouf\Mvc\Splash\Services; -use Mouf\Mvc\Splash\Utils\SplashException; +use TheCodingMachine\Splash\Utils\SplashException; class SplashCreateControllerServiceException extends SplashException { diff --git a/src/Mouf/Mvc/Splash/Services/SplashRequestContext.php b/src/Mouf/Mvc/Splash/Services/SplashRequestContext.php deleted file mode 100644 index fdc64a9..0000000 --- a/src/Mouf/Mvc/Splash/Services/SplashRequestContext.php +++ /dev/null @@ -1,118 +0,0 @@ -request = $request; - } - - /** - * Add a new parameter. - * - * @param string $key - * @param string $value - */ - public function addUrlParameter($key, $value) - { - $this->urlParameters[$key] = $value; - } - - /** - * Sets all parameters at once. - * - * @param array $urlParameters - */ - public function setUrlParameters(array $urlParameters) - { - $this->urlParameters = $urlParameters; - } - - /** - * Returns the list of parameters seen while analysing the URL. - * - * @return array - */ - public function getUrlParameters() - { - return $this->urlParameters; - } - - /** - * Returns the request. - * - * @return ServerRequestInterface - */ - public function getRequest() - { - return $this->request; - } - - public function hasParameter($key) : bool - { - if (isset($this->urlParameters[$key])) { - return true; - } elseif (isset($this->request->getParsedBody()[$key])) { - return true; - } elseif (isset($this->request->getQueryParams()[$key])) { - return true; - } elseif (isset($this->request->getUploadedFiles()[$key])) { - return true; - } - - return false; - } - - /** - * Scan the URL parameters and the request parameters and return the given parameter (or a default value). - * - * @param string $key - * - * @return mixed - * - * @throws SplashMissingParameterException - */ - public function getParameter(string $key, bool $compulsory, $default = null) - { - if (isset($this->urlParameters[$key])) { - $value = $this->urlParameters[$key]; - } else { - $postVals = $this->request->getParsedBody(); - $value = null; - if (isset($postVals[$key])) { - $value = $postVals[$key]; - } else { - $getVals = $this->request->getQueryParams(); - if (isset($getVals[$key])) { - $value = $getVals[$key]; - } else { - $uploadedFiles = $this->request->getUploadedFiles(); - if (isset($uploadedFiles[$key])) { - $value = $uploadedFiles[$key]; - } - } - } - } - if ($value !== null) { - return $value; - } elseif (!$compulsory) { - return $default; - } else { - throw SplashMissingParameterException::create($key); - } - } -} diff --git a/src/Mouf/Mvc/Splash/Services/SplashRequestFetcher.php b/src/Mouf/Mvc/Splash/Services/SplashRequestFetcher.php deleted file mode 100644 index 83a86a4..0000000 --- a/src/Mouf/Mvc/Splash/Services/SplashRequestFetcher.php +++ /dev/null @@ -1,64 +0,0 @@ -getClass(); - if ($class === null) { - return false; - } - $name = $class->getName(); - // Check type of requested parameter; Only interfaces are allowed in an action of a controller. - if ($name === 'Psr\\Http\\Message\\RequestInterface' || $name === 'Psr\\Http\\Message\\ServerRequestInterface') { - return true; - } else { - return false; - } - } - - /** - * Returns some data needed by this fetcher to fetch data from the request. - * This data MUST be serializable (and will be serialized). This function will be called only once - * and data cached. You can perform expensive computation in this function. - * - * @param ReflectionParameter $reflectionParameter - * @param string|null $url - * - * @return mixed - */ - public function getFetcherData(ReflectionParameter $reflectionParameter, string $url = null) - { - return; - } - - /** - * Returns the value to be injected in this parameter. - * - * @param mixed $data The data generated by "getFetcherData" - * @param SplashRequestContext $context - * - * @return mixed - */ - public function fetchValue($data, SplashRequestContext $context) - { - return $context->getRequest(); - } -} diff --git a/src/Mouf/Mvc/Splash/Services/SplashRequestParameterFetcher.php b/src/Mouf/Mvc/Splash/Services/SplashRequestParameterFetcher.php deleted file mode 100644 index 210794e..0000000 --- a/src/Mouf/Mvc/Splash/Services/SplashRequestParameterFetcher.php +++ /dev/null @@ -1,70 +0,0 @@ -isDefaultValueAvailable()) { - return [ - 'key' => $reflectionParameter->getName(), - 'compulsory' => false, - 'default' => $reflectionParameter->getDefaultValue(), - ]; - } else { - return [ - 'key' => $reflectionParameter->getName(), - 'compulsory' => true, - ]; - } - } - - /** - * Returns the value to be injected in this parameter. - * - * @param mixed $data The data generated by "getFetcherData" - * @param SplashRequestContext $context - * - * @return mixed - */ - public function fetchValue($data, SplashRequestContext $context) - { - $key = $data['key']; - $compulsory = $data['compulsory']; - $default = $data['default'] ?? null; - - return $context->getParameter($key, $compulsory, $default); - } -} diff --git a/src/Mouf/Mvc/Splash/Services/SplashRoute.php b/src/Mouf/Mvc/Splash/Services/SplashRoute.php deleted file mode 100644 index 619aa98..0000000 --- a/src/Mouf/Mvc/Splash/Services/SplashRoute.php +++ /dev/null @@ -1,183 +0,0 @@ - - */ - private $httpMethods; - - private $controllerInstanceName; - - private $methodName; - - private $title; - - private $fullComment; - - /** - * An ordered list of parameters. - * The first parameter to be passed to the method will be fetched from values set $parameters[0], etc... - * - * - * @var array - */ - private $parameters; - - /** - * A list of all filters to apply to the route. - * - * @var array An array of filters. - */ - private $filters; - - /** - * The list of parameters matched during the route. - * This is filled at runtime, by the SplashUrlNode class. - * - * @var array - */ - private $filledParameters = array(); - // Question: abstraire SplashRoute et rajouter un getCallbackHandler??? - - /** - * The file that contains the controller class. - * Used to invalidate the cache. - * - * @var string - */ - private $fileName; - - /** - * @var int - */ - private $fileModificationTime; - - public function __construct(string $url, string $controllerInstanceName, string $methodName, string $title = null, string $fullComment = null, array $httpMethods = array(), array $parameters = array(), array $filters = array(), string $fileName = null) - { - $this->url = $url; - $this->httpMethods = $httpMethods; - $this->controllerInstanceName = $controllerInstanceName; - $this->methodName = $methodName; - $this->title = $title; - $this->fullComment = $fullComment; - $this->parameters = $parameters; - $this->filters = $filters; - - if ($fileName !== null) { - $this->fileName = $fileName; - $this->fileModificationTime = filemtime($fileName); - if ($this->fileModificationTime === false) { - throw new SplashException(sprintf('Could not find file modification time for "%s"', $this->fileName)); - } - } - } - - /** - * @return mixed - */ - public function getUrl() : string - { - return $this->url; - } - - /** - * List of HTTP methods allowed for this callback. - * If empty, all methods are allowed. - * - * @return string[] - */ - public function getHttpMethods() : array - { - return $this->httpMethods; - } - - /** - * @return string - */ - public function getControllerInstanceName() : string - { - return $this->controllerInstanceName; - } - - /** - * @return string - */ - public function getMethodName() : string - { - return $this->methodName; - } - - /** - * @return string - */ - public function getTitle() - { - return $this->title; - } - - /** - * @return string|null - */ - public function getFullComment() - { - return $this->fullComment; - } - - /** - * @return array - */ - public function getParameters() : array - { - return $this->parameters; - } - - /** - * @return array - */ - public function getFilters() : array - { - return $this->filters; - } - - /** - * @return string[] - */ - public function getFilledParameters() : array - { - return $this->filledParameters; - } - - public function setFilledParameters(array $parameters) - { - $this->filledParameters = $parameters; - } - - /** - * Checks if the data stored in this route is fresh or not (it comes from the cache). - * - * @return bool - */ - public function isCacheValid() : bool - { - if ($this->fileName === null) { - return true; - } - - return $this->fileModificationTime === filemtime($this->fileName); - } -} diff --git a/src/Mouf/Mvc/Splash/Services/SplashRouteInterface.php b/src/Mouf/Mvc/Splash/Services/SplashRouteInterface.php deleted file mode 100644 index 28a7403..0000000 --- a/src/Mouf/Mvc/Splash/Services/SplashRouteInterface.php +++ /dev/null @@ -1,68 +0,0 @@ - $head) { - header_remove($key); - } - - if ($result !== null) { - // We might be in weak mode, it is not normal to have both an output and a response! - $html = '

Output started in controller. It is not normal to have an output in the controller, and a response returned by the controller. Output detected:

'.$html; - $code = 500; - } - - return new HtmlResponse($html, $code, $headers); - } else { - if ($debug) { - $html = '

Output started in controller. A controller should return an object implementing the ResponseInterface rather than outputting directly content. Output detected:

'.$html; - - return new HtmlResponse($html, 500); - } else { - throw new SplashException('Output started in Controller : '.$html); - } - } - } - - if (!$result instanceof ResponseInterface) { - if ($result === null) { - throw new SplashException('Your controller should return an instance of Psr\\Http\\Message\\ResponseInterface. Your controller did not return any value.'); - } else { - $class = (gettype($result) == 'object') ? get_class($result) : gettype($result); - throw new SplashException('Your controller should return an instance of Psr\\Http\\Message\\ResponseInterface. Type of value returned: '.$class); - } - } - - return $result; - - // TODO: If Symfony Response convert to psr-7 -// if ($result instanceof Response) { -// if ($html !== "") { -// throw new SplashException("You cannot output text AND return Response object in the same action. Output already started :'$html"); -// } -// -// if (headers_sent()) { -// $headers = headers_list(); -// throw new SplashException("Headers already sent. Detected headers are : ".var_export($headers, true)); -// } -// -// return $result; -// } -// -// $code = http_response_code(); -// $headers = SplashUtils::greatResponseHeaders(); -// -// // Suppress actual headers (re-add by Symfony Response) -// // If you don't remove old headers, it's duplicated in HTTP Headers -// foreach ($headers as $key => $head) { -// header_remove($key); -// } -// -// return new Response($html, $code, $headers); - } - - /** - * Same as apache_response_headers (for any server). - * - * @return array - */ - private static function getResponseHeaders() - { - $arh = array(); - - // headers_list don't return associative array - $headers = headers_list(); - foreach ($headers as $header) { - $header = explode(':', $header); - $arh[array_shift($header)] = trim(implode(':', $header)); - } - - return $arh; - } -} diff --git a/src/Mouf/Mvc/Splash/Services/UrlProviderInterface.php b/src/Mouf/Mvc/Splash/Services/UrlProviderInterface.php deleted file mode 100644 index 8614811..0000000 --- a/src/Mouf/Mvc/Splash/Services/UrlProviderInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ - private $children = array(); - - /** - * An array of parameterized subnodes. - * - * @var array - */ - private $parameterizedChildren = array(); - - /** - * A list of callbacks (assicated to there HTTP method). - * - * @var array - */ - private $callbacks = array(); - - /** - * A list of callbacks (assicated to there HTTP method) finishing with "*". - * - * @var array - */ - private $wildcardCallbacks = array(); - - public function registerCallback(SplashRouteInterface $callback) - { - $this->addUrl(explode('/', $callback->getUrl()), $callback); - } - - /** - * Registers a new URL. - * The URL is passed as an array of strings (exploded on /). - * - * @param array $urlParts - */ - protected function addUrl(array $urlParts, SplashRouteInterface $callback) - { - if (!empty($urlParts)) { - $key = array_shift($urlParts); - - if ($key == '*') { - // Wildcard URL - if (!empty($urlParts)) { - throw new SplashException('Sorry, the URL pattern /foo/*/bar is not supported. The wildcard (*) must be at the end of an URL'); - } - - $httpMethods = $callback->getHttpMethods(); - if (empty($httpMethods)) { - if (isset($this->wildcardCallbacks[''])) { - throw new SplashException("An error occured while looking at the list URL managed in Splash. The URL '".$callback->getUrl()."' is associated " - ."to 2 methods: \$".$callback->getControllerInstanceName().'->'.$callback->getMethodName()." and \$".$this->wildcardCallbacks['']->getControllerInstanceName().'->'.$this->wildcardCallbacks['']->getMethodName()); - } - $this->wildcardCallbacks[''] = $callback; - } else { - foreach ($httpMethods as $httpMethod) { - if (isset($this->wildcardCallbacks[$httpMethod])) { - throw new SplashException("An error occured while looking at the list URL managed in Splash. The URL '".$callback->getUrl()."' for HTTP method '".$httpMethod."' is associated " - ."to 2 methods: \$".$callback->getControllerInstanceName().'->'.$callback->getMethodName()." and \$".$this->wildcardCallbacks[$httpMethod]->getControllerInstanceName().'->'.$this->wildcardCallbacks[$httpMethod]->getMethodName()); - } - $this->wildcardCallbacks[$httpMethod] = $callback; - } - } - } elseif (strpos($key, '{') === 0 && strpos($key, '}') === strlen($key) - 1) { - // Parameterized URL element - $varName = substr($key, 1, strlen($key) - 2); - - if (!isset($this->parameterizedChildren[$varName])) { - $this->parameterizedChildren[$varName] = new self(); - } - $this->parameterizedChildren[$varName]->addUrl($urlParts, $callback); - } else { - // Usual URL element - if (!isset($this->children[$key])) { - $this->children[$key] = new self(); - } - $this->children[$key]->addUrl($urlParts, $callback); - } - } else { - $httpMethods = $callback->getHttpMethods(); - if (empty($httpMethods)) { - if (isset($this->callbacks[''])) { - throw new SplashException("An error occured while looking at the list URL managed in Splash. The URL '".$callback->getUrl()."' is associated " - ."to 2 methods: \$".$callback->getControllerInstanceName().'->'.$callback->getMethodName()." and \$".$this->callbacks['']->getControllerInstanceName().'->'.$this->callbacks['']->getMethodName()); - } - $this->callbacks[''] = $callback; - } else { - foreach ($httpMethods as $httpMethod) { - if (isset($this->callbacks[$httpMethod])) { - throw new SplashException("An error occured while looking at the list URL managed in Splash. The URL '".$callback->getUrl()."' for HTTP method '".$httpMethod."' is associated " - ."to 2 methods: \$".$callback->getControllerInstanceName().'->'.$callback->getMethodName()." and \$".$this->callbacks[$httpMethod]->getControllerInstanceName().'->'.$this->callbacks[$httpMethod]->getMethodName()); - } - $this->callbacks[$httpMethod] = $callback; - } - } - } - } - - /** - * Walks through the nodes to find the callback associated to the URL. - * - * @param string $url - * @param ServerRequestInterface $request - * - * @return SplashRoute - */ - public function walk($url, ServerRequestInterface $request) - { - return $this->walkArray(explode('/', $url), $request, array()); - } - - /** - * Walks through the nodes to find the callback associated to the URL. - * - * @param array $urlParts - * @param ServerRequestInterface $request - * @param array $parameters - * @param SplashRoute $closestWildcardRoute The last wildcard (*) route encountered while navigating the tree. - * - * @return SplashRoute - * - * @throws SplashException - */ - private function walkArray(array $urlParts, ServerRequestInterface $request, array $parameters, $closestWildcardRoute = null) - { - $httpMethod = $request->getMethod(); - - if (isset($this->wildcardCallbacks[$httpMethod])) { - $closestWildcardRoute = $this->wildcardCallbacks[$httpMethod]; - $closestWildcardRoute->setFilledParameters($parameters); - } elseif (isset($this->wildcardCallbacks[''])) { - $closestWildcardRoute = $this->wildcardCallbacks['']; - $closestWildcardRoute->setFilledParameters($parameters); - } - - if (!empty($urlParts)) { - $key = array_shift($urlParts); - if (isset($this->children[$key])) { - return $this->children[$key]->walkArray($urlParts, $request, $parameters, $closestWildcardRoute); - } - - foreach ($this->parameterizedChildren as $varName => $splashUrlNode) { - if (isset($parameters[$varName])) { - throw new SplashException("An error occured while looking at the list URL managed in Splash. In a @URL annotation, the parameter '{$parameters[$varName]}' appears twice. That should never happen"); - } - $newParams = $parameters; - $newParams[$varName] = $key; - $result = $this->parameterizedChildren[$varName]->walkArray($urlParts, $request, $newParams, $closestWildcardRoute); - if ($result !== null) { - return $result; - } - } - - // If we arrive here, there was no parametrized URL matching our objective - return $closestWildcardRoute; - } else { - if (isset($this->callbacks[$httpMethod])) { - $route = $this->callbacks[$httpMethod]; - $route->setFilledParameters($parameters); - - return $route; - } elseif (isset($this->callbacks[''])) { - $route = $this->callbacks['']; - $route->setFilledParameters($parameters); - - return $route; - } else { - return $closestWildcardRoute; - } - } - } -} diff --git a/src/Mouf/Mvc/Splash/UrlEntryPoint.php b/src/Mouf/Mvc/Splash/UrlEntryPoint.php index ed37fd9..797c0a7 100644 --- a/src/Mouf/Mvc/Splash/UrlEntryPoint.php +++ b/src/Mouf/Mvc/Splash/UrlEntryPoint.php @@ -3,8 +3,8 @@ namespace Mouf\Mvc\Splash; use Mouf\Utils\Action\ActionInterface; -use Mouf\Mvc\Splash\Services\SplashRoute; -use Mouf\Mvc\Splash\Services\UrlProviderInterface; +use TheCodingMachine\Splash\Services\SplashRoute; +use TheCodingMachine\Splash\Services\UrlProviderInterface; use Mouf\Utils\Common\UrlInterface; /** @@ -66,8 +66,6 @@ public function getUrl() * If the tag changes, the cache is flushed by Splash. * * Important! This must be quick to compute. - * - * @return mixed */ public function getExpirationTag() : string { diff --git a/src/Mouf/Mvc/Splash/Utils/ExceptionUtils.php b/src/Mouf/Mvc/Splash/Utils/ExceptionUtils.php index dad6332..40d4111 100644 --- a/src/Mouf/Mvc/Splash/Utils/ExceptionUtils.php +++ b/src/Mouf/Mvc/Splash/Utils/ExceptionUtils.php @@ -7,9 +7,9 @@ class ExceptionUtils /** * Returns the Exception Backtrace as a nice HTML view. * - * @param unknown_type $backtrace + * @param array $backtrace * - * @return unknown + * @return string */ private static function getHTMLBackTrace($backtrace) { @@ -51,9 +51,9 @@ private static function getHTMLBackTrace($backtrace) * Function called to display an exception if it occurs. * It will make sure to purge anything in the buffer before calling the exception displayer. * - * @param Exception $exception + * @param \Throwable $exception */ - public static function getHtmlForException(\Exception $exception) + public static function getHtmlForException(\Throwable $exception) { //global $sys_error_reporting_mail; //global $sys_error_messages; @@ -87,9 +87,9 @@ public static function getHtmlForException(\Exception $exception) * Function called to display an exception if it occurs. * It will make sure to purge anything in the buffer before calling the exception displayer. * - * @param Exception $exception + * @param \Throwable $exception */ - public static function getTextForException(\Exception $exception) + public static function getTextForException(\Throwable $exception) { // Now, let's compute the same message, but without the HTML markup for the error log. $textTrace = 'Message: '.$exception->getMessage()."\n"; @@ -103,9 +103,9 @@ public static function getTextForException(\Exception $exception) /** * Returns the Exception Backtrace as a text string. * - * @param unknown_type $backtrace + * @param array $backtrace * - * @return unknown + * @return string */ private static function getTextBackTrace($backtrace) { @@ -145,9 +145,9 @@ private static function getTextBackTrace($backtrace) /** * Used by the debug function to display a nice view of the parameters. * - * @param unknown_type $var + * @param mixed $var * - * @return unknown + * @return string */ private static function getPhpVariableAsText($var) { diff --git a/src/Mouf/Mvc/Splash/Utils/SplashException.php b/src/Mouf/Mvc/Splash/Utils/SplashException.php deleted file mode 100644 index b0e565a..0000000 --- a/src/Mouf/Mvc/Splash/Utils/SplashException.php +++ /dev/null @@ -1,10 +0,0 @@ -register(new SplashServiceProvider()); - - $simplex[Reader::class] = function () { - return new AnnotationReader(); - }; - - $simplex[TestController2::class] = function () { - return new TestController2(); - }; - - $simplex['thecodingmachine.splash.controllers'] = [ - TestController2::class, - ]; - - $defaultRouter = $simplex->get(SplashDefaultRouter::class); - $this->assertInstanceOf(SplashDefaultRouter::class, $defaultRouter); - - // Now, let's test the redirect - $request = new ServerRequest([], [], '/foo/var/bar/', 'GET', 'php://input', - [], - [], - ['id' => 42] - ); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertEquals('/foo/var/bar', $response->getHeader('Location')[0]); - } -} diff --git a/tests/Mouf/Mvc/Splash/Fixtures/TestAction.php b/tests/Mouf/Mvc/Splash/Fixtures/TestAction.php deleted file mode 100644 index 3a691cc..0000000 --- a/tests/Mouf/Mvc/Splash/Fixtures/TestAction.php +++ /dev/null @@ -1,23 +0,0 @@ -notexist}/") - */ - public function badParam() - { - return new JsonResponse([ - ]); - } -} diff --git a/tests/Mouf/Mvc/Splash/Fixtures/TestController.php b/tests/Mouf/Mvc/Splash/Fixtures/TestController.php deleted file mode 100644 index b493aef..0000000 --- a/tests/Mouf/Mvc/Splash/Fixtures/TestController.php +++ /dev/null @@ -1,20 +0,0 @@ -param}/foo/{$this->param2}") - */ - public function action1() - { - return new JsonResponse([ - "hello" => "world" - ]); - } - - /** - * @Action - */ - public function actionAnnotation() - { - } - - /** - * @Action - * @Title("Main page") - * @Get - * @Post - * @Put - * @Delete - */ - public function index() - { - } - - /** - * @URL("/foo/{var}/bar") - */ - public function completeTest($id, ServerRequestInterface $request, $var, $opt = 42) - { - return new JsonResponse([ - 'id' => $id, - 'id2' => $request->getQueryParams()['id'], - 'var' => $var, - 'opt' => $opt, - ]); - } - - /** - * @Action - */ - public function triggerException() - { - throw new \Exception('boum!'); - } -} diff --git a/tests/Mouf/Mvc/Splash/Fixtures/TestController3.php b/tests/Mouf/Mvc/Splash/Fixtures/TestController3.php deleted file mode 100644 index 56555f5..0000000 --- a/tests/Mouf/Mvc/Splash/Fixtures/TestController3.php +++ /dev/null @@ -1,27 +0,0 @@ - 'success' - ]); - } -} diff --git a/tests/Mouf/Mvc/Splash/Fixtures/TestExtendedController2.php b/tests/Mouf/Mvc/Splash/Fixtures/TestExtendedController2.php deleted file mode 100644 index 259730c..0000000 --- a/tests/Mouf/Mvc/Splash/Fixtures/TestExtendedController2.php +++ /dev/null @@ -1,18 +0,0 @@ -param1} params when extending a class. -} diff --git a/tests/Mouf/Mvc/Splash/Fixtures/TestFilter.php b/tests/Mouf/Mvc/Splash/Fixtures/TestFilter.php deleted file mode 100644 index 02bd2e6..0000000 --- a/tests/Mouf/Mvc/Splash/Fixtures/TestFilter.php +++ /dev/null @@ -1,33 +0,0 @@ -params = $params; - } - - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next, ContainerInterface $container) - { - $request = $request->withQueryParams(array_merge($request->getQueryParams(), $this->params)); - $response = $next($request, $response); - - return $response; - } -} diff --git a/tests/Mouf/Mvc/Splash/Fixtures/TestFilteredController.php b/tests/Mouf/Mvc/Splash/Fixtures/TestFilteredController.php deleted file mode 100644 index fb11b67..0000000 --- a/tests/Mouf/Mvc/Splash/Fixtures/TestFilteredController.php +++ /dev/null @@ -1,23 +0,0 @@ - function () { - return new TestController2(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry); - - $request = new ServerRequest([], [], '/foo/var/bar', 'GET', 'php://input', - [], - [], - ['id' => 42] - ); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertInstanceOf(JsonResponse::class, $response); - /* @var $response JsonResponse */ - $decodedResponse = json_decode((string) $response->getBody(), true); - $this->assertEquals(42, $decodedResponse['id']); - $this->assertEquals('var', $decodedResponse['var']); - $this->assertEquals(42, $decodedResponse['id2']); - $this->assertEquals(42, $decodedResponse['opt']); - - // Now, let's test the redirect - $request = new ServerRequest([], [], '/foo/var/bar/', 'GET', 'php://input', - [], - [], - ['id' => 42] - ); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertEquals('/foo/var/bar', $response->getHeader('Location')[0]); - - // Now, let's test the second kind of redirect - $request = new ServerRequest([], [], '/controller', 'GET'); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertEquals('/controller/', $response->getHeader('Location')[0]); - } - - public function testUnknownRoute() - { - $container = new Picotainer([ - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $defaultRouter = new SplashDefaultRouter($container, [], $parameterFetcherRegistry); - - $request = new ServerRequest([], [], '/foo', 'GET'); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response, function () { - return new HtmlResponse('Not found', 404); - }); - $this->assertInstanceOf(HtmlResponse::class, $response); - /* @var $response HtmlResponse */ - $this->assertEquals(404, $response->getStatusCode()); - $this->assertEquals('Not found', (string) $response->getBody()); - - // Now, let's retry without a $out parameter and let's check we get an exception - $this->expectException(PageNotFoundException::class); - $response = $defaultRouter($request, $response); - } - - - public function testEmojiRoute() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestController3(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry); - - $request = new ServerRequest([], [], '/'.urlencode('🍕'), 'GET'); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertInstanceOf(JsonResponse::class, $response); - } - - public function testUnknownRouteWith404Handler() - { - $container = new Picotainer([ - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $defaultRouter = new SplashDefaultRouter($container, [], $parameterFetcherRegistry); - $errorsController = HttpErrorsController::createDefault(); - $defaultRouter->setHttp404Handler($errorsController); - - $request = new ServerRequest([], [], '/foo', 'GET'); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - /* @var $response HtmlResponse */ - - // Now, let's retry without a $out parameter and let's check we get an exception - $response = $defaultRouter($request, $response); - $this->assertEquals(404, $response->getStatusCode()); - } - - public function testRootUrlError() - { - $container = new Picotainer([ - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $defaultRouter = new SplashDefaultRouter($container, [], $parameterFetcherRegistry, null, null, SplashUtils::MODE_STRICT, true, '/baseUrl/'); - - $request = new ServerRequest([], [], '/foo', 'GET'); - $response = new HtmlResponse(''); - $this->expectException(SplashException::class); - $response = $defaultRouter($request, $response); - } - - public function testMissingCompulsoryParameter() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestController2(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry); - - // We need an ID parameter - $request = new ServerRequest([], [], '/foo/var/bar', 'GET'); - $response = new HtmlResponse(''); - $this->expectException(SplashMissingParameterException::class); - $response = $defaultRouter($request, $response); - } - - public function testMissingCompulsoryParameterWithHandler() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestController2(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry); - - $errorsController = HttpErrorsController::createDefault(); - $defaultRouter->setHttp400Handler($errorsController); - - // We need an ID parameter - $request = new ServerRequest([], [], '/foo/var/bar', 'GET'); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertEquals(400, $response->getStatusCode()); - } - - public function testExceptionWithHandler() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestController2(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry); - - $errorsController = HttpErrorsController::createDefault(); - $defaultRouter->setHttp500Handler($errorsController); - - // We need an ID parameter - $request = new ServerRequest([], [], '/controller/triggerException', 'GET'); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertEquals(500, $response->getStatusCode()); - } - - public function testPurgeUrlCache() - { - $cache = $this->prophesize(CacheItemPoolInterface::class); - $cache->deleteItem('splashUrlNodes')->shouldBeCalled(); - - $container = new Picotainer([]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $defaultRouter = new SplashDefaultRouter($container, [], $parameterFetcherRegistry, $cache->reveal()); - $defaultRouter->purgeUrlsCache(); - } - - public function testFilters() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestFilteredController(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry); - - $request = new ServerRequest([], [], '/foo', 'GET'); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertEquals('42bar', (string) $response->getBody()); - } - - public function testExpirationTag() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestController2(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry, new ArrayCachePool()); - - $request = new ServerRequest([], [], '/foo/var/bar', 'GET', 'php://input', - [], - [], - ['id' => 42] - ); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertInstanceOf(JsonResponse::class, $response); - - // Now, let's make another request (this time, we should go through the cache with unchanged etag) - $response2 = $defaultRouter($request, $response); - $this->assertInstanceOf(JsonResponse::class, $response2); - } - - public function testExtendedController() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestExtendedController2(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry, new ArrayCachePool()); - - $request = new ServerRequest([], [], '/url/42/foo/52', 'GET', 'php://input', - [], - [], - [] - ); - $response = new HtmlResponse(''); - $response = $defaultRouter($request, $response); - $this->assertInstanceOf(JsonResponse::class, $response); - } - - public function testBadParam() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestBadParamController(); - }, - ]); - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - $defaultRouter = new SplashDefaultRouter($container, [ - $controllerRegistry, - ], $parameterFetcherRegistry, new ArrayCachePool()); - - $request = new ServerRequest([], [], '/notexistparam/42', 'GET', 'php://input', - [], - [], - [] - ); - $response = new HtmlResponse(''); - $this->expectException(\InvalidArgumentException::class); - $response = $defaultRouter($request, $response); - - } -} diff --git a/tests/Mouf/Mvc/Splash/Services/ControllerAnalyzerTest.php b/tests/Mouf/Mvc/Splash/Services/ControllerAnalyzerTest.php deleted file mode 100644 index 9f80df8..0000000 --- a/tests/Mouf/Mvc/Splash/Services/ControllerAnalyzerTest.php +++ /dev/null @@ -1,25 +0,0 @@ -assertTrue($controllerAnalyzer->isController(TestController2::class)); - $this->assertTrue($controllerAnalyzer->isController(TestAction::class)); - $this->assertFalse($controllerAnalyzer->isController(TestFilter::class)); - } -} diff --git a/tests/Mouf/Mvc/Splash/Services/ControllerRegistryTest.php b/tests/Mouf/Mvc/Splash/Services/ControllerRegistryTest.php deleted file mode 100644 index 6558cb9..0000000 --- a/tests/Mouf/Mvc/Splash/Services/ControllerRegistryTest.php +++ /dev/null @@ -1,102 +0,0 @@ - function () { - return new TestController(); - }, - ]); - - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer); - $controllerRegistry->addController('controller'); - - $urlsList = $controllerRegistry->getUrlsList('foo'); - - $this->assertCount(1, $urlsList); - $this->assertInstanceOf(SplashRoute::class, $urlsList[0]); - $this->assertEquals('myurl', $urlsList[0]->getUrl()); - } - - public function testControllerRegistryThisParam() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestController2(); - }, - ]); - - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, ['controller']); - - $urlsList = $controllerRegistry->getUrlsList('foo'); - - $this->assertCount(5, $urlsList); - $this->assertInstanceOf(SplashRoute::class, $urlsList[0]); - $this->assertEquals('url/42/foo/52', $urlsList[0]->getUrl()); - - $this->assertInstanceOf(SplashRoute::class, $urlsList[1]); - $this->assertEquals('controller/actionAnnotation', $urlsList[1]->getUrl()); - - $this->assertInstanceOf(SplashRoute::class, $urlsList[2]); - $this->assertEquals('controller/', $urlsList[2]->getUrl()); - $this->assertInstanceOf(SplashRoute::class, $urlsList[2]); - $this->assertEquals('Main page', $urlsList[2]->getTitle()); - $this->assertContains('GET', $urlsList[2]->getHttpMethods()); - $this->assertContains('POST', $urlsList[2]->getHttpMethods()); - $this->assertContains('PUT', $urlsList[2]->getHttpMethods()); - $this->assertContains('DELETE', $urlsList[2]->getHttpMethods()); - } - - public function testControllerRegistryWithDetector() - { - $container = new Picotainer([ - 'controller' => function () { - return new TestController(); - }, - ]); - - $parameterFetcherRegistry = ParameterFetcherRegistry::buildDefaultControllerRegistry(); - - $detector = new class implements ControllerDetector - { - public function getControllerIdentifiers(ControllerAnalyzer $controllerAnalyzer) : array - { - return ['controller']; - } - - public function getExpirationTag() : string - { - return ''; - } - }; - - $controllerAnalyzer = new ControllerAnalyzer($container, $parameterFetcherRegistry, new AnnotationReader()); - $controllerRegistry = new ControllerRegistry($controllerAnalyzer, [], $detector); - - $urlsList = $controllerRegistry->getUrlsList('foo'); - - $this->assertCount(1, $urlsList); - $this->assertInstanceOf(SplashRoute::class, $urlsList[0]); - $this->assertEquals('myurl', $urlsList[0]->getUrl()); - } -} diff --git a/tests/Mouf/Mvc/Splash/Services/ParameterFetcherRegistryTest.php b/tests/Mouf/Mvc/Splash/Services/ParameterFetcherRegistryTest.php deleted file mode 100644 index 9c946ba..0000000 --- a/tests/Mouf/Mvc/Splash/Services/ParameterFetcherRegistryTest.php +++ /dev/null @@ -1,40 +0,0 @@ -registerParameterFetcher(new SplashRequestParameterFetcher()); - $registry->registerParameterFetcher(new SplashRequestFetcher()); - - $map = $registry->mapParameters(new ReflectionMethod(TestController2::class, 'completeTest')); - - $this->assertEquals(1, $map[0]['fetcherId']); - $this->assertEquals(0, $map[1]['fetcherId']); - $this->assertEquals(1, $map[2]['fetcherId']); - $this->assertEquals(1, $map[3]['fetcherId']); - - // Now, let's use that map to retrieve data. - $request = new ServerRequest([], [], '/foo/12/bar', 'GET', 'php://input', - [], - [], - ['id' => 42] - ); - $splashContext = new SplashRequestContext($request); - $splashContext->addUrlParameter('var', 'var'); - - $arguments = $registry->toArguments($splashContext, $map); - - $this->assertEquals(42, $arguments[0]); - $this->assertSame($request, $arguments[1]); - $this->assertEquals('var', $arguments[2]); - $this->assertEquals(42, $arguments[3]); - } -} diff --git a/tests/Mouf/Mvc/Splash/Services/SplashRequestContextTest.php b/tests/Mouf/Mvc/Splash/Services/SplashRequestContextTest.php deleted file mode 100644 index 2888c95..0000000 --- a/tests/Mouf/Mvc/Splash/Services/SplashRequestContextTest.php +++ /dev/null @@ -1,48 +0,0 @@ -withQueryParams(['id' => 42]) - ->withParsedBody(['post' => 'foo']) - ->withUploadedFiles(['file' => $file]); - $context = new SplashRequestContext($request); - $context->addUrlParameter('url', 'bar'); - - $this->assertTrue($context->hasParameter('id')); - $this->assertTrue($context->hasParameter('post')); - $this->assertTrue($context->hasParameter('file')); - $this->assertTrue($context->hasParameter('url')); - $this->assertFalse($context->hasParameter('no_exist')); - - $this->assertEquals(42, $context->getParameter('id', true)); - $this->assertEquals('foo', $context->getParameter('post', true)); - $this->assertEquals($file, $context->getParameter('file', true)); - $this->assertEquals('bar', $context->getParameter('url', true)); - $this->assertEquals('default', $context->getParameter('no_exist', false, 'default')); - - $this->expectException(SplashMissingParameterException::class); - $this->assertEquals('bar', $context->getParameter('no_exist', true)); - } - - public function testUrlParameters() - { - $request = new ServerRequest(); - $context = new SplashRequestContext($request); - $context->addUrlParameter('id', 24); - $this->assertEquals(24, $context->getUrlParameters()['id']); - $context->setUrlParameters([ - 'id' => 42, - ]); - $this->assertEquals(42, $context->getUrlParameters()['id']); - } -} diff --git a/tests/Mouf/Mvc/Splash/Services/SplashRequestFetcherTest.php b/tests/Mouf/Mvc/Splash/Services/SplashRequestFetcherTest.php deleted file mode 100644 index b4a7fa0..0000000 --- a/tests/Mouf/Mvc/Splash/Services/SplashRequestFetcherTest.php +++ /dev/null @@ -1,26 +0,0 @@ -getParameters(); - - $splashRequestFetcher = new SplashRequestFetcher(); - $this->assertTrue($splashRequestFetcher->canHandle($params[0])); - $this->assertFalse($splashRequestFetcher->canHandle($params[1])); - - $this->assertNull($splashRequestFetcher->getFetcherData($params[0])); - - $request = new ServerRequest(); - $context = new SplashRequestContext($request); - - $this->assertSame($request, $splashRequestFetcher->fetchValue(null, $context)); - } -} diff --git a/tests/Mouf/Mvc/Splash/Services/SplashRequestParameterFetcherTest.php b/tests/Mouf/Mvc/Splash/Services/SplashRequestParameterFetcherTest.php deleted file mode 100644 index a67a94a..0000000 --- a/tests/Mouf/Mvc/Splash/Services/SplashRequestParameterFetcherTest.php +++ /dev/null @@ -1,49 +0,0 @@ -getParameters(); - - $splashRequestFetcher = new SplashRequestParameterFetcher(); - //$this->assertFalse($splashRequestFetcher->canHandle($params[0])); - $this->assertTrue($splashRequestFetcher->canHandle($params[2])); - - $data = $splashRequestFetcher->getFetcherData($params[2]); - $this->assertEquals([ - 'key' => 'id', - 'compulsory' => false, - 'default' => null, - ], $data); - - $dataCompulsory = $splashRequestFetcher->getFetcherData($params[1]); - $this->assertEquals([ - 'key' => 'compulsory', - 'compulsory' => true, - ], $dataCompulsory); - - $request = new ServerRequest( - [], - [], - 'toto/tata', - 'GET', - 'php://input', - [], - [], - [ - 'id' => 42, - ] - ); - $context = new SplashRequestContext($request); - - $this->assertSame(42, $splashRequestFetcher->fetchValue($data, $context)); - } -} diff --git a/tests/Mouf/Mvc/Splash/Store/SplashUrlNodeTest.php b/tests/Mouf/Mvc/Splash/Store/SplashUrlNodeTest.php deleted file mode 100644 index ecfb7a6..0000000 --- a/tests/Mouf/Mvc/Splash/Store/SplashUrlNodeTest.php +++ /dev/null @@ -1,210 +0,0 @@ -registerCallback($callback); - - $result = $splashUrlNode->walk('toto/tata', new ServerRequest([], [], 'toto/tata', 'GET')); - - /* @var $result SplashRoute */ - $this->assertInstanceOf(SplashRoute::class, $result); - $this->assertEquals('myController', $result->getControllerInstanceName()); - $this->assertEquals('myMethod', $result->getMethodName()); - } - - public function testTrailingSlashUrl() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('toto/tata/', 'myController', 'myMethod', 'myTitle', 'fullComment', array('GET', 'POST')); - $splashUrlNode->registerCallback($callback); - - $result = $splashUrlNode->walk('toto/tata/', new ServerRequest([], [], 'toto/tata', 'GET')); - /* @var $result SplashRoute */ - $this->assertInstanceOf(SplashRoute::class, $result); - $this->assertEquals('myController', $result->getControllerInstanceName()); - $this->assertEquals('myMethod', $result->getMethodName()); - } - - public function testRootUrl() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('/', 'myController', 'myMethod', 'myTitle', 'fullComment', array('GET', 'POST')); - $splashUrlNode->registerCallback($callback); - - $result = $splashUrlNode->walk('/', new ServerRequest([], [], '/', 'GET')); - /* @var $result SplashRoute */ - $this->assertInstanceOf(SplashRoute::class, $result); - $this->assertEquals('myController', $result->getControllerInstanceName()); - $this->assertEquals('myMethod', $result->getMethodName()); - } - - public function testSameUrls() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('/', 'myController', 'myMethod', 'myTitle', 'fullComment', array('GET', 'POST')); - $splashUrlNode->registerCallback($callback); - $callback = new SplashRoute('/', 'myController', 'myMethod', 'myTitle', 'fullComment', array('GET', 'POST')); - - $this->expectException(SplashException::class); - $splashUrlNode->registerCallback($callback); - } - - /** - * - */ - public function testGlobalUrlCatchGet() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('/toto', 'myController', 'myMethod', 'myTitle', 'fullComment', array()); - $splashUrlNode->registerCallback($callback); - - $result = $splashUrlNode->walk('/toto', new ServerRequest([], [], '/toto', 'GET')); - /* @var $result SplashRoute */ - $this->assertInstanceOf(SplashRoute::class, $result); - $this->assertEquals('myController', $result->getControllerInstanceName()); - $this->assertEquals('myMethod', $result->getMethodName()); - } - - /** - * - */ - public function testMultiUrls() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('/toto', 'myControllerOk', 'myMethodOk', 'myTitle', 'fullComment', array()); - $splashUrlNode->registerCallback($callback); - $callback = new SplashRoute('/toto/tata', 'myController', 'myMethod', 'myTitle', 'fullComment', array()); - $splashUrlNode->registerCallback($callback); - $callback = new SplashRoute('/tata', 'myController', 'myMethod', 'myTitle', 'fullComment', array()); - $splashUrlNode->registerCallback($callback); - - $result = $splashUrlNode->walk('/toto', new ServerRequest([], [], '/toto', 'POST')); - /* @var $result SplashRoute */ - $this->assertInstanceOf(SplashRoute::class, $result); - $this->assertEquals('myControllerOk', $result->getControllerInstanceName()); - $this->assertEquals('myMethodOk', $result->getMethodName()); - } - - /** - * - */ - public function testParametersUrls() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('/toto/{var}/tata', 'myController', 'myMethod', 'myTitle', 'fullComment', []); - $splashUrlNode->registerCallback($callback); - - $result = $splashUrlNode->walk('/toto/12/tata', new ServerRequest([], [], '/toto/12/tata', 'POST')); - /* @var $result SplashRoute */ - $this->assertInstanceOf(SplashRoute::class, $result); - $this->assertEquals('myController', $result->getControllerInstanceName()); - $this->assertEquals('myMethod', $result->getMethodName()); - $this->assertEquals(12, $result->getFilledParameters()['var']); - } - - /** - * - */ - public function testWildcardUrls() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('/toto/*', 'myController', 'myMethod', 'myTitle', 'fullComment', array()); - $splashUrlNode->registerCallback($callback); - $callback2 = new SplashRoute('/toto/*', 'myControllerPost', 'myMethodPost', 'myTitle', 'fullComment', array('POST')); - $splashUrlNode->registerCallback($callback2); - - $result = $splashUrlNode->walk('/toto/tata/titi', new ServerRequest([], [], '/toto', 'GET')); - /* @var $result SplashRoute */ - $this->assertInstanceOf(SplashRoute::class, $result); - $this->assertEquals('myController', $result->getControllerInstanceName()); - $this->assertEquals('myMethod', $result->getMethodName()); - - $result = $splashUrlNode->walk('/toto/', new ServerRequest([], [], '/toto', 'GET')); - /* @var $result SplashRoute */ - $this->assertInstanceOf(SplashRoute::class, $result); - $this->assertEquals('myController', $result->getControllerInstanceName()); - $this->assertEquals('myMethod', $result->getMethodName()); - - // Now, let's test an URL with HTTP method set. - $result = $splashUrlNode->walk('/toto/tata/titi', new ServerRequest([], [], '/toto', 'POST')); - $this->assertEquals('myControllerPost', $result->getControllerInstanceName()); - $this->assertEquals('myMethodPost', $result->getMethodName()); - } - - public function testUnsupportedWildcard() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('toto/*/tata', 'myController', 'myMethod', 'myTitle', 'myComment'); - $this->expectException(SplashException::class); - $splashUrlNode->registerCallback($callback); - } - - public function testDoubleMethod() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('foo/bar', 'myController', 'myMethod', 'myTitle', 'myComment'); - $splashUrlNode->registerCallback($callback); - $callback2 = new SplashRoute('foo/bar', 'myController2', 'myMethod2', 'myTitle', 'myComment'); - $this->expectException(SplashException::class); - $splashUrlNode->registerCallback($callback2); - } - - public function testDoubleWildcardMethod() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('foo/*', 'myController', 'myMethod', 'myTitle', 'myComment'); - $splashUrlNode->registerCallback($callback); - $callback2 = new SplashRoute('foo/*', 'myController2', 'myMethod2', 'myTitle', 'myComment'); - $this->expectException(SplashException::class); - $splashUrlNode->registerCallback($callback2); - } - - public function testDoubleWildcardMethodWithHttpMethod() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('foo/*', 'myController', 'myMethod', 'myTitle', 'fullComment', ['GET', 'POST']); - $splashUrlNode->registerCallback($callback); - $callback2 = new SplashRoute('foo/*', 'myController2', 'myMethod2', 'myTitle', 'fullComment', ['GET']); - $this->expectException(SplashException::class); - $splashUrlNode->registerCallback($callback2); - } - - public function testDoubleParameter() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('foo/{var}/bar/{var}/', 'myController', 'myMethod', 'myTitle', 'myComment'); - $splashUrlNode->registerCallback($callback); - - $this->expectException(SplashException::class); - $splashUrlNode->walk('foo/12/bar/42/', new ServerRequest([], [], '/toto', 'GET')); - } - - public function testFallbackToWilcard() - { - $splashUrlNode = new SplashUrlNode(); - $callback = new SplashRoute('foo/bar/baz', 'myController', 'myMethod', 'myTitle', 'fullComment'); - $splashUrlNode->registerCallback($callback); - $callback2 = new SplashRoute('foo/*', 'myController2', 'myMethod2', 'myTitle', 'fullComment'); - $splashUrlNode->registerCallback($callback2); - - $result = $splashUrlNode->walk('foo/bar', new ServerRequest([], [], '/foo/bar/biz', 'POST')); - $this->assertEquals('myController2', $result->getControllerInstanceName()); - $this->assertEquals('myMethod2', $result->getMethodName()); - } -}