We Wish You a Framework X-Mas 🎄
Tags: php, webdev
Published: 2021-12-21 06:56:34

>**Just the Gist**  
>How about an asynchronous model for your PHP web application? And what if it had a small footprint as well? Framework-X is a micro-framework built on ReactPHP, giving its users the ability to create their own asynchronous web applications. Today we wish you a *Framework X-mas*!

There's a joke going around, that each and every day there is another framework released for building web applications. The criticism of new frameworks is that they take labour that possibly could have put into making improvements to the frameworks that we have - which already is a bunch. But for some projects, the new framework could be a reflection of a new way to collaborate, or a new way to build a web application - despite its likeness to the old frameworks. But today's subject is leaning more towards a new way to build web applications that is not like our established frameworks in the PHP community. We're talking about Framework-X, a framework implemented on ReactPHP which enables asynchronous execution of PHP code.    
> This may be a bit hyperbolic as I've only experience with 4 other frameworks, so if you know of a similar framework as I'm writing of today, please let me know. 

>Before we dive deeper into this subject, let's clarify a few things: 
>1. This is a surface-level look at **Framework-X**. Prior to Framework-X, I have no experience with ReactPHP.
>2. This article won't go into event-driven programming.
>3. As of writing, Framework-X is still to reach a stable release. So it's not a good idea to use it in production without considering what this means for your project.  
>  
>That's it, and please comment if there are any questions or corrections you would like to point out.

## Framework-X
As the project describes itself, it is a "*Simple and fast PHP micro framework. Async made easy.*" So for today's article, we will build a very simple application with only one page. The page will display the title, description and link of my latest article on [DEV](dev.to). Most of what we will do here is pretty straight forward. We will not be relying on any other libraries but what comes out of the box with [**Framework-X**](https://framework-x.org/) and enjoying the comforts of **PHP 8.1**.  
  
> OH, if you haven't got Composer I'd recommend you to go get it for your system if you want to be building PHP projects. It's a very handy tool that will help you to manage your dependencies. Get it at [getcomposer.org](https://getcomposer.org/).

### Create a new project
First things first, let's create a new project. How about a project called dev-x?  Create this directory and cd into it. Then run the following command:
```bash	
composer require clue/framework-x:dev-main
```	
> Note that this specifies that we will be using *dev-main* as it currently lacks any official release. It will create a new directory called `dev-x` and install the dependencies that we need for this project.

Let's open the `dev-x` directory in our favorite text editor (mine is [VSCode](https://code.visualstudio.com/), what is yours). We will have a `vendor` directory and our `composer` files. Now for our little fun:

* Let's **create a new folder called `public`** in our project root  
* In that folder, **create a new file called `index.php`**  
☝ That's where we will be playing next!

In the `index.php` file we will set it up to listen to a `GET` request on `/` - meaning the homepage. Just to see that we're doing something that works, let this be our first iteration:  
```php
use FrameworkX\App;
use React\Http\Message\Response;

require __DIR__ . '/../vendor/autoload.php';

$app = new App();

$app->get('/', function () {
    return new Response(body: 'Merry Christmas!');
});

$app->run();
```

We can run this file from our project root with: `php public/index.php`. This will start the built-in web server that Framework-X provides. If you have the Symfony CLI, you could start the project with `symfony serve -d` instead. The Symfony web server gives a SSL certificate that you can use to run the project on HTTPS - it also allows us to update the project while the server is running and show the updates to the user. The Framework-X server would need to be restarted.

>I don't know the reason why the Framework-X server needs to be restarted to show updates. Maybe it has something to do with the event-driven architecture. If you know what's up, please leave a comment or mail me.

This very bare-bones application will now serve us a nice little *Merry Christmas* when we visit `localhost:8080` (or `localhost:8000` if you ran it with Symfony). Getting us this far, let's expand this a little bit so we can get my latest article from [DEV](dev.to) on the page. Maybe (let's hope) the article will be a bit more intriguing than "Merry Christmas", but let that be a fallback for us. 

### I need control! 
A good practice is the separation of concerns. So to not get a crowded `index.php` file let's create a new folder called `src` and another folder called `Controller` within that. Yes, you guessed it! We are building a controller that will be mapped to from the `index.php` file.

