Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.x] Add support for FrankenPHP #764

Merged
merged 83 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
5e13fa8
Add support for FrankenPHP
dunglas Oct 10, 2023
c16125e
make linters happy
dunglas Oct 26, 2023
f560ebf
Update src/Commands/StatusCommand.php
dunglas Oct 26, 2023
a8a7ea4
use a static closure in the worker script
dunglas Oct 28, 2023
7b8200d
tabs
dunglas Oct 28, 2023
fe0f691
tabs
dunglas Oct 28, 2023
6c90b98
tabs
dunglas Oct 28, 2023
0432d92
tabs
dunglas Oct 28, 2023
d74e7f8
use match
dunglas Oct 28, 2023
b0021b7
fix vuln and simplify config
dunglas Oct 28, 2023
531d43c
cs
dunglas Oct 28, 2023
6c47901
@francislavoie review
dunglas Oct 28, 2023
7616f43
Update StartFrankenPhpCommand.php
dunglas Oct 28, 2023
235cb48
Update frankenphp-worker.php
dunglas Oct 28, 2023
7042db6
Update InstallCommand.php
dunglas Oct 28, 2023
4e30c2a
docs: update README and composer.json
dunglas Oct 30, 2023
8a36beb
cs
dunglas Oct 30, 2023
cf9e3a8
phpstan
dunglas Oct 30, 2023
5eb9871
simplify
dunglas Oct 30, 2023
7654199
cs
dunglas Oct 30, 2023
10d2d29
better Caddyfile
dunglas Oct 30, 2023
1ca92c4
Update octane.php
dunglas Nov 1, 2023
dfcae59
fix error handling
dunglas Nov 19, 2023
05f10ab
Merge branch 'feat/frankenphp' of github.com:dunglas/octane into feat…
dunglas Nov 19, 2023
0564dae
disable debug mode as it's slow
dunglas Nov 19, 2023
523c6c4
Fix swoole and octane write server running
nunomaduro Nov 20, 2023
574fe87
fix: ignore user abort
dunglas Nov 22, 2023
81fa352
feat: download the stable version
dunglas Dec 10, 2023
7ec5e46
follow symlinks
dunglas Dec 10, 2023
e36cac6
improve perf
dunglas Dec 10, 2023
4f275f8
fix non-tty mode
dunglas Dec 10, 2023
82088b7
formatting
taylorotwell Dec 13, 2023
ad53742
formatting
taylorotwell Dec 13, 2023
6d4af1e
formatting
taylorotwell Dec 13, 2023
799df5b
formatting
taylorotwell Dec 13, 2023
7198e80
formatting
taylorotwell Dec 13, 2023
16ae754
formatting
taylorotwell Dec 13, 2023
bf67790
performance
taylorotwell Dec 13, 2023
267d9fa
fix log level support
dunglas Dec 13, 2023
67d1112
more robust log level handling in Mercure block
dunglas Dec 13, 2023
e6c032a
fix logs
dunglas Dec 13, 2023
50d1fae
better logging handling
dunglas Dec 13, 2023
8f4da9c
cs
dunglas Dec 13, 2023
a024e85
cs
dunglas Dec 13, 2023
f14179d
missing changes in Caddyfile
dunglas Dec 13, 2023
fa3a3d2
Updates changelog
nunomaduro Dec 14, 2023
1917eb8
Prettify output
nunomaduro Dec 14, 2023
f091feb
cs
nunomaduro Dec 14, 2023
f37194b
Displays regular error messages
nunomaduro Dec 14, 2023
15b2447
Ensures binary is up-to-date
nunomaduro Dec 14, 2023
6775ba2
Ensures binary is up-to-date
nunomaduro Dec 14, 2023
ee5142a
Removes mercure
nunomaduro Dec 14, 2023
b33b7d1
Removes unused warning
nunomaduro Dec 14, 2023
9972b38
Publishes only a simple require
nunomaduro Dec 14, 2023
309caed
Style
nunomaduro Dec 14, 2023
fd56077
Cleans up code
nunomaduro Dec 14, 2023
d6547ed
Uses `--caddyfile`
nunomaduro Dec 14, 2023
ed6441c
Fixes catch
nunomaduro Dec 14, 2023
b9d8575
formatting
taylorotwell Dec 14, 2023
e9d9ba6
Fixes log output
nunomaduro Dec 15, 2023
61ab3c1
Fix bug
nunomaduro Dec 15, 2023
af34f43
Removes non-needed comments
nunomaduro Dec 15, 2023
3740c0e
Prefixes caddy environment variables
nunomaduro Dec 15, 2023
d329c10
Reports worker exceptions
nunomaduro Dec 15, 2023
9a532d6
Displays anything different from 200
nunomaduro Dec 15, 2023
904d037
Shutdowns on server exception boot
nunomaduro Dec 15, 2023
515a6f9
Fixes 500 responses from worker
nunomaduro Dec 15, 2023
484e470
Don't publish caddyfile
nunomaduro Dec 15, 2023
c47de88
Fixes double realpath call
nunomaduro Dec 15, 2023
73e401b
Fixes method name
nunomaduro Dec 15, 2023
f4a5ddf
Dont ignore caddy
nunomaduro Dec 15, 2023
1e99c9c
Fix logs in non local
nunomaduro Dec 15, 2023
d2a7fed
warn
nunomaduro Dec 15, 2023
2410ba3
dont report to master
nunomaduro Dec 15, 2023
48e64e1
Fixes method "put" not getting caught
nunomaduro Dec 18, 2023
af4ae4d
Uses mercure `demo` when requested
nunomaduro Dec 18, 2023
bfc8306
Don't display mercure stuff
nunomaduro Dec 18, 2023
b21b6b8
Adds link to readme:
nunomaduro Dec 18, 2023
cddc22a
Update README.md
taylorotwell Dec 18, 2023
3781b16
Update InstallsFrankenPhpDependencies.php
taylorotwell Dec 18, 2023
2dd3c21
Update InstallCommand.php
taylorotwell Dec 18, 2023
5e754ed
Update StartCommand.php
taylorotwell Dec 18, 2023
6a45c36
Update StartFrankenPhpCommand.php
taylorotwell Dec 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions bin/frankenphp-worker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

