From d0b5c54017df184e13de2e6af59fda03603ca54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 23 Apr 2021 13:40:44 +0200 Subject: [PATCH 1/9] Create basic documentation structure from README --- README.md | 252 +-------------------- docs/01-getting-started/01-quickstart.md | 26 +++ docs/01-getting-started/02-installation.md | 25 ++ docs/01-getting-started/03-deployment.md | 32 +++ docs/02-best-practices/01-controllers.md | 136 +++++++++++ docs/02-best-practices/02-testing.md | 14 ++ docs/03-api/01-app.md | 10 + docs/03-api/02-request.md | 3 + docs/03-api/03-response.md | 3 + docs/03-api/04-middleware.md | 3 + docs/04-async/01-promises.md | 6 + docs/04-async/02-coroutines.md | 6 + docs/04-async/03-fibers.md | 9 + docs/04-async/04-streaming.md | 19 ++ docs/04-async/05-child-processes.md | 6 + docs/05-integrations/01-database.md | 8 + docs/05-integrations/02-filesystem.md | 6 + docs/05-integrations/03-authentication.md | 5 + docs/05-integrations/04-sessions.md | 5 + docs/05-integrations/05-templates.md | 4 + docs/05-integrations/06-queuing.md | 4 + docs/06-more/01-philosophy.md | 8 + docs/06-more/02-architecture.md | 7 + docs/06-more/03-community.md | 14 ++ 24 files changed, 367 insertions(+), 244 deletions(-) create mode 100644 docs/01-getting-started/01-quickstart.md create mode 100644 docs/01-getting-started/02-installation.md create mode 100644 docs/01-getting-started/03-deployment.md create mode 100644 docs/02-best-practices/01-controllers.md create mode 100644 docs/02-best-practices/02-testing.md create mode 100644 docs/03-api/01-app.md create mode 100644 docs/03-api/02-request.md create mode 100644 docs/03-api/03-response.md create mode 100644 docs/03-api/04-middleware.md create mode 100644 docs/04-async/01-promises.md create mode 100644 docs/04-async/02-coroutines.md create mode 100644 docs/04-async/03-fibers.md create mode 100644 docs/04-async/04-streaming.md create mode 100644 docs/04-async/05-child-processes.md create mode 100644 docs/05-integrations/01-database.md create mode 100644 docs/05-integrations/02-filesystem.md create mode 100644 docs/05-integrations/03-authentication.md create mode 100644 docs/05-integrations/04-sessions.md create mode 100644 docs/05-integrations/05-templates.md create mode 100644 docs/05-integrations/06-queuing.md create mode 100644 docs/06-more/01-philosophy.md create mode 100644 docs/06-more/02-architecture.md create mode 100644 docs/06-more/03-community.md diff --git a/README.md b/README.md index 29c1f08..4f857ac 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# FrugalPHP +# Frugal [![CI status](https://github.com/clue/frugalphp-incubator/workflows/CI/badge.svg)](https://github.com/clue/frugalphp-incubator/actions) @@ -10,21 +10,7 @@ Lightweight microframework for fast, event-driven and async-first web applicatio Take a look at https://ninenines.eu/docs/en/cowboy/2.6/guide/modern_web/ * [Quickstart](#quickstart) -* [Basics](#basics) - * [Installation](#installation) - * [Structure your app (Controllers)](#structure-your-app-controllers) - * [Testing your app](#testing-your-app) - * [Deployment](#deployment) -* [Usage](#usage) - * [App](#app) - * [Request](#request) - * [Response](#response) - * [Database](#database) - * [Filesystem](#filesystem) - * [Authentication](#authentication) - * [Sessions](#sessions) - * [Templates](#templates) - * [Queuing](#queuing) +* [Documentation](#documentation) * [Tests](#tests) * [License](#license) @@ -38,7 +24,7 @@ First manually change your `composer.json` to include these lines: "repositories": [ { "type": "vcs", - "url": "https://github.com/clue/frugalphp" + "url": "https://github.com/clue-engineering/frugal" } ] } @@ -49,7 +35,7 @@ First manually change your `composer.json` to include these lines: Simply install FrugalPHP: ```bash -$ composer require clue/frugal:dev-master +$ composer require clue/frugal:dev-main ``` > TODO: Tagged release. @@ -98,232 +84,10 @@ HTTP/1.1 200 OK Hello wörld! ``` -## Basics +## Documentation -### Installation - -* Runs everywhere -* Requires only PHP 7.1+, no extensions required -* Can run behind existing web servers or locally with built-in webserver (see deployment) - -[…] - -### Structure your app (Controllers) - -Once everything is up and running, we can take a look at how to best structure -our actual web application. - -To get started, it's often easiest to start with simple closure definitions -like the following: - -```php -get('/', function () { - return new React\Http\Message\Response( - 200, - [], - "Hello wörld!\n" - ); -}); - -$app->get('/users/{name}', function (Psr\Http\Message\ServerRequestInterface $request) { - return new React\Http\Message\Response( - 200, - [], - "Hello " . $request->getAttribute('name') . "!\n" - ); -}); - -$loop->run(); -``` - -While easy to get started, this will easily get out of hand for more complex -business domains when you have more than a couple of routes registered. - -For real-world applications, we highly recommend structuring your application -into invidividual controller classes. This way, we can break up the above -definition into three even simpler files: - -```php -# main.php -get('/', new Acme\Todo\HelloController()); -$app->get('/users/{name}', new Acme\Todo\UserController()); - -$loop->run(); -``` - -```php -# src/HelloController.php -getAttribute('name') . "!\n" - ); - } -} -``` - -Doesn't look too complex, right? Now, we only need to tell Composer's autoloader -about our vendor namespace `Acme\\Todo` in the `src/` folder. Make sure to include -the following lines in your `composer.json` file: - -```json -{ - "autoload": { - "psr-4": { - "Acme\\Todo\\": "src/" - } - } -} -``` - -When we're doing this the first time, we have to update Composer's generated -autoloader classes: - -```bash -$ composer dump-autoload -``` - -Don't worry, that's a one-time setup only. If you're used to working with -Composer, this shouldn't be too surprising. If this sounds new to you, rest -assured this is the only time you have to worry about this, new classes can -simply be added without having to run Composer again. - -Again, let's see our web application still works by using your favorite -webbrowser or command line tool: - -```bash -$ curl -v http://localhost:8080/ -HTTP/1.1 200 OK -… - -Hello wörld! -``` - -### Testing your app - -**We ❤️ TDD and DDD!** - -New to testing your web application? While we don't want to *force* you to test -your app, we want to emphasize the importance of automated test suits and try hard -to make testing your web application as easy as possible. - -Once your app is structured into dedicated controller classes as per the previous -chapter, […] - -> TODO: PHPUnit setup basics and first test cases. - -> TODO: Higher-level functional tests. - -### Deployment - -Runs everywhere: - -* Built-in webserver -* nginx with PHP-FPM -* Apache with mod_fcgid and PHP-FPM -* Apache with mod_php -* PHP's development webserver - -[…] - -## Usage - -### App - -* Batteries included, but swappable -* Providing HTTP routing (RESTful applications) - -### Request - -* PSR-7 - -### Response - -* PSR-7 - -### Database - -* Async -* No PDO, no Doctrine and family -* Easy to spot, harder to replace -* MySQL, Postgres and SQLite supported -* ORM -* Redis - -### Filesystem - -* Async -* No `fopen()`, `file_get_contents()` and family -* Easy to overlook -* Few blocking calls *can* be acceptable - -### Authentication - -* Basic auth easy -* HTTP middleware better -* JWT and oauth possible - -### Sessions - -* Built-in (or module?) -* HTTP middleware -* Persistence via database/ORM or other mechanism? - -### Templates - -* Any template language possible -* Twig recommended? - -### Queuing - -* Built-in (or module?) -* Redis built-in, but swappable with real instance (constraints?) +Hooked? +See [full documentation](docs/) for more details. ## Tests @@ -342,7 +106,7 @@ $ php vendor/bin/phpunit Additionally, you can run some simple acceptance tests to verify the framework examples work as expected behind your web server. Use your web server of choice -(see [deployment](#deployment)) and execute the tests with the URL to your +(see deployment documentation) and execute the tests with the URL to your installation like this: ```bash diff --git a/docs/01-getting-started/01-quickstart.md b/docs/01-getting-started/01-quickstart.md new file mode 100644 index 0000000..bdde66f --- /dev/null +++ b/docs/01-getting-started/01-quickstart.md @@ -0,0 +1,26 @@ +# Quickstart in 5 minutes + +Getting started with X is easy! +You can use this example to get started with a new `app.php` file: + +```php +get('/', function () { + return new React\Http\Message\Response( + 200, + [], + "Hello wörld!\n" + ); +}); + +$loop->run(); +``` + +That's it already! +The next step is now to install its dependencies and serve this web application. diff --git a/docs/01-getting-started/02-installation.md b/docs/01-getting-started/02-installation.md new file mode 100644 index 0000000..e0156a2 --- /dev/null +++ b/docs/01-getting-started/02-installation.md @@ -0,0 +1,25 @@ +# Installation + +First manually change your `composer.json` to include these lines: + +```json +{ + …, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/clue-engineering/frugal" + } + ] +} +``` + +> TODO: Change package name and register on Packagist. + +Then, simply install: + +```bash +$ composer require clue/frugal:dev-main +``` + +> TODO: Tagged release. diff --git a/docs/01-getting-started/03-deployment.md b/docs/01-getting-started/03-deployment.md new file mode 100644 index 0000000..3ec2555 --- /dev/null +++ b/docs/01-getting-started/03-deployment.md @@ -0,0 +1,32 @@ +# Deployment + +One of the nice properties of this project is that is works both behind +traditional web server setups as well as in a stand-alone environment. + +For example, you can run the above example using PHP's built-in webserver for +testing purposes like this: + +```bash +$ php -S 0.0.0.0:8080 app.php +``` + +You can now use your favorite webbrowser or command line tool to check your web +application responds as expected: + +```bash +$ curl -v http://localhost:8080/ +HTTP/1.1 200 OK +… + +Hello wörld! +``` + +Runs everywhere: + +* Built-in webserver +* nginx with PHP-FPM +* Apache with mod_fcgid and PHP-FPM +* Apache with mod_php +* PHP's development webserver + +> TODO: Show different deployment scenarios diff --git a/docs/02-best-practices/01-controllers.md b/docs/02-best-practices/01-controllers.md new file mode 100644 index 0000000..15ac9a4 --- /dev/null +++ b/docs/02-best-practices/01-controllers.md @@ -0,0 +1,136 @@ +### Controller classes to structure your app + +Once everything is up and running, we can take a look at how to best structure +our actual web application. + +To get started, it's often easiest to start with simple closure definitions +like the following: + +```php +get('/', function () { + return new React\Http\Message\Response( + 200, + [], + "Hello wörld!\n" + ); +}); + +$app->get('/users/{name}', function (Psr\Http\Message\ServerRequestInterface $request) { + return new React\Http\Message\Response( + 200, + [], + "Hello " . $request->getAttribute('name') . "!\n" + ); +}); + +$loop->run(); +``` + +While easy to get started, this will easily get out of hand for more complex +business domains when you have more than a couple of routes registered. + +For real-world applications, we highly recommend structuring your application +into invidividual controller classes. This way, we can break up the above +definition into three even simpler files: + +```php +# main.php +get('/', new Acme\Todo\HelloController()); +$app->get('/users/{name}', new Acme\Todo\UserController()); + +$loop->run(); +``` + +```php +# src/HelloController.php +getAttribute('name') . "!\n" + ); + } +} +``` + +Doesn't look too complex, right? Now, we only need to tell Composer's autoloader +about our vendor namespace `Acme\\Todo` in the `src/` folder. Make sure to include +the following lines in your `composer.json` file: + +```json +{ + "autoload": { + "psr-4": { + "Acme\\Todo\\": "src/" + } + } +} +``` + +When we're doing this the first time, we have to update Composer's generated +autoloader classes: + +```bash +$ composer dump-autoload +``` + +Don't worry, that's a one-time setup only. If you're used to working with +Composer, this shouldn't be too surprising. If this sounds new to you, rest +assured this is the only time you have to worry about this, new classes can +simply be added without having to run Composer again. + +Again, let's see our web application still works by using your favorite +webbrowser or command line tool: + +```bash +$ curl -v http://localhost:8080/ +HTTP/1.1 200 OK +… + +Hello wörld! +``` diff --git a/docs/02-best-practices/02-testing.md b/docs/02-best-practices/02-testing.md new file mode 100644 index 0000000..837d386 --- /dev/null +++ b/docs/02-best-practices/02-testing.md @@ -0,0 +1,14 @@ +# Testing + +**We ❤️ TDD and DDD!** + +New to testing your web application? While we don't want to *force* you to test +your app, we want to emphasize the importance of automated test suits and try hard +to make testing your web application as easy as possible. + +Once your app is structured into dedicated controller classes as per the previous +chapter, […] + +> TODO: PHPUnit setup basics and first test cases. + +> TODO: Higher-level functional tests. diff --git a/docs/03-api/01-app.md b/docs/03-api/01-app.md new file mode 100644 index 0000000..43f22dd --- /dev/null +++ b/docs/03-api/01-app.md @@ -0,0 +1,10 @@ +# App + +* Batteries included, but swappable +* Providing HTTP routing (RESTful applications) + +## Routing + +## Middleware + +See [middleware documentation](../04-middleware.md). diff --git a/docs/03-api/02-request.md b/docs/03-api/02-request.md new file mode 100644 index 0000000..7365378 --- /dev/null +++ b/docs/03-api/02-request.md @@ -0,0 +1,3 @@ +# Request + +* PSR-7 diff --git a/docs/03-api/03-response.md b/docs/03-api/03-response.md new file mode 100644 index 0000000..34b5075 --- /dev/null +++ b/docs/03-api/03-response.md @@ -0,0 +1,3 @@ +# Response + +* PSR-7 diff --git a/docs/03-api/04-middleware.md b/docs/03-api/04-middleware.md new file mode 100644 index 0000000..107581e --- /dev/null +++ b/docs/03-api/04-middleware.md @@ -0,0 +1,3 @@ +# Middleware + +> Under active development, stay tuned! diff --git a/docs/04-async/01-promises.md b/docs/04-async/01-promises.md new file mode 100644 index 0000000..b21d403 --- /dev/null +++ b/docs/04-async/01-promises.md @@ -0,0 +1,6 @@ +# Promises + +* Avoid blocking +* Deferred execution +* Concurrent execution (more efficient than parallel threads / multithreading) +* API can be a bit harder (see Coroutines or Fibers) diff --git a/docs/04-async/02-coroutines.md b/docs/04-async/02-coroutines.md new file mode 100644 index 0000000..51e86b2 --- /dev/null +++ b/docs/04-async/02-coroutines.md @@ -0,0 +1,6 @@ +# Coroutines + +* Promises can be hard due to nested callbacks +* Generator-based coroutines provided +* Synchronous code structure, yet asynchronous execution +* Generators can be a bit harder to understand, see [fibers](03-fibers.md). diff --git a/docs/04-async/03-fibers.md b/docs/04-async/03-fibers.md new file mode 100644 index 0000000..ac66712 --- /dev/null +++ b/docs/04-async/03-fibers.md @@ -0,0 +1,9 @@ +# Fibers + +> Hot topic for PHP 8.1 + +* Async APIs that look just like their synchronous counterparts +* Much easier to integrate, possibly larger ecosystem in future +* Requires PHP 8.1 (November 2021), adoption will take time +* Promises still required for concurrent execution +* Promises and coroutines work just fine until ecosystem matures diff --git a/docs/04-async/04-streaming.md b/docs/04-async/04-streaming.md new file mode 100644 index 0000000..6be26ac --- /dev/null +++ b/docs/04-async/04-streaming.md @@ -0,0 +1,19 @@ +# Streaming + +Processing large amounts of data or when data arrives at future time + +## Streaming downloads + +* Efficient processing of large files without keeping contents in memory +* Similar for video streaming, but not scope of this project + +## EventSource + +* HTML5 Server-Sent Events (SSE) aka. EventSource supported out-of-the-box +* Live streaming, live data, realtime communication + +## WebSockets + +* HTML5 WebSockets integration supported with Ratchet +* See also EventSource as alternative +* Bidirectional communication diff --git a/docs/04-async/05-child-processes.md b/docs/04-async/05-child-processes.md new file mode 100644 index 0000000..e5d7360 --- /dev/null +++ b/docs/04-async/05-child-processes.md @@ -0,0 +1,6 @@ +# Parallel processing with child processes + +* Avoid blocking by moving blocking implementation to child process +* Child process I/O for communication +* Multithreading +* PQ diff --git a/docs/05-integrations/01-database.md b/docs/05-integrations/01-database.md new file mode 100644 index 0000000..a7cf3fa --- /dev/null +++ b/docs/05-integrations/01-database.md @@ -0,0 +1,8 @@ +# Database + +* Async +* No PDO, no Doctrine and family +* Easy to spot, harder to replace +* MySQL, Postgres and SQLite supported +* ORM +* Redis diff --git a/docs/05-integrations/02-filesystem.md b/docs/05-integrations/02-filesystem.md new file mode 100644 index 0000000..e1c0de9 --- /dev/null +++ b/docs/05-integrations/02-filesystem.md @@ -0,0 +1,6 @@ +# Filesystem + +* Async +* No `fopen()`, `file_get_contents()` and family +* Easy to overlook +* Few blocking calls *can* be acceptable diff --git a/docs/05-integrations/03-authentication.md b/docs/05-integrations/03-authentication.md new file mode 100644 index 0000000..4d398ca --- /dev/null +++ b/docs/05-integrations/03-authentication.md @@ -0,0 +1,5 @@ +# Authentication + +* Basic auth easy +* HTTP middleware better +* JWT and oauth possible diff --git a/docs/05-integrations/04-sessions.md b/docs/05-integrations/04-sessions.md new file mode 100644 index 0000000..43ddfe6 --- /dev/null +++ b/docs/05-integrations/04-sessions.md @@ -0,0 +1,5 @@ +# Sessions + +* Built-in (or module?) +* HTTP middleware +* Persistence via database/ORM or other mechanism? diff --git a/docs/05-integrations/05-templates.md b/docs/05-integrations/05-templates.md new file mode 100644 index 0000000..f19a7d5 --- /dev/null +++ b/docs/05-integrations/05-templates.md @@ -0,0 +1,4 @@ +# Templates + +* Any template language possible +* Twig recommended? diff --git a/docs/05-integrations/06-queuing.md b/docs/05-integrations/06-queuing.md new file mode 100644 index 0000000..7fa9aef --- /dev/null +++ b/docs/05-integrations/06-queuing.md @@ -0,0 +1,4 @@ +# Queuing + +* Built-in (or module?) +* Redis built-in, but swappable with real instance (constraints?) diff --git a/docs/06-more/01-philosophy.md b/docs/06-more/01-philosophy.md new file mode 100644 index 0000000..035914f --- /dev/null +++ b/docs/06-more/01-philosophy.md @@ -0,0 +1,8 @@ +# Our philosophy + +* Motto: make easy things easy & hard things possible +* From quick prototyping (RAD) to production environment in hours +* Batteries included, but swappable +* Reuse where applicable +* LTS + careful upgrades +* Runs everywhere diff --git a/docs/06-more/02-architecture.md b/docs/06-more/02-architecture.md new file mode 100644 index 0000000..9a13894 --- /dev/null +++ b/docs/06-more/02-architecture.md @@ -0,0 +1,7 @@ +# Architecture + +## PHP + +## ReactPHP + +## Async PHP diff --git a/docs/06-more/03-community.md b/docs/06-more/03-community.md new file mode 100644 index 0000000..9c404e6 --- /dev/null +++ b/docs/06-more/03-community.md @@ -0,0 +1,14 @@ +# Community + +Framework X will be released as open-source under the permissive MIT license. +This means it will be free as in free speech and as in free beer. + +We believe in open source and made a conscious decision to take this path. +Being open-source means we can foster a community to focus on building the best possible framework together. +Framework X builds on top of existing open-source projects and we want to give back to this community of awesome engineers and developers. +Being open to outside contributions means we can guarantee interoperability with a vivid ecosystem and ensure the longevity of the project. + +* Twitter @x_framework +* GitHub discussions +* Support chat +* GitHub sponsors From 9568f94c0e71988dcaffbbab1001549e8cb8bfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Mon, 26 Apr 2021 14:27:42 +0200 Subject: [PATCH 2/9] Use mkdocs-material to render pretty HTML documentation --- .gitignore | 3 +- README.md | 24 ++++++++++ docs/{03-api/01-app.md => api/app.md} | 0 .../04-middleware.md => api/middleware.md} | 0 docs/{03-api/02-request.md => api/request.md} | 0 .../03-response.md => api/response.md} | 0 .../child-processes.md} | 0 .../02-coroutines.md => async/coroutines.md} | 0 .../03-fibers.md => async/fibers.md} | 0 .../01-promises.md => async/promises.md} | 0 .../04-streaming.md => async/streaming.md} | 0 .../controllers.md} | 2 +- .../testing.md} | 0 .../deployment.md} | 0 .../installation.md} | 0 .../quickstart.md} | 0 .../authentication.md} | 0 .../database.md} | 0 .../filesystem.md} | 0 .../06-queuing.md => integrations/queuing.md} | 0 .../sessions.md} | 0 .../templates.md} | 0 .../architecture.md} | 0 .../03-community.md => more/community.md} | 0 .../01-philosophy.md => more/philosophy.md} | 0 mkdocs.yml | 46 +++++++++++++++++++ 26 files changed, 73 insertions(+), 2 deletions(-) rename docs/{03-api/01-app.md => api/app.md} (100%) rename docs/{03-api/04-middleware.md => api/middleware.md} (100%) rename docs/{03-api/02-request.md => api/request.md} (100%) rename docs/{03-api/03-response.md => api/response.md} (100%) rename docs/{04-async/05-child-processes.md => async/child-processes.md} (100%) rename docs/{04-async/02-coroutines.md => async/coroutines.md} (100%) rename docs/{04-async/03-fibers.md => async/fibers.md} (100%) rename docs/{04-async/01-promises.md => async/promises.md} (100%) rename docs/{04-async/04-streaming.md => async/streaming.md} (100%) rename docs/{02-best-practices/01-controllers.md => best-practices/controllers.md} (98%) rename docs/{02-best-practices/02-testing.md => best-practices/testing.md} (100%) rename docs/{01-getting-started/03-deployment.md => getting-started/deployment.md} (100%) rename docs/{01-getting-started/02-installation.md => getting-started/installation.md} (100%) rename docs/{01-getting-started/01-quickstart.md => getting-started/quickstart.md} (100%) rename docs/{05-integrations/03-authentication.md => integrations/authentication.md} (100%) rename docs/{05-integrations/01-database.md => integrations/database.md} (100%) rename docs/{05-integrations/02-filesystem.md => integrations/filesystem.md} (100%) rename docs/{05-integrations/06-queuing.md => integrations/queuing.md} (100%) rename docs/{05-integrations/04-sessions.md => integrations/sessions.md} (100%) rename docs/{05-integrations/05-templates.md => integrations/templates.md} (100%) rename docs/{06-more/02-architecture.md => more/architecture.md} (100%) rename docs/{06-more/03-community.md => more/community.md} (100%) rename docs/{06-more/01-philosophy.md => more/philosophy.md} (100%) create mode 100644 mkdocs.yml diff --git a/.gitignore b/.gitignore index 4fbb073..e26c29e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -/vendor/ +/build/ /composer.lock +/vendor/ diff --git a/README.md b/README.md index 4f857ac..042ae05 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,30 @@ Hello wörld! Hooked? See [full documentation](docs/) for more details. +> We use [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) to +> render our documentation to a pretty HTML version. +> +> If you want to contribute to the documentation, it's easiest to just run +> this in a Docker container like this: +> +> ```bash +> $ docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material +> ``` +> +> You can access the documentation via `http://localhost:8000`. +> If you want to generate a static HTML folder for deployment, you can again +> use a Docker container like this: +> +> ```bash +> $ docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material build +> ``` +> +> The resulting `build/docs/` should then be deployed behind a web server (tbd). +> If you want to add a new documentation file and/or change the page order, make sure the [`mkdocs.yml`](mkdocs.yml) +> file contains an up-to-date list of all pages. +> +> Happy hacking! + ## Tests To run the test suite, you first need to clone this repo and then install all diff --git a/docs/03-api/01-app.md b/docs/api/app.md similarity index 100% rename from docs/03-api/01-app.md rename to docs/api/app.md diff --git a/docs/03-api/04-middleware.md b/docs/api/middleware.md similarity index 100% rename from docs/03-api/04-middleware.md rename to docs/api/middleware.md diff --git a/docs/03-api/02-request.md b/docs/api/request.md similarity index 100% rename from docs/03-api/02-request.md rename to docs/api/request.md diff --git a/docs/03-api/03-response.md b/docs/api/response.md similarity index 100% rename from docs/03-api/03-response.md rename to docs/api/response.md diff --git a/docs/04-async/05-child-processes.md b/docs/async/child-processes.md similarity index 100% rename from docs/04-async/05-child-processes.md rename to docs/async/child-processes.md diff --git a/docs/04-async/02-coroutines.md b/docs/async/coroutines.md similarity index 100% rename from docs/04-async/02-coroutines.md rename to docs/async/coroutines.md diff --git a/docs/04-async/03-fibers.md b/docs/async/fibers.md similarity index 100% rename from docs/04-async/03-fibers.md rename to docs/async/fibers.md diff --git a/docs/04-async/01-promises.md b/docs/async/promises.md similarity index 100% rename from docs/04-async/01-promises.md rename to docs/async/promises.md diff --git a/docs/04-async/04-streaming.md b/docs/async/streaming.md similarity index 100% rename from docs/04-async/04-streaming.md rename to docs/async/streaming.md diff --git a/docs/02-best-practices/01-controllers.md b/docs/best-practices/controllers.md similarity index 98% rename from docs/02-best-practices/01-controllers.md rename to docs/best-practices/controllers.md index 15ac9a4..1b7148e 100644 --- a/docs/02-best-practices/01-controllers.md +++ b/docs/best-practices/controllers.md @@ -1,4 +1,4 @@ -### Controller classes to structure your app +# Controller classes to structure your app Once everything is up and running, we can take a look at how to best structure our actual web application. diff --git a/docs/02-best-practices/02-testing.md b/docs/best-practices/testing.md similarity index 100% rename from docs/02-best-practices/02-testing.md rename to docs/best-practices/testing.md diff --git a/docs/01-getting-started/03-deployment.md b/docs/getting-started/deployment.md similarity index 100% rename from docs/01-getting-started/03-deployment.md rename to docs/getting-started/deployment.md diff --git a/docs/01-getting-started/02-installation.md b/docs/getting-started/installation.md similarity index 100% rename from docs/01-getting-started/02-installation.md rename to docs/getting-started/installation.md diff --git a/docs/01-getting-started/01-quickstart.md b/docs/getting-started/quickstart.md similarity index 100% rename from docs/01-getting-started/01-quickstart.md rename to docs/getting-started/quickstart.md diff --git a/docs/05-integrations/03-authentication.md b/docs/integrations/authentication.md similarity index 100% rename from docs/05-integrations/03-authentication.md rename to docs/integrations/authentication.md diff --git a/docs/05-integrations/01-database.md b/docs/integrations/database.md similarity index 100% rename from docs/05-integrations/01-database.md rename to docs/integrations/database.md diff --git a/docs/05-integrations/02-filesystem.md b/docs/integrations/filesystem.md similarity index 100% rename from docs/05-integrations/02-filesystem.md rename to docs/integrations/filesystem.md diff --git a/docs/05-integrations/06-queuing.md b/docs/integrations/queuing.md similarity index 100% rename from docs/05-integrations/06-queuing.md rename to docs/integrations/queuing.md diff --git a/docs/05-integrations/04-sessions.md b/docs/integrations/sessions.md similarity index 100% rename from docs/05-integrations/04-sessions.md rename to docs/integrations/sessions.md diff --git a/docs/05-integrations/05-templates.md b/docs/integrations/templates.md similarity index 100% rename from docs/05-integrations/05-templates.md rename to docs/integrations/templates.md diff --git a/docs/06-more/02-architecture.md b/docs/more/architecture.md similarity index 100% rename from docs/06-more/02-architecture.md rename to docs/more/architecture.md diff --git a/docs/06-more/03-community.md b/docs/more/community.md similarity index 100% rename from docs/06-more/03-community.md rename to docs/more/community.md diff --git a/docs/06-more/01-philosophy.md b/docs/more/philosophy.md similarity index 100% rename from docs/06-more/01-philosophy.md rename to docs/more/philosophy.md diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..ae16067 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,46 @@ +site_name: Documentation +site_dir: build/docs +extra: + homepage: ../ + +theme: + name: material + features: + - navigation.sections + +markdown_extensions: + - pymdownx.highlight + - pymdownx.superfences + - toc: + permalink: true + +nav: + - Getting Started: + - getting-started/quickstart.md + - getting-started/installation.md + - getting-started/deployment.md + - Best Practices: + - best-practices/controllers.md + - best-practices/testing.md + - API: + - api/app.md + - api/middleware.md + - api/request.md + - api/response.md + - Async: + - async/promises.md + - async/coroutines.md + - async/fibers.md + - async/streaming.md + - async/child-processes.md + - Integrations: + - integrations/database.md + - integrations/filesystem.md + - integrations/authentication.md + - integrations/sessions.md + - integrations/templates.md + - integrations/queuing.md + - More: + - more/philosophy.md + - more/architecture.md + - more/community.md From 88dc524af371b75ab2903326354ea8397502e8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Tue, 27 Apr 2021 14:35:45 +0200 Subject: [PATCH 3/9] Completed quickstart guide --- .../deployment.md | 2 +- docs/getting-started/installation.md | 25 ----- docs/getting-started/quickstart.md | 96 ++++++++++++++++++- mkdocs.yml | 4 +- 4 files changed, 95 insertions(+), 32 deletions(-) rename docs/{getting-started => best-practices}/deployment.md (96%) delete mode 100644 docs/getting-started/installation.md diff --git a/docs/getting-started/deployment.md b/docs/best-practices/deployment.md similarity index 96% rename from docs/getting-started/deployment.md rename to docs/best-practices/deployment.md index 3ec2555..31b292c 100644 --- a/docs/getting-started/deployment.md +++ b/docs/best-practices/deployment.md @@ -1,4 +1,4 @@ -# Deployment +# Production deployment One of the nice properties of this project is that is works both behind traditional web server setups as well as in a stand-alone environment. diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md deleted file mode 100644 index e0156a2..0000000 --- a/docs/getting-started/installation.md +++ /dev/null @@ -1,25 +0,0 @@ -# Installation - -First manually change your `composer.json` to include these lines: - -```json -{ - …, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/clue-engineering/frugal" - } - ] -} -``` - -> TODO: Change package name and register on Packagist. - -Then, simply install: - -```bash -$ composer require clue/frugal:dev-main -``` - -> TODO: Tagged release. diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index bdde66f..0b2455f 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -1,12 +1,26 @@ # Quickstart in 5 minutes Getting started with X is easy! -You can use this example to get started with a new `app.php` file: +Here's a quick tutorial to get you up and running in 5 minutes or less. +Start your timer and here we go! + +## Code + +In order to first start using X, let's start with an entirely empty project directory. +This shouldn't be too confusing, but here's how you can do so on the common line: + +```bash +$ mkdir ~/projects/acme +$ cd ~/projects/acme +``` + +Next, we can start by taking a look at a simple example application. +You can use this example to get started by creating a new `app.php` file in your empty project directory: ```php get('/', function () { ); }); +$app->run(); $loop->run(); ``` -That's it already! -The next step is now to install its dependencies and serve this web application. +On a code level, this is everything you need to get started. +For more sophisticated projects, you may want to make sure to [structure your controllers](../best-practices/controllers.md), +but the above should be just fine for starters. + +## Installation + +Next, we need to install X and its dependencies to actually run this project. + +> ⚠️ **Beta** +> +> This project is currently in closed beta, so the installation requires a manual step first. +> This will not be necessary once the project is released to the public. +> +> Start by creating a new `composer.json` in the project directory with the following contents: +> +> ```json +> { +> "repositories": [ +> { +> "type": "vcs", +> "url": "https://github.com/clue-engineering/frugal" +> } +> ] +> } +> ``` + +Thanks to [Composer](https://getcomposer.org/), this installation only requires a single command. + +> ℹ️ **New to Composer?** +> +> If you haven't heard about Composer before, Composer is *the* package manager for PHP-based projects. +> You can think of it as what NPM is to JavaScript, *but better*. +> If you haven't used it before, you have to install a recent PHP version and Composer before you can proceed. +> On Ubuntu- or Debian-based systems, this would be as simple as this: +> +> ```bash +> $ sudo apt install php-cli php-mbstring php-xml composer +> ``` + +In your project directory, simply run the following command: + +```bash +$ composer require clue/frugal:dev-main +``` + +This isn't NPM, so this should only take a moment or two. + +## Running + +The next step after installing all dependencies is now serve this web application. +One of the nice properties of this project is that is *runs anywhere* (provided you have PHP installed of course). + +For example, you can run the above example using PHP's built-in webserver for +testing purposes like this: + +```bash +$ php -S 0.0.0.0:8080 app.php +``` + +You can now use your favorite webbrowser or command line tool to check your web +application responds as expected: + +```bash +$ curl -v http://localhost:8080/ +HTTP/1.1 200 OK +… + +Hello wörld! +``` + +And that's it already, you can now stop your timer. +If you've made it this far, you should have an understanding why X is so exciting. +As a next step, we would recommend checking out the [best practices](../../best-practices/) in order to deploy this to production. + +Happy hacking! diff --git a/mkdocs.yml b/mkdocs.yml index ae16067..33154ee 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,12 +16,12 @@ markdown_extensions: nav: - Getting Started: +# - "Why?": "" - getting-started/quickstart.md - - getting-started/installation.md - - getting-started/deployment.md - Best Practices: - best-practices/controllers.md - best-practices/testing.md + - best-practices/deployment.md - API: - api/app.md - api/middleware.md From 63eb4cc84c813f3413dda0024ca1a03f8d30457e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 28 Apr 2021 16:09:48 +0200 Subject: [PATCH 4/9] Completed best practices --- docs/best-practices/controllers.md | 26 ++-- docs/best-practices/deployment.md | 79 ++++++++++-- docs/best-practices/testing.md | 201 +++++++++++++++++++++++++++-- mkdocs.yml | 2 +- 4 files changed, 277 insertions(+), 31 deletions(-) diff --git a/docs/best-practices/controllers.md b/docs/best-practices/controllers.md index 1b7148e..25e8167 100644 --- a/docs/best-practices/controllers.md +++ b/docs/best-practices/controllers.md @@ -1,15 +1,17 @@ # Controller classes to structure your app -Once everything is up and running, we can take a look at how to best structure -our actual web application. +When starting with X, it's often easiest to start with simple closure definitions like suggested in the [quickstart guide](../getting-started/quickstart.md). -To get started, it's often easiest to start with simple closure definitions -like the following: +As a next step, let's take a look at how this structure can be improved with controller classes. +This is especially useful once you leave the prototyping phase and want to find the best structure for a production-ready setup. + +To get started, let's take a look at the following simple closure definitions: ```php +# app.php get('/users/{name}', function (Psr\Http\Message\ServerRequestInterface $re ); }); +$app->run(); $loop->run(); ``` -While easy to get started, this will easily get out of hand for more complex +While easy to get started, it's also easy to see how this will get out of hand for more complex business domains when you have more than a couple of routes registered. For real-world applications, we highly recommend structuring your application @@ -41,10 +44,10 @@ into invidividual controller classes. This way, we can break up the above definition into three even simpler files: ```php -# main.php +# app.php get('/', new Acme\Todo\HelloController()); $app->get('/users/{name}', new Acme\Todo\UserController()); +$app->run(); $loop->run(); ``` @@ -119,7 +123,9 @@ autoloader classes: $ composer dump-autoload ``` -Don't worry, that's a one-time setup only. If you're used to working with +> ℹ️ **New to Composer?** +> +> Don't worry, that's a one-time setup only. If you're used to working with Composer, this shouldn't be too surprising. If this sounds new to you, rest assured this is the only time you have to worry about this, new classes can simply be added without having to run Composer again. @@ -134,3 +140,5 @@ HTTP/1.1 200 OK Hello wörld! ``` + +If everything works as expected, we can continue with writing our first tests to automate this. diff --git a/docs/best-practices/deployment.md b/docs/best-practices/deployment.md index 31b292c..625aafb 100644 --- a/docs/best-practices/deployment.md +++ b/docs/best-practices/deployment.md @@ -1,17 +1,30 @@ # Production deployment -One of the nice properties of this project is that is works both behind -traditional web server setups as well as in a stand-alone environment. +One of the nice properties of X is that it **runs anywhere**, i.e. it works both +behind traditional web server setups as well as in a stand-alone environment. +This makes it easy to get started with existing web application stacks, yet it +provides even more awesome features with its built-in web server. -For example, you can run the above example using PHP's built-in webserver for -testing purposes like this: +## Traditional stacks + +No matter what existing PHP stack you're using, X runs anywhere. +This means that if you've already used PHP before, X will *just work*. + +* nginx with PHP-FPM +* Apache with PHP-FPM, mod_fcgid, mod_cgi or mod_php +* Any other web server using FastCGI to talk to PHP-FPM +* Linux, Mac and Windows operating systems (LAMP, MAMP, WAMP) + +*We've got you covered.* + +For example, if you've followed the [quickstart guide](../getting-started/quickstart.md), you can run this using PHP's built-in development web +server for testing purposes like this: ```bash $ php -S 0.0.0.0:8080 app.php ``` -You can now use your favorite webbrowser or command line tool to check your web -application responds as expected: +In order to check your web application responds as expected, you can use your favorite webbrowser or command line tool: ```bash $ curl -v http://localhost:8080/ @@ -21,12 +34,52 @@ HTTP/1.1 200 OK Hello wörld! ``` -Runs everywhere: +## Built-in web server -* Built-in webserver -* nginx with PHP-FPM -* Apache with mod_fcgid and PHP-FPM -* Apache with mod_php -* PHP's development webserver +But there's more! +Framework X ships its own efficient web server implementation written in pure PHP. +This uses an event-driven architecture to allow you to get the most out of Framework X. +With the built-in web server, we provide a non-blocking implementation that can handle thousands of incoming connections and provide a much better user experience in high-load scenarios. + +With no changes required, you can run the built-in web server with the exact same code base on the command line: + +```bash +$ php app.php +``` + +Let's take a look and see this works just like before: + +```bash +$ curl -v http://localhost:8080/ +HTTP/1.1 200 OK +… + +Hello wörld! +``` + +You may be wondering how fast a pure PHP web server implementation could possibly be. + +``` +$ ab -n10000 -c10 http://localhost:8080/ +… +Concurrency Level: 10 +Time taken for tests: 0.991 seconds +Complete requests: 10000 +Failed requests: 0 +Total transferred: 1090000 bytes +HTML transferred: 130000 bytes +Requests per second: 10095.17 [#/sec] (mean) +Time per request: 0.991 [ms] (mean) +Time per request: 0.099 [ms] (mean, across all concurrent requests) +Transfer rate: 1074.58 [Kbytes/sec] received +``` + +The answer: Very fast! + +If you're going to use this in production, we still recommend running this +behind a reverse proxy such as nginx, HAproxy, etc. for TLS termination +(HTTPS support). -> TODO: Show different deployment scenarios +Additionally, you should use service monitoring to make sure the server will +automatically restart after system reboot or failure. Docker containers or +systemd unit files would be common solutions here. diff --git a/docs/best-practices/testing.md b/docs/best-practices/testing.md index 837d386..513a628 100644 --- a/docs/best-practices/testing.md +++ b/docs/best-practices/testing.md @@ -1,14 +1,199 @@ # Testing -**We ❤️ TDD and DDD!** +> ℹ️ **New to testing your web application?** +> +> While we don't want to *force* you to test your app, we want to emphasize the +> importance of automated test suites and try hard to make testing your web +> application as easy as possible. +> +> Tests allow you to verify correct behavior of your implementation, so that you +> match expected behavior with the actual implementation. +> And perhaps more importantly, by automating this process you can be sure +> future changes do not introduce any regressions and suddenly break something else. +> *Develop your application with ease and certainty.* +> +> **We ❤️ TDD!** -New to testing your web application? While we don't want to *force* you to test -your app, we want to emphasize the importance of automated test suits and try hard -to make testing your web application as easy as possible. +## PHPUnit basics -Once your app is structured into dedicated controller classes as per the previous -chapter, […] +Once your app is structured into [dedicated controller classes](controllers.md) +as per the previous chapter, we can test each controller class in isolation. +This way, testing becomes pretty straight forward. -> TODO: PHPUnit setup basics and first test cases. +Let's start simple and write some unit tests for our simple `HelloController` class: -> TODO: Higher-level functional tests. +```php +# src/HelloController.php + ℹ️ **New to PHPUnit?** +> +> If you haven't heard about [PHPUnit](https://phpunit.de/) before, +> PHPUnit is *the* testing framework for PHP projects. +> After installing it as a development dependency, we can take advantage of its +> structure to write tests for our own application. + +Next, we can start by creating our first unit test: + +```php +# tests/HelloControllerTest.php +assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals("Hello wörld!\n", (string) $response->getBody()); + } +} +``` + +We're intentionally starting simple. +By starting with a controller class following a somewhat trivial implementation, +we can focus on just getting the test suite up and running first. +All following tests will also follow a somewhat similar structure, so we can +always use this as a simple building block: + +* create an HTTP request object +* pass it into our controller function +* and then run assertions on the expected HTTP response object. + +Once you've created your first unit tests, it's time to run PHPUnit by executing +this command in the project directory: + +``` +$ vendor/bin/phpunit tests +PHPUnit 9.5.4 by Sebastian Bergmann and contributors. + +. 1 / 1 (100%) + +Time: 00:00.006, Memory: 4.00 MB + +OK (1 test, 1 assertion) +``` + +## Testing with specific requests + +Once the basic test setup works, let's continue with testing a controller that +shows different behavior depending on what HTTP request comes in. +For this example, we're using [request attributes](../api/request.md#attributes), +but the same logic applies to testing different URLs, HTTP request headers, etc.: + +```php +# src/UserController.php +getAttribute('name') . "!\n" + ); + } +} +``` + +Again, we create a new test class matching the controller class: + +```php +# tests/UserControllerTest.php +withAttribute('name', 'Alice'); + + $controller = new UserController(); + $response = $controller($request); + + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals("Hello Alice!\n", (string) $response->getBody()); + } +} +``` + +This follows the exact same logic like the previous example, except this time +we're setting up a specific HTTP request and asserting the HTTP response +contains the correct name. +Again, we can run PHPUnit in the project directory to see this works as expected: + +``` +$ vendor/bin/phpunit tests +PHPUnit 9.5.4 by Sebastian Bergmann and contributors. + +.. 2 / 2 (100%) + +Time: 00:00.003, Memory: 4.00 MB + +OK (2 tests, 2 assertions) +``` + +## Further reading + +If you've made it this far, you should have a basic understanding about how +testing can help you *develop your application with ease and certainty*. +We believe mastering TTD is well +worth it, but perhaps this is somewhat out of scope for this documentation. +If you're curious, we recommend looking into the following topics: + +* TDD +* Higher-level functional tests +* Test automation +* CI / CD diff --git a/mkdocs.yml b/mkdocs.yml index 33154ee..92b75e9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,7 +19,7 @@ nav: # - "Why?": "" - getting-started/quickstart.md - Best Practices: - - best-practices/controllers.md + - "Controller classes": best-practices/controllers.md - best-practices/testing.md - best-practices/deployment.md - API: From 3a302acde846039fa075f57899db93512ace4112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Thu, 29 Apr 2021 13:24:41 +0200 Subject: [PATCH 5/9] Typo queueueueueing --- docs/integrations/{queuing.md => queueing.md} | 2 +- mkdocs.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename docs/integrations/{queuing.md => queueing.md} (89%) diff --git a/docs/integrations/queuing.md b/docs/integrations/queueing.md similarity index 89% rename from docs/integrations/queuing.md rename to docs/integrations/queueing.md index 7fa9aef..52f34a0 100644 --- a/docs/integrations/queuing.md +++ b/docs/integrations/queueing.md @@ -1,4 +1,4 @@ -# Queuing +# Queueing * Built-in (or module?) * Redis built-in, but swappable with real instance (constraints?) diff --git a/mkdocs.yml b/mkdocs.yml index 92b75e9..4dbb56f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -32,14 +32,14 @@ nav: - async/coroutines.md - async/fibers.md - async/streaming.md - - async/child-processes.md + - "Child processes": async/child-processes.md - Integrations: - integrations/database.md - integrations/filesystem.md - integrations/authentication.md - integrations/sessions.md - integrations/templates.md - - integrations/queuing.md + - integrations/queueing.md - More: - more/philosophy.md - more/architecture.md From 9895f655af1f461cb9f908623685af71ac997d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 30 Apr 2021 11:29:49 +0200 Subject: [PATCH 6/9] Completed API documentation --- docs/api/app.md | 149 +++++++++++++++++++++++++- docs/api/middleware.md | 87 ++++++++++++++- docs/api/request.md | 200 +++++++++++++++++++++++++++++++++- docs/api/response.md | 237 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 667 insertions(+), 6 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index 43f22dd..9a4826d 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -1,10 +1,153 @@ # App -* Batteries included, but swappable -* Providing HTTP routing (RESTful applications) +The `App` class is your main entrypoint to any application that builds on top of X. +It provides a simple API for routing HTTP requests as commonly used in RESTful applications. + +Internally, the `App` object builds on top of [ReactPHP](https://reactphp.org/) +to do its magic, hence you have to create it like this: + +```php +run(); +$loop->run(); +``` + +> ℹ️ **Heads up!** +> +> Major improvements upcoming! We're actively contributing to our underlying +> libraries to make sure this can look like this in the near future: +> +> ```php +> +> require __DIR__ . '/vendor/autoload.php'; +> +> $app = 🚀🚀🚀\App(); +> +> // Register routes here, see routing… +> +> $app->run(); +> ``` ## Routing +The `App` class offers a number of API methods that allow you to route incoming +HTTP requests to controller functions. In its most simple form, you can add +multiple routes using inline closures like this: + +```php +$app->get('/user', function () { + return new React\Http\Message\Response(200, [], "hello everybody!"); +}); + +$app->get('/user/{id}', function (Psr\Http\Message\ServerRequestInterface $request) { + $id = $request->getAttribute('id'); + return new React\Http\Message\Response(200, [], "hello $id"); +}); +``` + +For example, an HTTP `GET` request for `/user` would call the first controller +function. +An HTTP `GET` request for `/user/alice` would call the second controller function +which also highlights how you can use [request attributes](request.md#attributes) +to access values from URI templates. + +An HTTP `GET` request for `/foo` would automatically reject the HTTP request with +a 404 (Not Found) error response unless this route is registered. +Likewise, an HTTP `POST` request for `/user` would reject with a 405 (Method Not +Allowed) error response unless a route for this method is also registered. + +You can route any number of incoming HTTP requests to controller functions by +using the matching API methods like this: + +```php +$app->get('/user/{id}', $controller); +$app->head('/user/{id}', $controller); +$app->post('/user/{id}', $controller); +$app->put('/user/{id}', $controller); +$app->patch('/user/{id}', $controller); +$app->delete('/user/{id}', $controller); +$app->options('/user/{id}', $controller); +``` + +If you want to map multiple HTTP request methods to a single controller, you can +use this shortcut instead of listing each method explicitly like above: + +``` +$app->map(['GET', 'POST'], '/user/{id}', $controller); +``` + +If you want to map each and every HTTP request method to a single controller, +you can use this additional shortcut: + +``` +$app->any('/user/{id}', $controller); +``` + +## Controllers + +The above examples use inline closures as controller functions to make these +examples more concise: + +``` +$app->get('/', function () { + return new React\Http\Message\Response( + 200, + [], + "Hello wörld!\n" + ); +}); +``` + +While easy to get started, it's easy to see how this would become a mess once +you keep adding more controllers to a single application. +For this reason, we recommend using [controller classes](../best-practices/controllers.md) +for production use-cases like this: + +```php +# main.php +$app->get('/', new Acme\Todo\HelloController()); +``` + +```php +# src/HelloController.php + ℹ️ **Feature preview** +> +> This is a feature preview, i.e. it might not have made it into the current beta. +> Give feedback to help us prioritize. + +One of the main features of the `App` is middleware support. +Middleware allows you to extract common functionality such as HTTP login, session handling or logging into reusable components. +These middleware components can be added to both individual routes or globally to all registered routes. +See [middleware documentation](../04-middleware.md) for more details. diff --git a/docs/api/middleware.md b/docs/api/middleware.md index 107581e..e7a4b53 100644 --- a/docs/api/middleware.md +++ b/docs/api/middleware.md @@ -1,3 +1,88 @@ # Middleware -> Under active development, stay tuned! +> ℹ️ **Feature preview** +> +> This is a feature preview, i.e. it might not have made it into the current beta. +> Give feedback to help us prioritize. + +One of the main features of X is middleware support. +Middleware allows you to extract common functionality such as an HTTP login, session handling or logging into reusable components. + +To get started, we can add an example middleware handler to an individual route +by adding an additional callable before the final controller like this: + +```php hl_lines="3-6" +$app->get( + '/user', + function (Psr\Http\Message\ServerRequestInterface $request, callable $next) { + $request = $request->withAttribute('admin', false); + return $next($request); + }, + function (Psr\Http\Message\ServerRequestInterface $request) { + $role = $request->getAttribute('admin') ? 'admin' : 'user'; + return new React\Http\Message\Response(200, [], "hello $role!"); + } +); +``` + +For example, an HTTP `GET` request for `/user` would first call the middleware handler which then modifies this request and passes the modified request to the next controller function. + +While easy to get started, it's easy to see how this would become a mess once you +keep adding more controllers to a single application. +For this reason, we recommend using middleware classes for production use-cases +like this: + +```php hl_lines="8" +# main.php + +use Acme\Todo\AdminMiddleware; +use Acme\Todo\UserController; + +// … + +$app->get('/user', new AdminMiddleware(), new UserController()); +``` + +```php +# src/AdminMiddleware.php +withAttribute('admin', false); + return $next($request); + } +} +``` + +Likewise, you can add any number of middleware handlers to each route. +Each middleware is responsible for calling the next handler in the chain or +directly returning an error response if the request should not be processed. + +Additionally, you can also add middleware to the `App` object itself to register +a global middleware handler for all registered routes: + +```php hl_lines="7" +get('/user', new UserController()); + +$app->run(); +$loop->run(); +``` + +You can also combine global middleware handlers (think logging) with additional +middleware handlers for individual routes (think authentication). +Global middleware handlers will always be called before route middleware handlers. diff --git a/docs/api/request.md b/docs/api/request.md index 7365378..033b688 100644 --- a/docs/api/request.md +++ b/docs/api/request.md @@ -1,3 +1,201 @@ # Request -* PSR-7 +Whenever the client sends an HTTP request to our application, +we receive this as an request object and need to react to it. + +We love standards and want to make using X as simple as possible. +That's why we build on top of the established [PSR-7 standard](https://www.php-fig.org/psr/psr-7/) +(HTTP message interfaces). +This standard defines common interfaces for HTTP request and response objects. + +If you've ever used PSR-7 before, you should immediately feel at home when using X. +If you're new to PSR-7, don't worry. +Here's everything you need to know to get started. + +> ℹ️ **A note about other PSR-7 implementations** +> +> This documentation uses the +> [`Psr\Http\Message\ServerRequestInterface`](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface) +> for all examples. +> The actual class implementing this interface is an implementation detail that +> should not be relied upon. +> If you need to construct your own instance, we recommend using the +> [`React\Http\Message\ServerRequest`](https://reactphp.org/http/#serverrequest) +> class because this comes bundled as part of our dependencies, +> but you may use any other implementation as long as +> it implements the same interface. + +## Attributes + +You can access request attributes like this: + +```php +$app->get('/user/{id}', function (Psr\Http\Message\ServerRequestInterface $request) { + $id = $request->getAttribute('id'); + + return new React\Http\Message\Response(200, [], "Hello $id"); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user/Alice +Hello Alice +``` + +These custom attributes are most commonly used when using URI placeholders +from [routing](app.md#routing). +Each placeholder will automatically be assigned to a matching request attribute. +See also [routing](app.md#routing) for more details. + +Additionally, these custom attributes can also be useful when passing additional +information from a middleware handler to other handlers further down the chain +(think authentication information). +See also [middleware](middleware.md) for more details. + +## JSON + +You can access JSON data from the HTTP request body like this: + +```php +$app->post('/user', function (Psr\Http\Message\ServerRequestInterface $request) { + $data = json_decode((string) $request->getBody()); + $name = $data->name ?? 'anonymous'; + + return new React\Http\Message\Response(200, [], "Hello $name"); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user --data '{"name":"Alice"}' +Hello Alice +``` + +Additionally, you may want to validate the `Content-Type: application/json` request header +to be sure the client intended to send a JSON request body. + +This example returns a simple text response, you may also want to return a +[JSON response](response.md#json) for common API usage. + +## Form data + +You can access HTML form data from the HTTP request body like this: + +```php +$app->post('/user', function (Psr\Http\Message\ServerRequestInterface $request) { + $data = $request->getParsedBody(); + $name = $data['name'] ?? 'Anonymous'; + + return new React\Http\Message\Response(200, [], "Hello $name"); +}); +``` + + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user -d name=Alice +Hello Alice +``` + +This method returns a possibly nested array of form fields, very similar to +PHP's `$_POST` superglobal. + +## Uploads + +You can access any file uploads from HTML forms like this: + +```php +$app->post('/user', function (Psr\Http\Message\ServerRequestInterface $request) { + $files = $request->getUploadedFiles(); + $name = isset($files['image']) ? $files['image']->getClientFilename() : 'x'; + + return new React\Http\Message\Response(200, [], "Uploaded $name"); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user -F image=@~/Downloads/image.jpg +Uploaded image.jpg +``` + +This method returns a possibly nested array of files uploaded, very similar +to PHP's `$_FILES` superglobal. +Each file in this array implements the `Psr\Http\Message\UploadedFileInterface`: + +```php +$files = $request->getUploadedFiles(); +$image = $files['image']; +assert($image instanceof Psr\Http\Message\UploadedFileInterface); + +$stream = $image->getStream(); +assert($stream instanceof Psr\Http\Message\StreamInterface); +$contents = (string) $stream; + +$size = $image->getSize(); +assert(is_int($size)); + +$name = $image->getClientFilename(); +assert(is_string($name) || $name === null); + +$type = $image->getClientMediaType(); +assert(is_string($type) || $name === null); +``` + +> ℹ️ **Info** +> +> Note that HTTP requests are currently limited to 64 KiB. Any uploads above +> this size will currently show up as an empty request body with no file uploads +> whatsoever. + +## Headers + +You can access all HTTP request headers like this: + +```php +$app->get('/user', function (Psr\Http\Message\ServerRequestInterface $request) { + $agent = $request->getHeaderLine('User-Agent'); + + return new React\Http\Message\Response(200, [], "Hello $agent"); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user -H 'User-Agent: FrameworkX/0' +Hello FrameworkX/0 +``` + +This example returns a simple text response with no additional response headers, +you may also want to return [response headers](response.md#headers) for common API usage. + +## Parameters + +You can access server-side parameters like this: + +```php +$app->get('/user', function (Psr\Http\Message\ServerRequestInterface $request) { + $params = $request->getServerParams(); + $ip = $params['REMOTE_ADDR'] ?? 'unknown'; + + return new React\Http\Message\Response(200, [], "Hello $ip"); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user +Hello 127.0.0.1 +``` + +This method returns an array of server-side parameters, very similar +to PHP's `$_SERVER` superglobal. +Note that available server parameters depend on the server software and version +in use. diff --git a/docs/api/response.md b/docs/api/response.md index 34b5075..0eaa78a 100644 --- a/docs/api/response.md +++ b/docs/api/response.md @@ -1,3 +1,238 @@ # Response -* PSR-7 +Whenever the client sends an HTTP request to our application, +we need to send back an HTTP response message. + +We love standards and want to make using X as simple as possible. +That's why we build on top of the established [PSR-7 standard](https://www.php-fig.org/psr/psr-7/) +(HTTP message interfaces). +This standard defines common interfaces for HTTP request and response objects. + +If you've ever used PSR-7 before, you should immediately feel at home when using X. +If you're new to PSR-7, don't worry. +Here's everything you need to know to get started. + +> ℹ️ **A note about other PSR-7 implementations** +> +> All of the examples in this documentation use the +> [`React\Http\Message\Response`](https://reactphp.org/http/#response) class +> because this comes bundled as part of our dependencies. +> If you have more specific requirements or want to integrate this with an +> existing piece of code, you can use any response implementation as long as +> it implements the [`Psr\Http\Message\ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface). + +## JSON + +You can send JSON data as an HTTP response body like this: + +```php +$app->get('/user', function () { + $data = [ + [ + 'name' => 'Alice' + ], + [ + 'name' => 'Bob' + ] + ]; + + return new React\Http\Message\Response( + 200, + ['Content-Type' => 'application/json'], + json_encode($data) + ); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user +[{"name":"Alice"},{"name":"Bob"}] +``` + +If you want to return pretty-printed JSON, all you need to do is passing the +correct flags when encoding: + +```php hl_lines="14-17" +$app->get('/user', function () { + $data = [ + [ + 'name' => 'Alice' + ], + [ + 'name' => 'Bob' + ] + ]; + + return new React\Http\Message\Response( + 200, + ['Content-Type' => 'application/json'], + json_encode( + $data, + JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE + ) + ); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user +[ + { + "name": "Alice" + }, + { + "name":"Bob" + } +] +``` + +This example returns a simple JSON response from some static data. +In real-world applications, you may want to load this from a +[database](../integrations/database.md). +For common API usage, you may also want to receive a [JSON request](request.md#json). + +## HTML + +You can send HTML data as an HTTP response body like this: + +```php +$app->get('/user', function () { + $html = <<Hello Alice +HTML; + + return new React\Http\Message\Response( + 200, + ['Content-Type' => 'text/html; charset=utf-8'], + $html + ); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user +

