amphp / http-server

An advanced async HTTP server library for PHP, perfect for real-time apps and APIs with high concurrency demands.

Home Page:https://amphp.org/http-server

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Aliasing `HttpServer` with `Server` fails with an optimized autoloader

bzikarsky opened this issue · comments

Note: This discussion started in/relates to amphp/http-server-static-content#13

When using http-server-static-content, amphp/http-server-form-parser and amphp/http-server-router in production we noticed errors after an upgrade of amphp/http-server to a recent version:

Fatal error: Could not check compatibility between TestObserver::onStart(Amp\Http\Server\Server $server): Amp\Promise and Amp\Http\Server\ServerObserver::onStart(Amp\Http\Server\HttpServer $server): Amp\Promise, because class Amp\Http\Server\Server is not available in <path>

This issue was a mystery first, I wrote a minimum example that seemed to work fine (tested in root-dir of http-server 4.5.5 with PHP 7.4.16, with a standard composer install):

<?php
declare(strict_types=1);

use Amp\Http\Server\HttpServer;
use Amp\Http\Server\RequestHandler\CallableRequestHandler;
use Amp\Http\Server\Response;
use Amp\Http\Server\Server;
use Amp\Http\Server\ServerObserver;
use Amp\Http\Status;
use Amp\Loop;
use Amp\Promise;
use Amp\Success;
use Psr\Log\NullLogger;

require \dirname(__DIR__) . "/vendor/autoload.php";

class TestObserver implements ServerObserver {
    public function onStart(Server $server): Promise
    {
        echo "started\n";
        return new Success();
    }

    public function onStop(Server $server): Promise
    {
        echo "stopped\n";
        return new Success();
    }
}

$testObserver = new TestObserver();

Loop::run(function () use ($testObserver) {
    $socket = \Amp\Socket\Server::listen("tcp://127.0.0.1:0");
    $server = new HttpServer([$socket], new CallableRequestHandler(function () {
        return new Response(Status::OK);
    }), new NullLogger());

    $server->attach($testObserver);

    yield $server->start();
    yield $server->stop();
});

It worked, and this seemed strange to me. But in the end, it also didn't fail on us in CI. After some more analysis, I was able to narrow it down to composer autoloader optimizations: When I run composer install --optimize-autoloader --classmap-authoritative (as we do in our build process), I can reproduce it. Which makes sense, because the Server class is essentially defined at runtime with a class_alias.

It's possible to fix this in multiple ways, described in more detail here and here. For simplicity's sake, I suggest adding "src/Server.php" to the "files" section of the composer autoloader definition.

Yes, having it in src/Server.php is obviously wrong. I think we can just move it to src/functions.php, which is always loaded anyway. But I'd be fine with loading src/Server.php as well. Do you want to submit a PR?

PR is open, a timely tag would be very appreciated, so I can remove the hotfix in our codebase. :-)

I failed to pull before tagging, but v2.1.2 includes your fix, thanks!