use Laravel\Octane\ApplicationFactory;
use Laravel\Octane\FrankenPhp\FrankenPhpClient;
use Laravel\Octane\RequestContext;
use Laravel\Octane\Worker;

$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.
|
*/

$frankenPhpClient = new FrankenPhpClient();

$worker = null;
$nbRequests = 0;
try {
while (
$nbRequests < ($_ENV['MAX_REQUESTS'] ?? $_SERVER['MAX_REQUESTS']) &&
frankenphp_handle_request(function () use (&$worker, $basePath, $frankenPhpClient) {
$worker = $worker ?: tap(
new Worker(
new ApplicationFactory($basePath), $frankenPhpClient
)
)->boot();

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

$worker->handle($request, $context);
})
) {
$nbRequests++;
}
dunglas marked this conversation as resolved.
Show resolved Hide resolved
} finally {
$worker?->terminate();
}
25 changes: 24 additions & 1 deletion 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 All @@ -51,6 +51,29 @@

'https' => env('OCTANE_HTTPS', false),

/*
|--------------------------------------------------------------------------
| Mercure hub
|--------------------------------------------------------------------------
|
| This option configures the Mercure hub (events broadcasting) that is included in FrankenPHP.
| The defaults are good enough for most applications.
| "publisher_jwt" configures the key and algorithm to use for verifying JWTs provided by publishers.
| "subscriber_jwt" configures the key and algorithm to use for verifying JWTs provided by subscribers.
| Set "anonymous" to false to disable anonymous subscribing.
| Set "subscriptions" to false to disable the subscription API.
| Set "mercure" to false to entirely disable the Mercure hub.
|
| More information about Mercure can be found on https://mercure.rocks.
dunglas marked this conversation as resolved.
Show resolved Hide resolved
*/