Hello Alice

+``` + +This example returns a simple HTML response from some static data. +In real-world applications, you may want to load this from a +[database](../integrations/database.md) and perhaps use +[templates](../integrations/templates.md) to render your HTML. + +## Status Codes + +You can assign status codes like this: + +```php hl_lines="5 12" +$app->get('/user/{id}', function (Psr\Http\Message\ServerRequestInterface $request) { + $id = $request->getAttribute('id'); + if ($id === 'admin') { + return new React\Http\Message\Response( + 403, + [], + 'Forbidden' + ); + } + + return new React\Http\Message\Response( + 200, + [], + "Hello $id" + ); +}); +``` + +An HTTP request can be sent like this: + +```bash hl_lines="2 6" +$ curl http://localhost:8080/user/Alice -I +HTTP/1.1 200 OK +… + +$ curl http://localhost:8080/user/admin -I +HTTP/1.1 403 Forbidden +… +``` + +Each HTTP response message contains a status code that describes whether the +HTTP request has been successfully completed. +Here's a list of the most common HTTP status codes: + +* 200 (OK) +* 301 (Permanent Redirect) +* 302 (Temporary Redirect) +* 403 (Forbidden) +* 404 (Not Found) +* 500 (Internal Server Error) +* … + +See [list of HTTP status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) for more details. + +## Headers + +You can assign HTTP response headers like this: + +```php hl_lines="4" +$app->get('/user', function () { + return new React\Http\Message\Response( + 200, + ['Content-Type' => 'text/plain; charset=utf-8'], + "Hello $id" + ); +}); +``` + +An HTTP request can be sent like this: + +```bash hl_lines="3" +$ curl http://localhost:8080/user -I +HTTP/1.1 200 OK +Content-Type: text/plain; charset=utf-8 +Server: ReactPHP/1 +Date: Fri, 30 Apr 2021 08:51:04 GMT +Content-Length: 88 +Connection: close +``` + +Each HTTP response message can contain an arbitrary number of response headers. +You can pass these headers as an associative array to the response object. + +Additionally, the application will automatically include default headers required +by the HTTP protocol. +It's not recommended to mess with these default headers unless you're sure you +know what you're doing. + +## Internal Server Error + +Each controller function needs to return a response object in order to send +an HTTP response message. +If the controller functions throws an `Exception` (or `Throwable`) or any other type, the +HTTP request will automatically be rejected with a 500 (Internal Server Error) +HTTP error response: + +```php +$app->get('/user', function () { + // TODO: load data + throw new BadMethodCallException(); +}); +``` + +An HTTP request can be sent like this: + +```bash hl_lines="2" +$ curl http://localhost:8080/user -I +HTTP/1.1 500 Internal Server Error +… +``` + +This error message contains only few details to the client to avoid leaking +internal information. +If you want to implement custom error handling, you're recommended to either +catch any exceptions your own or use a [middleware handler](middleware.md) to +catch any exceptions in your application. From 6866a98a7952d82ca02d1b242214abbe9865a1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 30 Apr 2021 11:49:46 +0200 Subject: [PATCH 7/9] Placeholder documentation for async --- docs/async/child-processes.md | 11 +++++++++-- docs/async/coroutines.md | 15 ++++++++++++--- docs/async/fibers.md | 15 ++++++++++++--- docs/async/promises.md | 14 +++++++++++--- docs/async/streaming.md | 10 ++++++++-- 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/docs/async/child-processes.md b/docs/async/child-processes.md index e5d7360..37ed21a 100644 --- a/docs/async/child-processes.md +++ b/docs/async/child-processes.md @@ -1,6 +1,13 @@ # Parallel processing with child processes +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + * Avoid blocking by moving blocking implementation to child process * Child process I/O for communication -* Multithreading -* PQ +* Multithreading, but isolated processes +* See [reactphp/child-process](https://reactphp.org/child-process/) for underlying APIs +* See [clue/reactphp-pq](https://github.com/clue/reactphp-pq) for higher-level API to automatically wrap blocking functions in an async child process and turn blocking functions into non-blocking [promises](promises.md) diff --git a/docs/async/coroutines.md b/docs/async/coroutines.md index 51e86b2..38acbb0 100644 --- a/docs/async/coroutines.md +++ b/docs/async/coroutines.md @@ -1,6 +1,15 @@ # Coroutines -* Promises can be hard due to nested callbacks -* Generator-based coroutines provided +> ⚠️ **Feature preview** +> +> This is a feature preview, i.e. it might not have made it into the current beta. +> Give feedback to help us prioritize. +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +* [Promises](promises.md) can be hard due to nested callbacks +* X provides Generator-based coroutines * Synchronous code structure, yet asynchronous execution -* Generators can be a bit harder to understand, see [fibers](03-fibers.md). +* Generators can be a bit harder to understand, see [Fibers](fibers.md) for future PHP 8.1 API. diff --git a/docs/async/fibers.md b/docs/async/fibers.md index ac66712..1fb5610 100644 --- a/docs/async/fibers.md +++ b/docs/async/fibers.md @@ -1,9 +1,18 @@ # Fibers -> Hot topic for PHP 8.1 +> ⚠️ **Feature preview** +> +> This is a feature preview, i.e. it might not have made it into the current beta. +> Give feedback to help us prioritize. +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! +* Hot topic for PHP 8.1 * Async APIs that look just like their synchronous counterparts * Much easier to integrate, possibly larger ecosystem in future * Requires PHP 8.1 (November 2021), adoption will take time -* Promises still required for concurrent execution -* Promises and coroutines work just fine until ecosystem matures +* [Promises](promises.md) still required for concurrent execution +* [Promises](promises.md) and [Coroutines](coroutines.md) work just fine until ecosystem matures +* See [blog post](https://clue.engineering/2021/fibers-in-php) diff --git a/docs/async/promises.md b/docs/async/promises.md index b21d403..c625d30 100644 --- a/docs/async/promises.md +++ b/docs/async/promises.md @@ -1,6 +1,14 @@ # Promises -* Avoid blocking +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +* Avoid blocking ([databases](../integrations/database.md), [filesystem](../integrations/filesystem.md), etc.) * Deferred execution -* Concurrent execution (more efficient than parallel threads / multithreading) -* API can be a bit harder (see Coroutines or Fibers) +* Concurrent execution more efficient than [multithreading](child-processes.md) +* API can be a bit harder (see [Coroutines](coroutines.md) or [Fibers](fibers.md)) +* See [reactphp/promise](https://reactphp.org/promise/) +* Avoid blocking by moving blocking implementation to [child process](child-processes.md) diff --git a/docs/async/streaming.md b/docs/async/streaming.md index 6be26ac..6211394 100644 --- a/docs/async/streaming.md +++ b/docs/async/streaming.md @@ -1,6 +1,12 @@ # Streaming -Processing large amounts of data or when data arrives at future time +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +Processing large amounts of data or when data arrives at future time. ## Streaming downloads @@ -14,6 +20,6 @@ Processing large amounts of data or when data arrives at future time ## WebSockets -* HTML5 WebSockets integration supported with Ratchet +* HTML5 WebSockets integration supported with [Ratchet](http://socketo.me/) * See also EventSource as alternative * Bidirectional communication From 703d03bb18b7a63eeaecf322e036218fcd59fe5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 30 Apr 2021 12:24:15 +0200 Subject: [PATCH 8/9] Placeholder documentation for integrations --- docs/integrations/authentication.md | 14 +++++++++++--- docs/integrations/database.md | 21 +++++++++++++++------ docs/integrations/filesystem.md | 14 +++++++++++--- docs/integrations/queueing.md | 14 ++++++++++++-- docs/integrations/sessions.md | 13 ++++++++++--- docs/integrations/templates.md | 12 +++++++++++- 6 files changed, 70 insertions(+), 18 deletions(-) diff --git a/docs/integrations/authentication.md b/docs/integrations/authentication.md index 4d398ca..1fac8f2 100644 --- a/docs/integrations/authentication.md +++ b/docs/integrations/authentication.md @@ -1,5 +1,13 @@ # Authentication -* Basic auth easy -* HTTP middleware better -* JWT and oauth possible +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +* HTTP Basic auth easy to implement +* Implementation as HTTP [middleware](../api/middleware.md) recommended +* JWT and OAuth possible +* Handling credentials application-specific, may take advantage of [database](database.md) +* See also [sessions](sessions.md) diff --git a/docs/integrations/database.md b/docs/integrations/database.md index a7cf3fa..a0df390 100644 --- a/docs/integrations/database.md +++ b/docs/integrations/database.md @@ -1,8 +1,17 @@ # Database -* Async -* No PDO, no Doctrine and family -* Easy to spot, harder to replace -* MySQL, Postgres and SQLite supported -* ORM -* Redis +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +* Async APIs with [Promises](../async/promises.md) +* Avoid using blocking PDO, Doctrine and family +* Major database vendors supported already + * [MySQL](https://github.com/friends-of-reactphp/mysql) + * [Postgres](https://github.com/voryx/PgAsync) + * [SQLite](https://github.com/clue/reactphp-sqlite) + * [Redis](https://github.com/clue/reactphp-redis) + * [ClickHouse](https://github.com/clue/reactphp-clickhouse) +* Future DBAL and ORM diff --git a/docs/integrations/filesystem.md b/docs/integrations/filesystem.md index e1c0de9..3d949df 100644 --- a/docs/integrations/filesystem.md +++ b/docs/integrations/filesystem.md @@ -1,6 +1,14 @@ # Filesystem -* Async -* No `fopen()`, `file_get_contents()` and family -* Easy to overlook +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +* Async APIs with [Promises](../async/promises.md) +* Avoid using blocking `fopen()`, `file_get_contents()` and family * Few blocking calls *can* be acceptable +* See [reactphp/filesystem](https://github.com/reactphp/filesystem) for filesystem prototypepq +* Avoid blocking filesystem by using [child process](child-processes.md) +* See [clue/reactphp-s3](https://github.com/clue/reactphp-s3) for async S3 filesystem API (supporting Amazon S3, Ceph, MiniIO, DigitalOcean Spaces and others) diff --git a/docs/integrations/queueing.md b/docs/integrations/queueing.md index 52f34a0..b4e3437 100644 --- a/docs/integrations/queueing.md +++ b/docs/integrations/queueing.md @@ -1,4 +1,14 @@ # Queueing -* Built-in (or module?) -* Redis built-in, but swappable with real instance (constraints?) +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +* Common requirement to offload work from frontend to background workers +* Major queue vendors supported already + * [BunnyPHP](https://github.com/jakubkulhan/bunny) for AMQP (RabbitMQ) + * [Redis](https://github.com/clue/reactphp-redis) with blocking lists and streams + * Experimental [STOMP](https://github.com/friends-of-reactphp/stomp) support for RabbitMQ, Apollo, ActiveMQ, etc. +* Future optionally built-in queueing support with no external dependencies, but swappable diff --git a/docs/integrations/sessions.md b/docs/integrations/sessions.md index 43ddfe6..6ebd446 100644 --- a/docs/integrations/sessions.md +++ b/docs/integrations/sessions.md @@ -1,5 +1,12 @@ # Sessions -* Built-in (or module?) -* HTTP middleware -* Persistence via database/ORM or other mechanism? +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +* Session handling common requirement and not hard to implement +* Implementation as HTTP [middleware](../api/middleware.md) recommended +* Handling credentials application-specific, may take advantage of [database](database.md) +* See also [authentication](authentication.md) diff --git a/docs/integrations/templates.md b/docs/integrations/templates.md index f19a7d5..9bc16a2 100644 --- a/docs/integrations/templates.md +++ b/docs/integrations/templates.md @@ -1,4 +1,14 @@ # Templates +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + +* Very common requirement, especially for [HTML pages](../api/response.md#html) * Any template language possible -* Twig recommended? + * [Twig](https://twig.symfony.com/) + * [Handlebars](https://github.com/salesforce/handlebars-php) + * [Mustache](https://github.com/bobthecow/mustache.php) +* Template files often loaded from [filesystem](filesystem.md) (avoid blocking) From 0f1fb49f00750b67676f07b14afcb9e78d39d386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 30 Apr 2021 12:32:49 +0200 Subject: [PATCH 9/9] Placeholder documentation for more sections --- docs/more/architecture.md | 14 ++++++++++---- docs/more/community.md | 8 +++++++- docs/more/philosophy.md | 16 ++++++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/more/architecture.md b/docs/more/architecture.md index 9a13894..5fcf73f 100644 --- a/docs/more/architecture.md +++ b/docs/more/architecture.md @@ -1,7 +1,13 @@ # Architecture -## PHP +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! -## ReactPHP - -## Async PHP +* HTTP request response semantics +* PHP runs everywhere +* shared nothing execution model (optional) +* ReactPHP (long-running optional) +* Async PHP diff --git a/docs/more/community.md b/docs/more/community.md index 9c404e6..0a32754 100644 --- a/docs/more/community.md +++ b/docs/more/community.md @@ -1,5 +1,11 @@ # Community +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + Framework X will be released as open-source under the permissive MIT license. This means it will be free as in free speech and as in free beer. @@ -8,7 +14,7 @@ Being open-source means we can foster a community to focus on building the best Framework X builds on top of existing open-source projects and we want to give back to this community of awesome engineers and developers. Being open to outside contributions means we can guarantee interoperability with a vivid ecosystem and ensure the longevity of the project. -* Twitter @x_framework +* Twitter [@x_framework](https://twitter.com/x_framework) * GitHub discussions * Support chat * GitHub sponsors diff --git a/docs/more/philosophy.md b/docs/more/philosophy.md index 035914f..62ce5cd 100644 --- a/docs/more/philosophy.md +++ b/docs/more/philosophy.md @@ -1,8 +1,16 @@ # Our philosophy +> ⚠️ **Documentation still under construction** +> +> You're seeing an early draft of the documentation that is still in the works. +> Give feedback to help us prioritize. +> We also welcome [contributors](../more/community.md) to help out! + * Motto: make easy things easy & hard things possible -* From quick prototyping (RAD) to production environment in hours +* From quick prototyping (RAD) to production environment in hours * Batteries included, but swappable -* Reuse where applicable -* LTS + careful upgrades -* Runs everywhere +* Reuse where applicable, but accept some duplication +* Long-term support (LTS) and careful upgrade paths +* Promote best practices, but don't enfore certain style +* Runs anywhere +* Open and inclusive [community](community.md)