brefphp / bref

Serverless PHP on AWS Lambda
https://bref.sh
MIT License
3.1k stars 367 forks source link

Better documentation around function runtime and Symfony in Docker #1840

Open cameronmurphy opened 2 months ago

cameronmurphy commented 2 months ago

There's no documentation on how to use the docker function runtime to run a Symfony console command. My lambda function is invoked by SES, and I need access to the database, so I had to boot the Kernel.

This was my solution src/LambdaKernel.php:

<?php

namespace App;

class LambdaKernel extends Kernel
{
    public function getCacheDir(): string
    {
        return '/tmp/cache';
    }

    public function getLogDir(): string
    {
        return '/tmp/log';
    }
}

bin/lambda-handler.php:

<?php

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

use App\LambdaKernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Dotenv\Dotenv;

(new Dotenv())->bootEnv(dirname(__DIR__) . '/.env');

$env = $_SERVER['APP_ENV'] ?? 'dev';
$debug = $_SERVER['APP_DEBUG'] ?? ('prod' !== $env);

$kernel = new LambdaKernel($env, $debug);
$application = new Application($kernel);

$application->setAutoExit(false);
$application->setCatchErrors(false);

return function (array $event) use ($application): array {
    $input = new ArrayInput([
        'command' => 'app:console-command-to-execute',
        'event' => json_encode($event),
    ]);
    $output = new BufferedOutput();

    $result = $application->run($input, $output);

    return [
        'success' => 0 === $result,
        'output' => $output->fetch(),
    ];
};

Dockerfile:

FROM bref/php-82:2

COPY . /var/task

CMD ["bin/lambda-handler.php"]

The real gotcha was preventing the application from auto exiting, which was putting the application into a loop and running the console command repeatedly.

Maybe it's just a really niche use case, but I was surprised by how difficult it was to deploy a single Symfony console command as a dockerised lambda function (where I don't have to invoke with specific arguments).

mnapoli commented 2 months ago

Why not use the console runtime? (https://bref.sh/docs/runtimes/console)

cameronmurphy commented 2 months ago

I did try but because event is an array when data is coming in from SES, this line prevents event data from getting to my handler.

https://github.com/brefphp/bref/blob/cb264c28fd4c3449853dccfe0dada08155e69264/src/ConsoleRuntime/Main.php#L37

mnapoli commented 2 months ago

Oh sorry I misread that. I think you want to use this feature instead of rebuilding it from scratch: https://github.com/brefphp/symfony-bridge?tab=readme-ov-file#class-handlers

cameronmurphy commented 2 months ago

I looked at this but I couldn't figure out how to invoke that class handler inside my Docker container locally? Only invoking plain PHP file handlers is documented.

https://bref.sh/docs/local-development/event-driven-functions#with-docker

Also which handler class would I use for SES events?

mnapoli commented 2 months ago

Try something like this:

docker run --rm -it -v $(PWD):/var/task:ro bref/php-81-fpm-dev:2 vendor/bin/bref-local "My\\Class"

(I think you need to escape the backslashes)

For SES there is no specific handler class. You can implement the Bref\Event\Handler interface.