'mercure' => [
'publisher_jwt' => env('MERCURE_SUBSCRIBER_JWT_KEY', '!ChangeThisMercureHubJWTSecretKey!'),
dunglas marked this conversation as resolved.
Show resolved Hide resolved
'subscriber_jwt' => env('MERCURE_SUBSCRIBER_JWT_KEY', '!ChangeThisMercureHubJWTSecretKey!'),
'anonymous' => true,
'subscriptions' => true,
],

/*
|--------------------------------------------------------------------------
| Octane Listeners
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ parameters:
- "#Unsafe usage of new static\\(\\)#"
- "#\\(void\\) is used.#"
- "#Function Swoole\\\\Coroutine\\\\run not found.#"
- "#Function frankenphp_handle_request not found.#"
103 changes: 103 additions & 0 deletions src/Commands/Concerns/InstallsFrankenPhpDependencies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace Laravel\Octane\Commands\Concerns;

use GuzzleHttp\Client;
use Illuminate\Support\Facades\Http;
use Laravel\Octane\FrankenPhp\Concerns\FindsFrankenPhpBinary;

trait InstallsFrankenPhpDependencies
{
use FindsFrankenPhpBinary;

/**
* Ensure the FrankenPHP binary is installed into the project.
*/
protected function ensureFrankenPhpBinaryIsInstalled(): string
{
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();

copy(__DIR__.'/../stubs/Caddyfile', base_path('Caddyfile'));
}

return base_path('frankenphp');
}

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

$assetName = null;
if (PHP_OS_FAMILY === 'Linux') {
if ($arch !== 'x86_64') {
$assetName = 'frankenphp-linux-x86_64';
}
dunglas marked this conversation as resolved.
Show resolved Hide resolved
} elseif (PHP_OS_FAMILY === 'Darwin') {
$assetName = "frankenphp-mac-$arch";
}

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

return false;
}

// TODO: use https://api.github.com/repos/dunglas/frankenphp/releases/latest when a stable version will be available
$assets = Http::accept('application/vnd.github+json')
->withHeaders(['X-GitHub-Api-Version' => '2022-11-28'])
->get('https://api.github.com/repos/dunglas/frankenphp/releases', ['per_page' => 1])[0]['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();

if (PHP_OS_FAMILY === 'Darwin') {
$this->warn("Run `xattr -d com.apple.quarantine $path` to release FrankenPHP from Apple's quarantine before starting the server.");
}

return $path;
}

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

return $path;
}
}
2 changes: 1 addition & 1 deletion src/Commands/Concerns/InteractsWithServers.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ protected function writeServerRunningMessage()

