Re: [VOTE] True Async RFC 1.6

From: Date: Fri, 21 Nov 2025 11:29:16 +0000
Subject: Re: [VOTE] True Async RFC 1.6
References: 1 2 3 4 5 6 7 8  Groups: php.internals 
Request: Send a blank email to internals+get-129358@lists.php.net to get a copy of this message
Hi, On Fri, Nov 21, 2025 at 8:16 AM Edmond Dantes <edmond.ht@gmail.com> wrote: > Hello. > > Imagine that we have an application like this. > > ```php > class AuthService > { > private static ?self $instance = null; > > private PDO $db; > private ?string $sessionId = null; > > // Private constructor for singleton > private function __construct(PDO $db) > { > $this->db = $db; > } > > // Get singleton instance > public static function getInstance(PDO $db): self > { > if (self::$instance === null) { > self::$instance = new self($db); > } > return self::$instance; > } > > public function login(string $email, string $password): bool > { > // Find user by email > $stmt = $this->db->prepare('SELECT * FROM users WHERE email = ?'); > $stmt->execute([$email]); > $user = $stmt->fetch(PDO::FETCH_ASSOC); > > // Invalid credentials > if (!$user || !password_verify($password, $user['password_hash'])) > { > return false; > } > > // Generate and save session ID > $this->sessionId = bin2hex(random_bytes(16)); > > $stmt = $this->db->prepare( > 'INSERT INTO sessions (user_id, session_id) VALUES (?, ?)' > ); > $stmt->execute([$user['id'], $this->sessionId]); > > return true; > } > > // Return current session ID > public function getSessionId(): ?string > { > return $this->sessionId; > } > } > ``` > > One day you decide you want more performance and make a single PHP > process handle multiple connections concurrently. You wrap each > request in a separate coroutine and try to use the old code. > > ```php > $server = new Swoole\Http\Server("127.0.0.1", 9501); > > $server->on("request", function ($req, $res) { > > // create DB connection (just for example) > $db = new PDO('mysql:host=localhost;dbname=test', 'root', > ''); > > // get singleton > $auth = AuthService::getInstance($db); > > // read request data > $data = json_decode($req->rawContent(), true); > > $email = $data['email'] ?? ''; > $password = $data['password'] ?? ''; > > // call old sync code > $ok = $auth->login($email, $password); > > if ($ok) { > $res->end("Logged in, session: " . $auth->getSessionId()); > } else { > $res->status(401); > $res->end("Invalid credentials"); > } > }); > > $server->start(); > ``` > > What is happening here? > Now, in PHP, inside a single process or thread, the same code is > literally handling multiple connections. > At the same time, there are constant switches between different > requests at the points where MySQL queries occur. > > That is, when the code executes > $stmt->execute([$email]); > control is passed to another coroutine with a different > $stmt->execute([$email]); > > What breaks in this code? > Correct, coroutines break the singleton because they alternate writing > different Session IDs! > > I think you seriously underestimate impact of this in the current PHP code bases where many applications depend on global state. Especially the legacy ones but even the most popular ones. Just look into WordPress which use global state extensively. Now imagine that some popular plugin decides to use async which change some of its globals (like $post or $wp_query) during the suspension of the main code. I would assume this could horribly break things. Don't forget that other code don't have control over the plugin and it might not even know that async is used there. So I'm not sure if this design is compatible with WordPress and similar applications where global state is used extensively. If that's the case, it's of course a problem because those applications (well WordPress on its own in fact) compose the majority of PHP users so introducing something that would have potential to break its code would limit usability of the whole feature and could even lead to loosing more users that we could gain from introducing this feature.. So I think it will need to find some solution that will prevent this from happening. I guess there might be few options 1. Disallow suspension of the main sync code which is effectively some sort of colouring. 2. Preventing access to globals from coroutine which I'm not sure is even fully doable from the engine PoV - it would mean some sort of different execution mode that could not use globals (e.g. global keyword and calling some functions that change global state). It would need channels for communications between coroutines. The advantage of such model would be possibility to combine it with threads in the future but I could imagine it could still lead to some subtle issue for years as there is internal global state as well that can lead to some surprises. But maybe that would be worth it. Just some thoughts... Cheers Jakub

« previous php.internals (#129358) next »