*./src/Controller/DevController.php*
```php
<?php 

namespace App\Controller;

use React\Http\Message\Response;

class DevController
{
    public function __invoke()
    {
        return new Response(body: 'Merry Christmas!');
    }
}
```

With this added structure in mind, let's make it so the class can be autoloaded with a use-statement. For this to work we will map the `src`-folder to `App` namespace. So let's add this to the `composer.json` file in the project root:  
```json
{
    "require": {
        "clue/framework-x": "dev-main"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}
```
>You may need to run `composer dump` for it to register the new namespace.

Having registered the namespace, we can use the `use` statement to import the Controller-class. We will be mapping to this class from our `index.php` file, in essential this will mean that we connect a route to a specific controller. So here's how the `index.php` file will look like:	
```php	
<?php

use App\Controller\DevController;
use FrameworkX\App;

require __DIR__ . '/../vendor/autoload.php';

$app = new App();

$app->get('/', new DevController());

$app->run();

```

Running this file again will result in the same output as before, so here's another "Merry Christmas" 🎅! Now, why don't we see to it that we actually get the latest article from [DEV](dev.to)?

### DevControl to major DEV 🎶
What we will do next is make an asynchronous call to DEV and get the latest article. We will use the [**Framework-X**](https://framework-x.org/) built-in HTTP client to make the request. But note that while we make an essentially asynchronous call, we are only calling one resource at one time - hardly making good use of this asynchronous structure. Anyway, here's how it will look like:  
```php
<?php 

namespace App\Controller;

use Psr\Http\Message\ResponseInterface;
use React\Http\Browser;
use React\Http\Message\Response;

class DevController
{
    public function __invoke()
    {
        $browser = new Browser();

        return $browser
            ->get('https://dev.to/api/articles?username=andersbjorkland&per_page=1')
            ->then(
                onFulfilled: function (ResponseInterface $response) {
                    $json = json_decode($response->getBody(), true);
                    $latestArticle = $json[0];
                    return $this->parseJsonArticleToResponse($latestArticle);
                },
                onRejected: function () {
                    return new Response(body: "<p>Well, that didn't work. But HEY: Merry Christmas!</p>");
                }
            );

    }

    protected function parseJsonArticleToResponse($article): Response 
    {
        $title = $article['title'];
        $description = $article['description'];
        $url = $article['url'];
        
        $body = <<<BODY
        <h1>$title</h1>
        <p>$description</p>
        <a href="$url">$url</a>
        BODY;

        return new Response(body: $body);
    }
}
```

Ok, now we are cooking! So there are a couple of things going on here. First off, we are introduced to a whole new class called `Browser`. This class is used to make HTTP-requests, and that's exactly what we need to get a response from DEV with that *sweet* article 😉. We specify that `$browser` will make a GET-request to DEV. Once we have received a response the `then`-method will be called. This method can take two callbacks (*well, actually three but the third callback is deprecated*). The first callback is used upon a successful response and the second callback is used for a failed response. 

> While we are here, did you see how we can see what each callback's purpose is? With **named arguments** introduced with PHP 8.0 we can specify fow which parameter each argument is supposed to be used with. It's quite obvious then that the `onFulfilled`-callback is used when the response is successful and the `onRejected`-callback is used when the response is not successful.  
  
Taking a look at the `onFulfilled`-callback, we are going to parse the JSON-response from DEV into a response that we can return to the user. This is done by calling the `parseJsonArticleToResponse`-method. This method takes one argument, which is the article in a JSON-format. We are taking the title and description from the article and returning it with some basic HTML in a new response.

> So this is all very basic, but it gives a good feel for how to work with Framework-X. If you want your application with a bit more style, you could either expand the HEREDOC or use a template engine such as Twig (very do-able, and if there is a need for it I could write a tutorial on how to do it).

## What about you?
Have you used Framework-X or ReactPHP before? Do you have a favorite micro-framework, what is it? Does PHP benefit from asynchronous functions? Comment below and let us know what you think ✍

## Further Reading
* [ReactPHP](https://reactphp.org/)
* [Framework-X](https://framework-x.org/)