$this->output->writeln([
'',
' Local: <fg=white;options=bold>http://'.$this->getHost().':'.$this->getPort().' </>',
' Local: <fg=white;options=bold>'.($this->option('https') ? 'https://' : 'http://').$this->getHost().':'.$this->getPort().' </>',
'',
' <fg=yellow>Press Ctrl+C to stop the server</>',
'',
Expand Down
29 changes: 27 additions & 2 deletions src/Commands/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

class InstallCommand extends Command
{
use Concerns\InstallsRoadRunnerDependencies;
use Concerns\InstallsFrankenPhpDependencies,
Concerns\InstallsRoadRunnerDependencies;

/**
* The command's signature.
Expand All @@ -34,12 +35,13 @@ public function handle()
{
$server = $this->option('server') ?: $this->choice(
'Which application server you would like to use?',
['roadrunner', 'swoole'],
['roadrunner', 'swoole', 'frankenphp'],
);

return (int) ! tap(match ($server) {
'swoole' => $this->installSwooleServer(),
'roadrunner' => $this->installRoadRunnerServer(),
'frankenphp' => $this->installFrankenPhpServer(),
default => $this->invalidServer($server),
}, function ($installed) use ($server) {
if ($installed) {
Expand Down Expand Up @@ -116,6 +118,29 @@ public function installSwooleServer()
return true;
}

/**
* Install the FrankenPHP server.
*
* @return bool
*/
public function installFrankenPhpServer()
{
if (File::exists(base_path('.gitignore'))) {
collect(['frankenphp', 'Caddyfile'])
->each(function ($file) {
$contents = File::get(base_path('.gitignore'));
if (! Str::contains($contents, $file.PHP_EOL)) {
File::append(
base_path('.gitignore'),
$file.PHP_EOL
);
}
});
}

return $this->ensureFrankenPhpBinaryIsInstalled();
}

/**
* Inform the user that the server type is invalid.
*
Expand Down
24 changes: 24 additions & 0 deletions src/Commands/ReloadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laravel\Octane\Commands;

use Laravel\Octane\FrankenPhp\ServerProcessInspector as FrankenPhpServerProcessInspector;
use Laravel\Octane\RoadRunner\ServerProcessInspector as RoadRunnerServerProcessInspector;
use Laravel\Octane\Swoole\ServerProcessInspector as SwooleServerProcessInspector;

Expand Down Expand Up @@ -33,6 +34,7 @@ public function handle()
return match ($server) {
'swoole' => $this->reloadSwooleServer(),
'roadrunner' => $this->reloadRoadRunnerServer(),
'frankenphp' => $this->reloadFrankenPhpServer(),
default => $this->invalidServer($server),
};
}
Expand Down Expand Up @@ -81,6 +83,28 @@ protected function reloadRoadRunnerServer()
return 0;
}

/**
* Reload the FrankenPHP server for Octane.
*
* @return int
*/
protected function reloadFrankenPhpServer()
{
$inspector = app(FrankenPhpServerProcessInspector::class);

if (! $inspector->serverIsRunning()) {
$this->error('Octane server is not running.');

return 1;
}

$this->info('Reloading workers...');

$inspector->reloadServer();

return 0;
}

/**
* Inform the user that the server type is invalid.
*
Expand Down
21 changes: 21 additions & 0 deletions src/Commands/StartCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class StartCommand extends Command implements SignalableCommandInterface
{--task-workers=auto : The number of task workers that should be available to handle tasks}
{--max-requests=500 : The number of requests to process before reloading the server}
{--rr-config= : The path to the RoadRunner .rr.yaml file}
{--frankenphp-config= : The path to the FrankenPHP Caddyfile file}
{--https : Enable HTTPS, HTTP/2 and HTTP/3, automatically generate and renew certificates}
taylorotwell marked this conversation as resolved.
Show resolved Hide resolved
{--watch : Automatically reload the server when the application is modified}
{--poll : Use file system polling while watching in order to watch files over a network}
{--log-level= : Log messages at or above the specified log level}';
Expand All @@ -46,6 +48,7 @@ public function handle()
return match ($server) {
'swoole' => $this->startSwooleServer(),
'roadrunner' => $this->startRoadRunnerServer(),
'frankenphp' => $this->startFrankenPhpServer(),
default => $this->invalidServer($server),
};
}
Expand Down Expand Up @@ -89,6 +92,24 @@ protected function startRoadRunnerServer()
]);
}

/**
* Start the FrankenPHP server for Octane.
*
* @return int
*/
protected function startFrankenPhpServer()
{
return $this->call('octane:frankenphp', [
'--host' => $this->getHost(),
'--port' => $this->getPort(),
'--workers' => $this->option('workers'),
'--max-requests' => $this->option('max-requests'),
'--https' => $this->option('https'),
'--watch' => $this->option('watch'),
'--poll' => $this->option('poll'),
]);
}

/**
* Inform the user that the server type is invalid.
*
Expand Down
Loading