frankenphp icon indicating copy to clipboard operation
frankenphp copied to clipboard

Worker configuration example for CakePHP ^4

Open steinkel opened this issue 9 months ago • 4 comments

Describe you feature request

Is your feature request related to a problem? Please describe. I've started working on a worker configuration example for CakePHP ^4 based on previous work done for RoadRunner here https://github.com/CakeDC/cakephp-roadrunner but I'm struggling a bit understanding how to integrate it with the \frankenphp_handle_request as it seems to "work with the defaults" even if I define an application previously.

Describe the solution you'd like Do you have already a working configuration I could use as a default, apart from the one located here https://frankenphp.dev/docs/worker/ in Custom Apps?

Describe alternatives you've considered I went through other workers to check the implementation, given the fact that CakePHP is PSR 7 and 15 compatible, the solution should be close to others implemented...

If you know of a working repo or configuration I could use as a starting point, I would be grateful, thank you!

steinkel avatar Mar 27 '25 18:03 steinkel

FrankenPHP uses the same globals as a regular PHP request, so you can probably just call createFromGlobals when creating a new request. The only other worker implementations I know are the one from Symfony Runtime and Laravel Octane. Laravel's implementation does a lot of global state resetting between requests. I'd say the less the framework depends on various global state, the easier it is to implement a worker mode.

AlliBalliBaba avatar Mar 28 '25 18:03 AlliBalliBaba

Thank you, I'll prepare some code...

steinkel avatar Mar 31 '25 12:03 steinkel

My first approach to understand the worker mode:

<?php
use Cake\Http\ResponseEmitter;

ignore_user_abort(true);

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

$myApp = new \App\Application(__DIR__ . '/config');
$myApp->bootstrap();
$myApp->pluginBootstrap();
error_log('Worker started');
$responseEmitter = new ResponseEmitter();

$handler = static function () use ($myApp, $responseEmitter) {
    error_log('Worker handler');
    $request = \Cake\Http\ServerRequestFactory::fromGlobals();
    $response = $myApp->handle($request);
    $responseEmitter->emit($response);
};

$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
    error_log('Worker handling request');
    $keepRunning = \frankenphp_handle_request($handler);

    gc_collect_cycles();

    if (!$keepRunning) break;
}

I've added an error_log trace into my Application::bootstrap method, I was assuming the bootstrap should NOT be called in each request, as the code inside the handler is not calling it, but it's being called...

I'm using a simple Dockerfile

FROM dunglas/frankenphp:1-php8.2

# Be sure to replace "your-domain-name.example.com" by your domain name
ENV SERVER_NAME=localhost
# If you want to disable HTTPS, use this value instead:
#ENV SERVER_NAME=:80

# Enable PHP production settings
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

RUN install-php-extensions \
	pdo_mysql \
	intl \
	zip \
	opcache

# Copy the PHP files of your project in the public directory
COPY . /app

And the commands

docker build -t my-php-app .
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp -e FRANKENPHP_CONFIG="worker ./worker.php" --tty my-php-app

Am I under the correct assumption that the code in the Application::bootstrap should not be called in each request handling?

Thank you,

steinkel avatar Apr 01 '25 19:04 steinkel

Yeah bootstrap should only be called once per $maxRequests iterations, unless the worker crashes somehow through a fatal error.

I'll try your script when I have time.

AlliBalliBaba avatar Apr 01 '25 22:04 AlliBalliBaba

FrankenPHP was fully integrated with CakePHP here https://github.com/cakephp/cakephp/pull/18694 and will be available as soon as v5.3.0 will be released.

steinkel avatar Oct 28 '25 11:10 steinkel