20

Release 0.5.14 · brefphp/bref · GitHub

 4 years ago
source link: https://github.com/brefphp/bref/releases/tag/0.5.14
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Release 0.5.14 · brefphp/bref · GitHub

mnapoli released this on Feb 4 · 531 commits to master since this release

73723431-4ed47880-4729-11ea-9b03-a67fc96e7acd.png

This Bref release is massive:

Typed events and handlers tada

This concerns the "PHP function" runtime, not the PHP-FPM runtime.

Currently it is possible to define PHP lambda functions via an untyped callable taking an untyped event:

lambda(function ($event) {
    return 'Hello ' . $event['name'];
});

This is in line with JavaScript support in Lambda. The goal with Bref is to be like if AWS implemented PHP support natively, so we stayed with that approach to mimic JS.

However, in more typed languages, Lambda handlers are classes that take typed events (Java example, C# example). Since PHP allows both dynamic typing and strict typing, I think this makes sense to support both options in Bref.

To sum up:

  • we keep anonymous function handlers
  • we keep untyped events (i.e. arrays)
  • we offer the possibility to write typed handlers taking typed events via PHP interfaces

Bref will detect what you use and call it accordingly.

Currently, you can use typed handlers and events with:

  • S3 events
  • SQS events
  • SNS events
  • HTTP events (more details below)

More will come later.

Here is an example:

class MyHandler extends SqsHandler
{
        public function handleSqs(SqsEvent $event, Context $context): void
        {
            // do something
            // Notice $event is typed, we can call methods on it:
            $event->getRecords()[0]->getMessageId();
            
            // if AWS adds a new field to SQS events,
            // users can still access the raw event data via:
            $array = $event->toArray();
        }
}

// Now we declare our handler
lambda(new MyHandler());

As you can see, the $event parameter is typed as SqsEvent. That allows you to avoid doing any kind of validation: you are sure that you get the event you expect. If not, Bref will throw a clear error.

As a side benefit, those handler classes will be easier to test than anonymous functions.

For those interested in SQS or SNS, you can see that writing job queue workers will be easier than ever! No need for a framework or a library. You can write a worker by simply extending a class!

The removal of the lambda() function

Still in the spirit of implementing a runtime that would look like an official AWS runtime, we are getting rid of the lambda() function.

For the moment, BC is kept and it will still work, it is only deprecated.

Before:

lambda(function ($event) {
    return 'Hello ' . $event['name'];
});

// or

lambda(new MyHandler());

After:

return function ($event) {
    return 'Hello ' . $event['name'];
};

// or

return new MyHandler();

Much simpler and less opinionated!

Native HTTP functions with PSR-7 and PSR-15

Creating HTTP applications with Bref is easy thanks to the PHP-FPM layer. It runs your application in the same environment as any server, and that lets you run Laravel, Symfony or any framework without much changes.

However, sometimes we want to create a small HTTP lambda. Using a framework can be overkill, especially since we have the PSR-7 standard for HTTP requests and responses now.

Using the typed handlers introduced above, Bref now provides native support for PSR-7 request/response and PSR-15 RequestHandlerInterface:

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use GuzzleHttp\Psr7\Response;

class MyHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        return new Response(200, [], 'Hello world!');
    }
}

return new MyHandler();

That means you can now write controllers using PSR-7 and skip using a framework: Bref will convert HTTP event data (API Gateway or ALB) into PSR-7 requests, and handle the responses as well. API Gateway can do the routing part.

And yes, there is support for form data POST, cookies, multi-value headers, multipart form post, file uploads…

But that doesn't stop here: since we support PSR-15 handlers, we support PSR-15 compliant frameworks natively, without PHP-FPM! For example Slim:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

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

$app = AppFactory::create();

$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
    $name = $args['name'];
    $response->getBody()->write("Hello, $name");
    return $response;
});

// Instead of running $app->run() we return it: $app is the HTTP handler!
return $app;

Performances!

Let's be straight: running HTTP applications with the "Function" runtime (as shown above) is slower than using the PHP-FPM layer.

The reason for that is that PHP-FPM reuses processes in memory, as PHP as always done. On the other hand, the function layer creates a brand new PHP process for every invocation/request. This is slower, but this is in line with how PHP has always run (and what users will expect): a blank slate every time.

For cases where performances do not matter, e.g. a very small endpoint, like a webhook, that is fine. The benefit: we don't need a framework yet we get HTTP request/response objects instead of $_GET and $_POST.

But, there is one last thing :)

Keep-alive layers

There is one way we can accelerate PHP and make it faster than the PHP function runtime, and even the FPM runtime. That involves keeping the PHP process alive between requests (just like ReactPHP, PHP-PM, etc.).

Thanks to the typed events + the PSR-15 handlers + the removal of the lambda() function, we can now implement keep-alive layers:

  • without introducing separate layers
  • without breaking the default behavior of PHP (which is to restart the process by default)
  • and apply it to HTTP as well as any other kind of Lambda event!

How it works?

The "Function" runtime is now keeping the PHP process alive between events. However, by default, it will handle only 1 event and then terminate (to be started again in the next event).

That default behavior reproduces PHP and Bref's current behavior: by default Bref creates a new process for every event.

But, by simply setting the BREF_LOOP_MAX environment variable to a different value than 1, e.g. 1000, the same layer will now act as a keep-alive layer! The same process will handle 1000 events before terminating.

That can accelerate performances by a lot in some cases. Be careful though: just like ReactPHP, you need to be careful not keeping state across requests! This is an execution mode that I would only recommend for advanced use cases.

In my benchmarks, Lambda functions can execute in less than 1 ms. This applies to HTTP applications as well as queue handlers and all other event handlers.

Side note: this feature is not documented yet. We want people to experiment with this and report any problem before we advertise for it.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK