26
loading...
This website collects cookies to deliver better user experience
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!
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:
That's it, and please comment if there are any questions or corrections you would like to point out.
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.
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.
dev-x
directory in our favorite text editor (mine is VSCode, what is yours). We will have a vendor
directory and our composer
files. Now for our little fun:public
in our project root
index.php
☝ That's where we will be playing next!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: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();
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.
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 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. 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.<?php
namespace App\Controller;
use React\Http\Message\Response;
class DevController
{
public function __invoke()
{
return new Response(body: 'Merry Christmas!');
}
}
src
-folder to App
namespace. So let's add this to the composer.json
file in the project root:{
"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.
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
use App\Controller\DevController;
use FrameworkX\App;
require __DIR__ . '/../vendor/autoload.php';
$app = new App();
$app->get('/', new DevController());
$app->run();
<?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);
}
}
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.
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).