diff --git a/README.md b/README.md index 880963de..5fb4da84 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ *Begin developing PSR-7 middleware applications in minutes!* -**Note: This project is a work in progress. Don't use it in production!** - zend-expressive builds on [zend-stratigility](https://github.com/zendframework/zend-stratigility) to provide a minimalist PSR-7 middleware framework for PHP, with the following features: @@ -23,17 +21,50 @@ features: ## Installation -Install this library using composer: +We provide two ways to install Expressive, both using +[Composer](https://getcomposer.org): via our +[skeleton project and installer](https://github.com/zendframework/zend-expressive-skeleton], +or manually. + +### Using the skeleton + installer + +The simplest way to install and get started is using the skeleton project, which +includes installer scripts for choosing a router, dependency injection +container, and optionally a template renderer and/or error handler. The skeleton +also provides configuration for officially supported dependencies. + +To use the skeleton, use Composer's `create-project` command: + +```bash +$ composer create-project zendframework/zend-expressive-skeleton +``` + +This will prompt you through choosing your dependencies, and then create and +install the project in the `` (omitting the `` will +create and install in a `zend-expressive-skeleton/` directory). + +### Manual Composer installation + +You can install Expressive standalone using Composer: ```bash $ composer require zendframework/zend-expressive ``` -You will also need a router. We currently support: +However, at this point, Expressive is not usable, as you need to supply +minimally: -- [Aura.Router](https://github.com/auraphp/Aura.Router): `composer require aura/router` -- [FastRoute](https://github.com/nikic/FastRoute): `composer require nikic/fast-route` -- [ZF2 MVC Router](https://github.com/zendframework/zend-mvc): `composer require zendframework/zend-mvc` +- a router. +- a dependency injection container. + +We currently support and provide the following routing integrations: + +- [Aura.Router](https://github.com/auraphp/Aura.Router): + `composer require zendframework/zend-expressive-aurarouter` +- [FastRoute](https://github.com/nikic/FastRoute): + `composer require zendframework/zend-expressive-fastroute` +- [ZF2 MVC Router](https://github.com/zendframework/zend-mvc): + `composer require zendframework/zend-expressive-zendrouter` We recommend using a dependency injection container, and typehint against [container-interop](https://github.com/container-interop/container-interop). We @@ -43,6 +74,12 @@ can recommend the following implementations: `composer require zendframework/zend-servicemanager` - [pimple-interop](https://github.com/moufmouf/pimple-interop): `composer require mouf/pimple-interop` +- [Aura.Di](https://github.com/auraphp/Aura.Di): + `composer require aura/di:3.0.*@beta` + +Additionally, you may optionally want to install a template renderer +implementation, and/or an error handling integration. These are covered in the +documentation. ## Documentation diff --git a/doc/book/quick-start-skeleton.md b/doc/book/quick-start-skeleton.md new file mode 100644 index 00000000..a69f93ab --- /dev/null +++ b/doc/book/quick-start-skeleton.md @@ -0,0 +1,268 @@ +# Quick Start: Using the Skeleton + Installer + +The easiest way to get started with Expressive is to use the [skeleton +application and installer](https://github.com/zendframework/zend-expressive-skeleton). +The skeleton provides a generic structure for creating your applications, and +prompts you to choose a router, dependency injection container, template +renderer, and error handler from the outset. + +## 1. Create a new project + +First, we'll create a new project, using Composer's `create-project` command: + +```bash +$ composer create-project zendframework/zend-expressive-skeleton expressive +``` + +This will prompt you to choose: + +- A router. We recommend using the default, FastRoute. +- A dependency injection container. We recommend using the default, Zend + ServiceManager. +- A template renderer. You can ignore this when creating an API project, but if + you will be creating any HTML pages, we recommend installing one. We prefer + Plates. +- An error handler. Whoops is a very nice option for development, as it gives + you extensive, browseable information for exceptions and errors raised. + +## 2. Start a web server + +The Skeleton + Installer creates a full application structure that's ready-to-go +when complete. You can test it out using [built-in web +server](http://php.net/manual/en/features.commandline.webserver.php). + +From the project root directory, execute the following: + +```bash +$ php -S 0.0.0.0:8080 -t public/ +``` + +This starts up a web server on localhost port 8080; browse to +http://localhost:8080/ to see if your application responds correctly! + +## Next Steps + +The skeleton makes the assumption that you will be writing your middleware as +classes, and using configuration to map routes to middleware. It also provides a +default structure for templates, if you choose to use them. Let's see how you +can create first vanilla middleware, and then templated middleware. + +### Creating middleware + +The easiest way to create middleware is to create a class that defines an +`__invoke()` method accepting a request, response, and callable "next" argument +(for invoking the "next" middleware in the queue). The skeleton defines an `App` +namespace for you, and suggests placing middleware under the namespace +`App\Action`. + +Let's create a "Hello" action. Place the following in +`src/Action/HelloAction.php`: + +```php +getQueryParams(); + $target = isset($query['target']) ? $query['target'] : 'World'; + $target = htmlspecialchars($target, ENT_HTML5, UTF-8); + + $response->getBody()->write(sprintf( + '

Hello, %s!

