Skip to content

Commit

Permalink
[2.x] Add support for FrankenPHP (#764)
Browse files Browse the repository at this point in the history
* Add support for FrankenPHP

* make linters happy

* Update src/Commands/StatusCommand.php

Co-authored-by: William Desportes <[email protected]>

* use a static closure in the worker script

Co-authored-by: Francis Lavoie <[email protected]>

* tabs

Co-authored-by: Francis Lavoie <[email protected]>

* tabs

Co-authored-by: Francis Lavoie <[email protected]>

* tabs

Co-authored-by: Francis Lavoie <[email protected]>

* tabs

Co-authored-by: Francis Lavoie <[email protected]>

* use match

* fix vuln and simplify config

* cs

* @francislavoie review

* Update StartFrankenPhpCommand.php

Co-authored-by: Kennedy Tedesco <[email protected]>

* Update frankenphp-worker.php

Co-authored-by: Kennedy Tedesco <[email protected]>

* Update InstallCommand.php

Co-authored-by: Kennedy Tedesco <[email protected]>

* docs: update README and composer.json

* cs

* phpstan

* simplify

* cs

* better Caddyfile

* Update octane.php

Co-authored-by: Shalvah <[email protected]>

* fix error handling

* disable debug mode as it's slow

* Fix swoole and octane write server running

* fix: ignore user abort

* feat: download the stable version

* follow symlinks

* improve perf

* fix non-tty mode

* formatting

* formatting

* formatting

* formatting

* formatting

* formatting

* performance

* fix log level support

* more robust log level handling in Mercure block

* fix logs

* better logging handling

* cs

* cs

* missing changes in Caddyfile

* Updates changelog

* Prettify output

* cs

* Displays regular error messages

* Ensures binary is up-to-date

* Ensures binary is up-to-date

* Removes mercure

* Removes unused warning

* Publishes only a simple require

* Style

* Cleans up code

* Uses `--caddyfile`

* Fixes catch

* formatting

* Fixes log output

* Fix bug

* Removes non-needed comments

* Prefixes caddy environment variables

* Reports worker exceptions

* Displays anything different from 200

* Shutdowns on server exception boot

* Fixes 500 responses from worker

* Don't publish caddyfile

* Fixes double realpath call

* Fixes method name

* Dont ignore caddy

* Fix logs in non local

* warn

* dont report to master

* Fixes method "put" not getting caught

* Uses mercure `demo` when requested

* Don't display mercure stuff

* Adds link to readme:

* Update README.md

* Update InstallsFrankenPhpDependencies.php

* Update InstallCommand.php

* Update StartCommand.php

* Update StartFrankenPhpCommand.php

---------

Co-authored-by: William Desportes <[email protected]>
Co-authored-by: Francis Lavoie <[email protected]>
Co-authored-by: Kennedy Tedesco <[email protected]>
Co-authored-by: Shalvah <[email protected]>
Co-authored-by: Nuno Maduro <[email protected]>
Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
7 people authored Dec 18, 2023
1 parent 1014bc6 commit a468648
Show file tree
Hide file tree
Showing 29 changed files with 1,001 additions and 34 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2

[Caddyfile]
indent_style = tab
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

- Fix passing invalid connection session id to `Swoole\Http\Response::create()` by [@smortexa](https://github.com/smortexa) in https://github.com/laravel/octane/pull/737
- Fix missing mode config by [@sy-records](https://github.com/sy-records) in https://github.com/laravel/octane/pull/740
- Add raw type in handleStream method for custom json in stdout by [@mphamid](https://github.com/mphamid) in https://github.com/laravel/octane/pull/742
- Add `raw` type in handleStream method for custom json in stdout by [@mphamid](https://github.com/mphamid) in https://github.com/laravel/octane/pull/742

## [v2.0.5](https://github.com/laravel/octane/compare/v2.0.4...v2.0.5) - 2023-08-08

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

## Introduction

Laravel Octane supercharges your application's performance by serving your application using high-powered application servers, including [Open Swoole](https://openswoole.com), [Swoole](https://github.com/swoole/swoole-src), and [RoadRunner](https://roadrunner.dev). Octane boots your application once, keeps it in memory, and then feeds it requests at supersonic speeds.
Laravel Octane supercharges your application's performance by serving your application using high-powered application servers, including [FrankenPHP](https://frankenphp.dev), [Open Swoole](https://openswoole.com), [Swoole](https://github.com/swoole/swoole-src), and [RoadRunner](https://roadrunner.dev). Octane boots your application once, keeps it in memory, and then feeds it requests at supersonic speeds.

## Official Documentation

Expand Down
5 changes: 3 additions & 2 deletions bin/bootstrap.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Laravel\Octane\Exceptions\DdException;
use Laravel\Octane\Octane;

ini_set('display_errors', 'stderr');

Expand All @@ -20,7 +21,7 @@
$basePath = $_SERVER['APP_BASE_PATH'] ?? $_ENV['APP_BASE_PATH'] ?? $serverState['octaneConfig']['base_path'] ?? null;

if (! is_string($basePath)) {
fwrite(STDERR, 'Cannot find application base path.'.PHP_EOL);
Octane::writeError('Cannot find application base path.');

exit(11);
}
Expand All @@ -45,7 +46,7 @@ function dd(...$vars)
*/

if (! is_file($autoload_file = $basePath.'/vendor/autoload.php')) {
fwrite(STDERR, "Composer autoload file was not found. Did you install the project's dependencies?".PHP_EOL);
Octane::writeError("Composer autoload file was not found. Did you install the project's dependencies?");

exit(10);
}
Expand Down
75 changes: 75 additions & 0 deletions bin/frankenphp-worker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

use Laravel\Octane\ApplicationFactory;
use Laravel\Octane\FrankenPhp\FrankenPhpClient;
use Laravel\Octane\RequestContext;
use Laravel\Octane\Stream;
use Laravel\Octane\Worker;
use Symfony\Component\HttpFoundation\Response;
use Throwable;

if ((! ($_SERVER['FRANKENPHP_WORKER'] ?? false)) || ! function_exists('frankenphp_handle_request')) {
echo 'FrankenPHP must be in worker mode to use this script.';

exit(1);
}

ignore_user_abort(true);

$basePath = require __DIR__.'/bootstrap.php';

/*
|--------------------------------------------------------------------------
| Start The Octane Worker
|--------------------------------------------------------------------------
|
| Next we will start the Octane worker, which is a long running process to
| handle incoming requests to the application. This worker will be used
| by FrankenPHP to serve an entire Laravel application at high speed.
|
*/

$frankenPhpClient = new FrankenPhpClient();

$worker = null;
$requestCount = 0;
$maxRequests = $_ENV['MAX_REQUESTS'] ?? $_SERVER['MAX_REQUESTS'];

try {
$handleRequest = static function () use (&$worker, $basePath, $frankenPhpClient) {
try {
$worker ??= tap(
new Worker(
new ApplicationFactory($basePath), $frankenPhpClient
)
)->boot();

[$request, $context] = $frankenPhpClient->marshalRequest(new RequestContext());

$worker->handle($request, $context);
} catch (Throwable $e) {
if ($worker) {
report($e);
}

$response = new Response(
'Internal Server Error',
500,
[
'Status' => '500 Internal Server Error',
'Content-Type' => 'text/plain',
],
);

$response->send();

Stream::shutdown($e);
}
};

while ($requestCount < $maxRequests && frankenphp_handle_request($handleRequest)) {
$requestCount++;
}
} finally {
$worker?->terminate();
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "laravel/octane",
"description": "Supercharge your Laravel application's performance.",
"keywords": ["laravel", "octane", "roadrunner", "swoole"],
"keywords": ["laravel", "octane", "roadrunner", "swoole", "frankenphp"],
"license": "MIT",
"support": {
"issues": "https://github.com/laravel/octane/issues",
Expand Down
34 changes: 17 additions & 17 deletions config/octane.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
| when starting, restarting, or stopping your server via the CLI. You
| are free to change this to the supported server of your choosing.
|
| Supported: "roadrunner", "swoole"
| Supported: "roadrunner", "swoole", "frankenphp"
|
*/

Expand Down Expand Up @@ -135,22 +135,6 @@
//
],

/*
|--------------------------------------------------------------------------
| Octane Cache Table
|--------------------------------------------------------------------------
|
| While using Swoole, you may leverage the Octane cache, which is powered
| by a Swoole table. You may set the maximum number of rows as well as
| the number of bytes per row using the configuration options below.
|
*/

'cache' => [
'rows' => 1000,
'bytes' => 10000,
],

/*
|--------------------------------------------------------------------------
| Octane Swoole Tables
Expand All @@ -169,6 +153,22 @@
],
],

/*
|--------------------------------------------------------------------------
| Octane Swoole Cache Table
|--------------------------------------------------------------------------
|
| While using Swoole, you may leverage the Octane cache, which is powered
| by a Swoole table. You may set the maximum number of rows as well as
| the number of bytes per row using the configuration options below.
|
*/

'cache' => [
'rows' => 1000,
'bytes' => 10000,
],

/*
|--------------------------------------------------------------------------
| File Watching
Expand Down
2 changes: 2 additions & 0 deletions src/ApplicationGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public function __construct(protected Application $app, protected Application $s
*/
public function handle(Request $request): Response
{
$request->enableHttpMethodParameterOverride();

$this->dispatchEvent($this->sandbox, new RequestReceived($this->app, $this->sandbox, $request));

if (Octane::hasRouteFor($request->getMethod(), '/'.$request->path())) {
Expand Down
163 changes: 163 additions & 0 deletions src/Commands/Concerns/InstallsFrankenPhpDependencies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

namespace Laravel\Octane\Commands\Concerns;

use GuzzleHttp\Client;
use Illuminate\Support\Facades\Http;
use Laravel\Octane\FrankenPhp\Concerns\FindsFrankenPhpBinary;
use Symfony\Component\Process\Process;
use Throwable;

trait InstallsFrankenPhpDependencies
{
use FindsFrankenPhpBinary;

/**
* The minimum required version of the FrankenPHP binary.
*
* @var string
*/
protected $requiredFrankenPhpVersion = '1.0.0';

/**
* Ensure the FrankenPHP's Caddyfile and worker script are installed.
*
* @return void
*/
public function ensureFrankenPhpWorkerIsInstalled()
{
if (! file_exists(public_path('frankenphp-worker.php'))) {
copy(__DIR__.'/../stubs/frankenphp-worker.php', public_path('frankenphp-worker.php'));
}
}

/**
* Ensure the FrankenPHP binary is installed into the project.
*
* @return string
*/
protected function ensureFrankenPhpBinaryIsInstalled()
{
if (! is_null($frankenphpBinary = $this->findFrankenPhpBinary())) {
return $frankenphpBinary;
}

if ($this->confirm('Unable to locate FrankenPHP binary. Should Octane download the binary for your operating system?', true)) {
$this->downloadFrankenPhpBinary();
}

return base_path('frankenphp');
}

/**
* Download the latest version of the FrankenPHP binary.
*
* @return bool
*/
protected function downloadFrankenPhpBinary()
{
$arch = php_uname('m');

$assetName = match (true) {
PHP_OS_FAMILY === 'Linux' && $arch === 'x86_64' => 'frankenphp-linux-x86_64',
PHP_OS_FAMILY === 'Darwin' => "frankenphp-mac-$arch",
default => null,
};

if ($assetName === null) {
$this->error('FrankenPHP binaries are currently only available for Linux (x86_64) and macOS. Other systems should use the Docker images or compile FrankenPHP manually.');

return false;
}

$assets = Http::accept('application/vnd.github+json')
->withHeaders(['X-GitHub-Api-Version' => '2022-11-28'])
->get('https://api.github.com/repos/dunglas/frankenphp/releases/latest')['assets'];

foreach ($assets as $asset) {
if ($asset['name'] !== $assetName) {
continue;
}

$path = base_path('frankenphp');

$progressBar = null;

(new Client)->get(
$asset['browser_download_url'],
[
'sink' => $path,
'progress' => function ($downloadTotal, $downloadedBytes) use (&$progressBar) {
if ($downloadTotal === 0) {
return;
}

if ($progressBar === null) {
$progressBar = $this->output->createProgressBar($downloadTotal);
$progressBar->start($downloadTotal, $downloadedBytes);

return;
}

$progressBar->setProgress($downloadedBytes);
},
]
);

chmod($path, 0755);

$progressBar->finish();

$this->newLine();

return $path;
}

$this->error('FrankenPHP asset not found.');

return $path;
}

/**
* Ensure the installed FrankenPHP binary meets Octane's requirements.
*
* @param string $frakenPhpBinary
* @return void
*/
protected function ensureFrankenPhpBinaryMeetsRequirements($frakenPhpBinary)
{
$version = tap(new Process([$frakenPhpBinary, '--version'], base_path()))
->run()
->getOutput();

$version = explode(' ', $version)[1] ?? null;

if ($version === null) {
return $this->warn(
'Unable to determine the current FrankenPHP binary version. Please report this issue: https://github.com/laravel/octane/issues/new.',
);
}

if (version_compare($version, $this->requiredFrankenPhpVersion, '>=')) {
return;
}

$this->warn("Your FrankenPHP binary version (<fg=red>$version</>) may be incompatible with Octane.");

if ($this->confirm('Should Octane download the latest FrankenPHP binary version for your operating system?', true)) {
rename($frakenPhpBinary, "$frakenPhpBinary.backup");

try {
$this->downloadFrankenPhpBinary();
} catch (Throwable $e) {
report($e);

rename("$frakenPhpBinary.backup", $frakenPhpBinary);

return $this->warn('Unable to download FrankenPHP binary. The HTTP request exception has been logged.');
}

unlink("$frakenPhpBinary.backup");
}
}
}
4 changes: 2 additions & 2 deletions src/Commands/Concerns/InstallsRoadRunnerDependencies.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ trait InstallsRoadRunnerDependencies
*
* @var string
*/
protected $requiredVersion = '2023.3.0';
protected $requiredRoadRunnerVersion = '2023.3.0';

/**
* Determine if RoadRunner is installed.
Expand Down Expand Up @@ -131,7 +131,7 @@ protected function ensureRoadRunnerBinaryMeetsRequirements($roadRunnerBinary)

$version = explode(' ', $version)[2];

if (version_compare($version, $this->requiredVersion, '>=')) {
if (version_compare($version, $this->requiredRoadRunnerVersion, '>=')) {
return;
}

Expand Down
Loading

0 comments on commit a468648

Please sign in to comment.