', + $target + )); + return $response->withHeader('Content-Type', 'text/html'); + } +} +``` + +The above looks for a query string parameter "target", and uses its value to +provide a message, which is then returned in an HTML response. + +Now we need to inform the application of this middleware, and indicate what +path will invoke it. Open the file `config/autoload/routes.global.php`. Inside +that file, you should have a structure similar to the following: + +```php +return [ + 'dependencies' => [ + /* ... */ + ], + 'routes' => [ + /* ... */ + ], +]; +``` + +We're going to add an entry under `routes`: + +```php +return [ + /* ... */ + 'routes' => [ + /* ... */ + [ + 'name' => 'hello', + 'path' => '/hello', + 'middleware' => App\Action\HelloAction::class, + 'allowed_methods' => ['GET'], + ], + ], +]; +``` + +Once you've added the above entry, give it a try by going to each of the +following URIs: + +- http://localhost:8080/hello +- http://localhost:8080/hello?target=ME + +You should see the message change as you go between the two URIs! + +### Using templates + +You likely don't want to hardcode HTML into your middleware; so, let's use +templates. This particular exercise assumes you chose to use the +[Plates](http://platesphp.com) integration. + +Templates are installed under the `templates/` subdirectory. By default, we also +register the template namespace `app` to correspond with the `templates/app` +subdirectory. Create the file `templates/app/hello-world.phtml` with the +following contents: + +```php +layout('layout::default', ['title' => 'Greetings']) ?> + +

Hello, e($target) ?>

+``` + +Now that we have a template, we need to: + +- Inject a renderer into our action class. +- Use the renderer to render the contents. + +Replace your `src/Action/HelloAction.php` file with the following contents: + +```php +renderer = $renderer; + } + + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) + { + $query = $request->getQueryParams(); + $target = isset($query['target']) ? $query['target'] : 'World'; + + return new HtmlResponse( + $this->renderer->render('app::hello-world', ['target' => $target]) + ); + } +} +``` + +The above modifies the class to accept a renderer to the constructor, and then +calls on it to render a template. A few things to note: + +- We no longer need to escape our target; the template takes care of that for us. +- We're using a specific response type here, from + [Diactoros](https://github.com/zendframework/zend-diactoros), which is the + default PSR-7 implementation Expressive uses. This response type simplifies + our response creation. + +How does the template renderer get into the action, however? The answer is +dependency injection. + +For the next part of the example, we'll be creating and wiring a factory for +creating the `HelloAction` instance; the example assumes you used the default +selection for a dependency injection container, Zend ServiceManager. + +Let's create a factory. Create the file `src/Action/HelloActionFactory.php` with +the following contents: + +```php +get(TemplateRendererInterface::class) + ); + } +} +``` + +With that in place, we'll now update our configuration. Open the file +`config/autoload/dependencies.global.php`; it should have a structure similar to +the following: + +```php +return [ + 'dependencies' => [ + 'invokables' => [ + /* ... */ + ], + 'factories' => [ + /* ... */ + ], + ], +]; +``` + +We're going to tell our application that we have a _factory_ for our +`HelloAction` class: + +```php +return [ + 'dependencies' => [ + /* ... */ + 'factories' => [ + /* ... */ + App\Action\HelloAction::class => App\Action\HelloActionFactory::class, + ], + ], +]; +``` + +Save that file, and now re-visit the URIs: + +- http://localhost:8080/hello +- http://localhost:8080/hello?target=ME + +Your page should now have the same layout as the landing page of the skeleton +application! + +## Congratulations! + +Congratulations! You've now created your application, and started writing +middleware! It's time to start learning about the rest of the features of +Expressive: + +- [Containers](container/intro.md) +- [Routing](router/intro.md) +- [Templating](template/intro.md) +- [Error Handling](error-handling.md) diff --git a/doc/book/quick-start.md b/doc/book/quick-start.md index ed224b45..2eb5bbfa 100644 --- a/doc/book/quick-start.md +++ b/doc/book/quick-start.md @@ -1,4 +1,4 @@ -# Quick Start +# Quick Start: Standalone Usage Expressive allows you to get started at your own pace. You can start with the simplest example, detailed below, or move on to a more structured, diff --git a/doc/bookdown.json b/doc/bookdown.json index e40ea7f6..2b55e223 100644 --- a/doc/bookdown.json +++ b/doc/bookdown.json @@ -3,7 +3,8 @@ "content": [ {"Intro": "../README.md"}, {"Overview and Features": "book/features.md"}, - {"Quick Start": "book/quick-start.md"}, + {"Quick Start: Skeleton + Installer": "book/quick-start-skeleton.md"}, + {"Quick Start: Standalone": "book/quick-start.md"}, {"Applications": "book/application.md"}, "book/container/bookdown.json", "book/router/bookdown.json", diff --git a/mkdocs.yml b/mkdocs.yml index 412ed22f..5ddefd18 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,7 +3,8 @@ site_dir: doc/html pages: - index.md - { 'Overview and Features': features.md } - - { 'Quick Start': quick-start.md } + - { 'Quick Start: Skeleton + Installer': quick-start-skeleton.md } + - { 'Quick Start: Standalone': quick-start.md } - { Applications: application.md } - { Containers: [{ Introduction: container/intro.md }, { 'Container Factories': container/factories.md }, { 'Using zend-servicemanager': container/zend-servicemanager.md }, { 'Using Pimple': container/pimple.md }, { 'Using Aura.Di': container/aura-di.md }] } - { 'Routing Adapters': [{ Introduction: router/intro.md }, { 'Routing Interface': router/interface.md }, { 'URI Generation': router/uri-generation.md }, { 'Routing vs Piping': router/piping.md }, { 'Using Aura': router/aura.md }, { 'Using FastRoute': router/fast-route.md }, { 'Using the ZF2 Router': router/zf2.md }] }